当前位置: 首页 > news >正文

spi 驱动-数据发送流程分析

总结

        核心函数是spi_sync, 设备驱动->核心函数-> 控制器驱动

 实例分析

(gdb) c
Continuing.Thread 115 hit Breakpoint 1, bcm2835_spi_transfer_one (master=0xffffffc07b8e6000, spi=0xffffffc07b911800, tfr=0xffffff8009f53c40) at drivers/spi/spi-bcm2835.c:534
534	{
(gdb) bt
#0  bcm2835_spi_transfer_one (master=0xffffffc07b8e6000, spi=0xffffffc07b911800, tfr=0xffffff8009f53c40) at drivers/spi/spi-bcm2835.c:534
#1  0xffffff80086cbe8c in spi_transfer_one_message (ctlr=0xffffffc07b8e6000, msg=0xffffff8009f53ca0) at drivers/spi/spi.c:1031
#2  0xffffff80086cc990 in __spi_pump_messages (ctlr=0xffffffc07b8e6000, in_kthread=<optimized out>) at drivers/spi/spi.c:1265
#3  0xffffff80086cccb8 in __spi_sync (spi=0xffffffc07b911800, message=0xffffff8009f53ca0) at drivers/spi/spi.c:3129
#4  0xffffff80086ccd04 in spi_sync (spi=0xffffffc07b911800, message=0xffffff8009f53ca0) at drivers/spi/spi.c:3165
#5  0xffffff80086ce14c in spidev_sync (spidev=<optimized out>, message=0xffffff8009f53ca0) at drivers/spi/spidev.c:112
#6  0xffffff80086ce2f0 in spidev_sync_write (len=<optimized out>, spidev=<optimized out>) at drivers/spi/spidev.c:132
#7  spidev_write (filp=<optimized out>, buf=<optimized out>, count=4, f_pos=<optimized out>) at drivers/spi/spidev.c:199
#8  0xffffff80082c919c in __vfs_write (file=0xffffffc072ea2500, p=0x5587f7cb60 "555\n/dev/spidev0.0\n", count=4, pos=0xffffff8009f53db0) at fs/read_write.c:485
#9  0xffffff80082c93e0 in vfs_write (file=0xffffffc072ea2500, buf=0x5587f7cb60 "555\n/dev/spidev0.0\n", count=4, pos=0xffffff8009f53db0) at fs/read_write.c:549
#10 0xffffff80082c9768 in ksys_write (fd=<optimized out>, buf=0x5587f7cb60 "555\n/dev/spidev0.0\n", count=4) at fs/read_write.c:599
#11 0xffffff80082c9810 in __do_sys_write (count=<optimized out>, buf=<optimized out>, fd=<optimized out>) at fs/read_write.c:611
#12 __se_sys_write (fd=1, buf=367353383776, count=4) at fs/read_write.c:608
#13 0xffffff80082c984c in __arm64_sys_write (regs=0xffffff8009f53ec0) at fs/read_write.c:608
#14 0xffffff8008099254 in __invoke_syscall (regs=0xffffff8009f53ec0, syscall_fn=0xffffff80082c9820 <__arm64_sys_write>) at arch/arm64/kernel/syscall.c:36
#15 0xffffff80080993a4 in invoke_syscall (regs=0xffffff8009f53ec0, scno=<optimized out>, sc_nr=<optimized out>, syscall_table=0xffffff80089c0778 <sys_call_table>) at arch/arm64/kernel/syscall.c:48
#16 0xffffff800809943c in el0_svc_common (regs=0xffffff8009f53ec0, scno=64, sc_nr=294, syscall_table=0xffffff80089c0778 <sys_call_table>) at arch/arm64/kernel/syscall.c:114
#17 0xffffff8008099524 in el0_svc_handler (regs=0xffffff8009f53ec0) at arch/arm64/kernel/syscall.c:160
#18 0xffffff8008084088 in el0_svc () at arch/arm64/kernel/entry.S:917

源码分析

1. 注册字符设备并提供操作函数 spidev_fops

static int __init spidev_init(void)status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
}static const struct file_operations spidev_fops = {.....write =    spidev_write,....
};


2. 分析spidev_write 调用spi_sync

static ssize_t
spidev_write(struct file *filp, const char __user *buf,size_t count, loff_t *f_pos)
{struct spidev_data    *spidev;ssize_t            status = 0;unsigned long        missing;spidev = filp->private_data;missing = copy_from_user(spidev->tx_buffer, buf, count);if (missing == 0)status = spidev_sync_write(spidev, count);....}
static inline ssize_t
spidev_sync_write(struct spidev_data *spidev, size_t len)
{struct spi_transfer    t = {.tx_buf        = spidev->tx_buffer,.len        = len,.speed_hz    = spidev->speed_hz,};struct spi_message    m;spi_message_init(&m);spi_message_add_tail(&t, &m);return spidev_sync(spidev, &m);
}static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{int status;struct spi_device *spi;spi = spidev->spi;status = spi_sync(spi, message);return status;
}


3. 分析 spi_sync 调用 ctlr->transfer_one_message

int spi_sync(struct spi_device *spi, struct spi_message *message)
{ret = __spi_sync(spi, message);
}static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{DECLARE_COMPLETION_ONSTACK(done);int status;struct spi_controller *ctlr = spi->controller;unsigned long flags;status = __spi_validate(spi, message);if (status != 0)return status;message->complete = spi_complete;message->context = &done;message->spi = spi;SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_sync);SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);if (ctlr->transfer == spi_queued_transfer) {   //新方法status = __spi_queued_transfer(spi, message, false);} else {                    //legacy method //老方法status = spi_async_locked(spi, message);}if (status == 0) {/* Push out the messages in the calling context if we* can.*/if (ctlr->transfer == spi_queued_transfer) {SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_sync_immediate);SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync_immediate);__spi_pump_messages(ctlr, false); //处理spi 消息}wait_for_completion(&done);status = message->status;}message->context = NULL;return status;
}
static int __spi_queued_transfer(struct spi_device *spi,struct spi_message *msg,bool need_pump)
{struct spi_controller *ctlr = spi->controller;unsigned long flags;...list_add_tail(&msg->queue, &ctlr->queue); 将消息队列添加到队列的尾部if (!ctlr->busy && need_pump)kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);....return 0;
}
static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
{unsigned long flags;bool was_busy = false;int ret;/* Lock queue */spin_lock_irqsave(&ctlr->queue_lock, flags);/* Make sure we are not already running a message */if (ctlr->cur_msg) {spin_unlock_irqrestore(&ctlr->queue_lock, flags);return;}/* If another context is idling the device then defer */if (ctlr->idling) {kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);spin_unlock_irqrestore(&ctlr->queue_lock, flags);return;}/* Check if the queue is idle */if (list_empty(&ctlr->queue) || !ctlr->running) {if (!ctlr->busy) {spin_unlock_irqrestore(&ctlr->queue_lock, flags);return;}/* Only do teardown in the thread */if (!in_kthread) {kthread_queue_work(&ctlr->kworker,&ctlr->pump_messages);spin_unlock_irqrestore(&ctlr->queue_lock, flags);return;}ctlr->busy = false;ctlr->idling = true;spin_unlock_irqrestore(&ctlr->queue_lock, flags);kfree(ctlr->dummy_rx);ctlr->dummy_rx = NULL;kfree(ctlr->dummy_tx);ctlr->dummy_tx = NULL;if (ctlr->unprepare_transfer_hardware &&ctlr->unprepare_transfer_hardware(ctlr))dev_err(&ctlr->dev,"failed to unprepare transfer hardware\n");if (ctlr->auto_runtime_pm) {pm_runtime_mark_last_busy(ctlr->dev.parent);pm_runtime_put_autosuspend(ctlr->dev.parent);}trace_spi_controller_idle(ctlr);spin_lock_irqsave(&ctlr->queue_lock, flags);ctlr->idling = false;spin_unlock_irqrestore(&ctlr->queue_lock, flags);return;}/* Extract head of queue */ctlr->cur_msg =list_first_entry(&ctlr->queue, struct spi_message, queue);list_del_init(&ctlr->cur_msg->queue);if (ctlr->busy)was_busy = true;elsectlr->busy = true;spin_unlock_irqrestore(&ctlr->queue_lock, flags);mutex_lock(&ctlr->io_mutex);if (!was_busy && ctlr->auto_runtime_pm) {ret = pm_runtime_get_sync(ctlr->dev.parent);if (ret < 0) {pm_runtime_put_noidle(ctlr->dev.parent);dev_err(&ctlr->dev, "Failed to power device: %d\n",ret);mutex_unlock(&ctlr->io_mutex);return;}}if (!was_busy)trace_spi_controller_busy(ctlr);if (!was_busy && ctlr->prepare_transfer_hardware) {ret = ctlr->prepare_transfer_hardware(ctlr);if (ret) {dev_err(&ctlr->dev,"failed to prepare transfer hardware\n");if (ctlr->auto_runtime_pm)pm_runtime_put(ctlr->dev.parent);mutex_unlock(&ctlr->io_mutex);return;}}trace_spi_message_start(ctlr->cur_msg);if (ctlr->prepare_message) {ret = ctlr->prepare_message(ctlr, ctlr->cur_msg);if (ret) {dev_err(&ctlr->dev, "failed to prepare message: %d\n",ret);ctlr->cur_msg->status = ret;spi_finalize_current_message(ctlr);goto out;}ctlr->cur_msg_prepared = true;}ret = spi_map_msg(ctlr, ctlr->cur_msg);if (ret) {ctlr->cur_msg->status = ret;spi_finalize_current_message(ctlr);goto out;}ret = ctlr->transfer_one_message(ctlr, ctlr->cur_msg); 调用控制器的传输函数if (ret) {dev_err(&ctlr->dev,"failed to transfer one message from queue\n");goto out;}out:mutex_unlock(&ctlr->io_mutex);/* Prod the scheduler in case transfer_one() was busy waiting */if (!ret)cond_resched();
}

4.  spi controler "传输函数" 之间的相互调用


        # ctlr->transfer =           spi_queued_transfer;  // 调用  ctlr->transfer_one_message
        ctlr->transfer_one_message = spi_transfer_one_message; //ctlr->transfer_one

        ctrl->transfer_one = bcm2835_spi_transfer_one;

相关文章:

spi 驱动-数据发送流程分析

总结 核心函数是spi_sync&#xff0c; 设备驱动->核心函数-> 控制器驱动 实例分析 (gdb) c Continuing.Thread 115 hit Breakpoint 1, bcm2835_spi_transfer_one (master0xffffffc07b8e6000, spi0xffffffc07b911800, tfr0xffffff8009f53c40) at drivers/spi/spi-bcm2835…...

平面分割--------PCL

平面分割 bool PclTool::planeSegmentation(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, pcl::ModelCoefficients::Ptr coefficients, pcl::PointIndices::Ptr inliers) {std::cout << "Point cloud data: " << cloud->points.size() <<…...

前端之深拷贝

前提&#xff1a; 就是在实际开发中&#xff0c;我有一个编辑的弹窗&#xff0c;可以查看和编辑&#xff0c;因为弹窗里面是一个步骤条&#xff0c;点击下一步就要向对应的接口发送请求&#xff0c;考虑到就比如我点击下一步&#xff0c;此次表箱信息其实不需要修改&#xff0…...

2024年 Java 面试八股文——SpringCloud篇

目录 1.Spring Cloud Alibaba 中的 Nacos 是如何进行服务注册和发现的&#xff1f; 2.Spring Cloud Alibaba Sentinel 的流量控制规则有哪些&#xff1f; 3.Spring Cloud Alibaba 中如何实现分布式配置管理&#xff1f; 4.Spring Cloud Alibaba RocketMQ 的主要特点有哪些&…...

linux C语言Makefile

ChatGPT 在Linux中使用Makefile来自动化C语言项目的构建过程是很普遍的实践。Makefile是一个包含了一系列构建目标及如何构建这些目标的依赖和规则的文本文件。 一个基本的Makefile例子可能会像这样&#xff1a; # 定义编译器 CCgcc# 定义编译选项 CFLAGS-I.# 定义可执行文件…...

pgvector扩展在IvorySQL Oracle兼容模式下的应用实践

向量数据库是生成式人工智能(GenAI)的关键组成部分。作为PostgreSQL的重要扩展&#xff0c;pgvector支持高达16000维的向量计算能力&#xff0c;使得PostgreSQL能够直接转化为高效的向量数据库。 IvorySQL基于PostgreSQL开发&#xff0c;因此它同样支持添加pgvector扩展。在Ora…...

c++ 线程概述

C中的线程是并发编程的重要组成部分&#xff0c;它允许程序同时执行多个任务。以下是对C线程的概述&#xff1a; 基本概念&#xff1a; 并发&#xff1a;意味着两个或多个任务同时执行。在单核CPU上&#xff0c;由于只有一个CPU&#xff0c;某一时刻只能执行一个任务&#xff0…...

纯血鸿蒙APP实战开发——短视频切换实现案例

短视频切换实现案例 介绍 短视频切换在应用开发中是一种常见场景&#xff0c;上下滑动可以切换视频&#xff0c;十分方便。本模块基于Swiper组件和Video组件实现短视频切换功能。 效果图预览 使用说明 上下滑动可以切换视频。点击屏幕暂停视频&#xff0c;再次点击继续播放…...

36.Docker-Dockerfile自定义镜像

镜像结构 镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。 镜像是分层机构&#xff0c;每一层都是一个layer BaseImage层&#xff1a;包含基本的系统函数库、环境变量、文件系统 EntryPoint:入口&#xff0c;是镜像中应用启动的命令 其他&#xff1a;在…...

【webrtc】MessageHandler 4: 基于线程的消息处理:以Fake 收发包模拟为例

G:\CDN\rtcCli\m98\src\media\base\fake_network_interface.h// Fake NetworkInterface that sends/receives RTP/RTCP packets.虚假的网络接口,用于模拟发送包、接收包单纯仅是处理一个ST_RTP包 消息的id就是ST_RTP 类型,– 然后给到目的地:mediachannel处理: 最后消息消…...

C#运算符“/”使用方法

C#中&#xff0c;当需要对两个整数进行除法运算时&#xff0c;结果会被截断为整数部分&#xff0c;即使结果本应是一个小数。这是因为整数除法会丢弃小数部分&#xff0c;只保留整数部分。 要想保留小数部分&#xff0c;需要将至少其中一个操作数转换为float、double或者 deci…...

虚拟机网络桥接模式无法通信,获取到的ip为169.254.X.X

原因&#xff1a;VMware自动选择的网卡可能不对 解决&#xff1a;编辑-虚拟网络编辑器-更改桥接模式-选择宿主机物理网卡&#xff0c;断开虚拟机网络连接后重新连接即可...

【数据结构】初识数据结构

引入&#xff1a; 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累。我…...

相机知识的补充

一&#xff1a;镜头 1.1MP的概念 相机中MP的意思是指百万像素。MP是mega pixel的缩写。mega意为一百万&#xff0c;mega pixel 指意为100万像素。“像素”是相机感光器件上的感光最小单位。就像是光学相机的感光胶片的银粒一样&#xff0c;记忆在数码相机的“胶片”&#xff…...

在Linux操作系统中实现磁盘开机自动挂载

当一个分区创建好&#xff0c;然后文件系统创建完毕之后&#xff0c; 需要使用mount命令将分区挂载到空目录上&#xff0c;这个挂载关系是临时的&#xff0c;也就是说当重启机器的时候&#xff0c;硬盘分区于空目录之间的挂载关系就会解除。 磁盘于目录之间的挂载关系断开意味…...

单片机编程实例400例大全(100-200)

今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际产品的参考价值。 今天继续分享单片机编程实例第100-200例。 今天的实例会比前面100复杂一些&#xff0c;我大概看了下&#xff0c;很多都具备实际…...

新兴游戏引擎Godot vs. 主流游戏引擎Unity和虚幻引擎,以及版本控制工具Perforce Helix Core如何与其高效集成

游戏行业出现一个新生事物——Godot&#xff0c;一个免费且开源的2D和3D游戏引擎。曾经由Unity和虚幻引擎&#xff08;Unreal Engine&#xff09;等巨头主导的领域如今迎来了竞争对手。随着最近“独特”定价模式的变化&#xff0c;越来越多的独立开发者和小型开发团队倾向于选择…...

Leetcode—1652. 拆炸弹【简单】

2024每日刷题&#xff08;127&#xff09; Leetcode—1652. 拆炸弹 实现代码 class Solution { public:vector<int> decrypt(vector<int>& code, int k) {int codeSize code.size();vector<int> ans(codeSize, 0);if(k 0) {return ans;}if(k > 0)…...

JAVASE---抽象类相关

instanceof 和类型转换 System.out.println(X instanceof Y );主要看X与Y之间是否存在父子&#xff08;继承&#xff09;关系&#xff0c;如果存在则编译可完成&#xff0c;否则无法 进行编译。 1.父类引用指向子类的对象 2.把子类转换为父类&#xff0c;向上转型; 3.把父类转…...

深入理解C++中的inline函数

在C编程中&#xff0c;我们经常会遇到inline关键字&#xff0c;它用于修饰函数&#xff0c;以建议编译器将该函数的调用替换为函数体的直接拷贝。这就是inline函数的基本概念。然而&#xff0c;inline函数并非真正意义上的函数&#xff0c;而只是一种"在调用点插入函数体&…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

C++实现分布式网络通信框架RPC(2)——rpc发布端

有了上篇文章的项目的基本知识的了解&#xff0c;现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...

恶补电源:1.电桥

一、元器件的选择 搜索并选择电桥&#xff0c;再multisim中选择FWB&#xff0c;就有各种型号的电桥: 电桥是用来干嘛的呢&#xff1f; 它是一个由四个二极管搭成的“桥梁”形状的电路&#xff0c;用来把交流电&#xff08;AC&#xff09;变成直流电&#xff08;DC&#xff09;。…...

前端调试HTTP状态码

1xx&#xff08;信息类状态码&#xff09; 这类状态码表示临时响应&#xff0c;需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分&#xff0c;客户端应继续发送剩余部分。 2xx&#xff08;成功类状态码&#xff09; 表示请求已成功被服务器接收、理解并处…...