news 2026/4/13 1:44:53

Linux驱动学习笔记:spi-imx.c收发消息的核心流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux驱动学习笔记:spi-imx.c收发消息的核心流程

spi-imx.c 分析策略与核心流程

一、spi-imx.c分析顺序

1. probe函数 → 理解初始化做了什么 2. 回调函数注册 → 找到关键回调 3. 数据传输路径 → 跟踪实际传输流程 4. 硬件操作细节 → 理解寄存器操作

二、核心关键:spi-bitbang.c 的介入

重大发现

/* spi_imx_probe 中 */spi_imx->bitbang.chipselect=spi_imx_chipselect;spi_imx->bitbang.setup_transfer=spi_imx_setupxfer;spi_imx->bitbang.txrx_bufs=spi_imx_transfer;// ← 最关键!ret=spi_bitbang_start(&spi_imx->bitbang);// ← 这里注册了回调!

关键理解

  • spi-imx.c只实现了简单的transfer_one(单次传输)接口(在bitbang.c中初始化为master->transfer_one = spi_bitbang_transfer_one),而没有实现复杂的transfer_one_message(整个消息处理)接口,因此内核会自动使用核心层中的函数spi_transfer_one_message来作为“总指挥”。
  • 核心层的spi_transfer_one_message中,在发起每一次的xfer单次传输时,都会调用master->transfer_one,也就是spi_bitbang_transfer_one,而在函数spi_bitbang_transfer_one中,会调用txrx_bufs(spi, transfer),也就是spi_imx_transfer
  • spi_transfer_one_message->transfer_one->spi_bitbang_transfer_one->txrx_bufs->spi_imx_transfer.

三、调用链重建

从spi.c到spi-imx.c的完整路径

用户态: ioctl(SPI_IOC_MESSAGE, xfer) ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ VFS层: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ sys_ioctl() ↓ file->f_op->unlocked_ioctl ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/spi/spidev.c: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ spidev_ioctl() ↓ 识别命令:SPI_IOC_MESSAGE ↓ spidev_message() ├─ 拷贝TX数据:copy_from_user(tx_buf, user_tx, len) ├─ 构造spi_transfer和spi_message └─ 调用 spi_sync() ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/spi/spi.c(SPI核心层): ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ spi_sync() ↓ __spi_sync() ├─ 设置完成量 └─ 调用 __spi_async() ↓ __spi_async() ├─ 验证参数 └─ 将message加入队列 ↓ __spi_pump_messages() ← 队列处理函数 ├─ 从队列取出message ├─ 准备硬件(prepare_transfer_hardware) ├─ 准备消息(prepare_message) ├─ 映射DMA(spi_map_msg) └─ 调用 master->transfer_one_message() ← 关键跳转! ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/spi/spi.c(SPI核心层的默认实现): ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ spi_transfer_one_message() ← 核心层提供的包装函数 ↓ /* ========== 关键:在这里遍历message ========== */ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ↓ /* 调用驱动的transfer_one */ ret = master->transfer_one(master, msg->spi, xfer); ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/spi/spi-bitbang.c(Bitbang框架): ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ spi_bitbang_transfer_one() ← 只处理单个transfer! ↓ /* 配置传输参数 */ if (bitbang->setup_transfer) bitbang->setup_transfer(spi, t); → spi_imx_setupxfer() ↓ /* 执行实际传输 */ status = bitbang->txrx_bufs(spi, t); → spi_imx_transfer() ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ drivers/spi/spi-imx.c(平台驱动): ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ spi_imx_transfer() ← 处理单个transfer ↓ if (spi_imx->usedma) spi_imx_dma_transfer() else spi_imx_pio_transfer() ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ PIO模式详细流程: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ spi_imx_pio_transfer() ├─ 设置缓冲区指针:tx_buf, rx_buf, count ├─ 重置完成量:reinit_completion(&xfer_done) ├─ 填充TX FIFO:spi_imx_push() │ ↓ │ while (txfifo < FIFO_SIZE && count > 0) { │ spi_imx->tx(spi_imx) → 写寄存器 │ txfifo++ │ } │ ↓ │ devtype_data->trigger() → 启动硬件传输 │ ├─ 使能中断:intctrl(spi_imx, MXC_INT_TE) └─ 等待完成:wait_for_completion(&xfer_done) ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 中断处理: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ spi_imx_isr() ← 硬件中断触发 ↓ /* 读取RX FIFO */ while (rx_available()) { spi_imx->rx(spi_imx) → 读寄存器 txfifo-- } ↓ if (count > 0) { /* 还有数据,继续发送 */ spi_imx_push() } else if (txfifo > 0) { /* 等待最后的接收 */ intctrl(spi_imx, MXC_INT_RR) } else { /* 传输完成 */ intctrl(spi_imx, 0) → 关闭中断 complete(&xfer_done) → 唤醒等待线程 } ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 返回路径: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ complete(&xfer_done)唤醒 ↓ spi_imx_pio_transfer() 返回 ↓ spi_imx_transfer() 返回 ↓ spi_bitbang_transfer_one() 返回 ↓ (继续处理下一个transfer) } ← 结束for_each_entry循环 ↓ spi_transfer_one_message() 完成 ↓ spi_finalize_current_message() ├─ unprepare_message() → 关闭时钟 ├─ master->cur_msg = NULL └─ complete(msg->context) → 唤醒用户线程 ↓ __spi_sync() 被唤醒 ↓ spi_sync() 返回 ↓ spidev_message() 返回 ├─ 拷贝RX数据:copy_to_user(user_rx, rx_buf, len) └─ 返回传输字节数 ↓ spidev_ioctl() 返回 ↓ 用户态:ioctl() 返回

四、关键函数详解

4.1 spi_bitbang_start(注册回调)

// drivers/spi/spi-bitbang.cintspi_bitbang_start(structspi_bitbang*bitbang){structspi_master*master=bitbang->master;/* 关键:注册 transfer_one_message 回调 */if(!master->transfer_one_message)master->transfer_one_message=spi_bitbang_transfer_one;/* 其他初始化... */returnspi_register_master(master);}

4.2 spi_bitbang_transfer_one(bitbang核心)

// drivers/spi/spi-bitbang.cstaticintspi_bitbang_transfer_one(structspi_master*master,structspi_message*msg){structspi_bitbang*bitbang=spi_master_get_devdata(master);structspi_transfer*t;printk("[BITBANG_TRACE] transfer_one: msg=%px\n",msg);/* 遍历消息中的每个传输段 */list_for_each_entry(t,&msg->transfers,transfer_list){printk("[BITBANG_TRACE] Processing transfer: len=%u\n",t->len);/* 1. 片选激活 */if(bitbang->chipselect){bitbang->chipselect(msg->spi,BITBANG_CS_ACTIVE);}/* 2. 配置传输参数(速度、位宽等) */if(bitbang->setup_transfer){bitbang->setup_transfer(msg->spi,t);}/* 3. 执行实际传输 ← 最关键! */if(bitbang->txrx_bufs){status=bitbang->txrx_bufs(msg->spi,t);// ↑ 调用 spi_imx_transfer()}/* 4. 延迟(如果需要) */if(t->delay_usecs)udelay(t->delay_usecs);/* 5. 片选控制 */if(t->cs_change){bitbang->chipselect(msg->spi,BITBANG_CS_INACTIVE);}}/* 6. 传输完成,调用回调 */msg->status=0;msg->actual_length=/* 累加所有transfer的len */;spi_finalize_current_message(master);return0;}

五、spi-imx.c 分析要点

5.1 probe函数(初始化)

staticintspi_imx_probe(structplatform_device*pdev){structspi_master*master;structspi_imx_data*spi_imx;/* 1. 分配master */master=spi_alloc_master(&pdev->dev,sizeof(*spi_imx));spi_imx=spi_master_get_devdata(master);spi_imx->bitbang.master=master;/* 2. 识别芯片型号(从设备树) */spi_imx->devtype_data=of_id->data;// ↑ 指向 imx51_ecspi_devtype_data/* 3. 注册bitbang回调 ← 你找到的关键代码! */spi_imx->bitbang.chipselect=spi_imx_chipselect;spi_imx->bitbang.setup_transfer=spi_imx_setupxfer;spi_imx->bitbang.txrx_bufs=spi_imx_transfer;// ← 传输入口/* 4. 注册master回调 */spi_imx->bitbang.master->setup=spi_imx_setup;spi_imx->bitbang.master->prepare_message=spi_imx_prepare_message;spi_imx->bitbang.master->unprepare_message=spi_imx_unprepare_message;/* 5. 初始化硬件资源 */spi_imx->base=devm_ioremap_resource(&pdev->dev,res);// 寄存器映射spi_imx->clk_per=devm_clk_get(&pdev->dev,"per");// 时钟/* 6. 注册中断 */devm_request_irq(&pdev->dev,irq,spi_imx_isr,0,...);/* 7. 初始化DMA(如果支持) */if(is_imx51_ecspi(spi_imx)){spi_imx_sdma_init(&pdev->dev,spi_imx,master);}/* 8. 复位硬件 */spi_imx->devtype_data->reset(spi_imx);// ↑ 调用 mx51_ecspi_reset()/* 9. 启动bitbang框架 ← 这里注册transfer_one_message */ret=spi_bitbang_start(&spi_imx->bitbang);return0;}

5.2 devtype_data(芯片差异抽象)

staticstructspi_imx_devtype_dataimx51_ecspi_devtype_data={.intctrl=mx51_ecspi_intctrl,// 中断控制.config=mx51_ecspi_config,// 硬件配置.trigger=mx51_ecspi_trigger,// 启动传输.rx_available=mx51_ecspi_rx_available,// 检查RX FIFO.reset=mx51_ecspi_reset,// 复位控制器.devtype=IMX51_ECSPI,};

设计模式:函数指针表,实现多型号支持


六、核心传输流程

6.1 spi_imx_transfer(传输入口)

staticintspi_imx_transfer(structspi_device*spi,structspi_transfer*transfer){structspi_imx_data*spi_imx=spi_master_get_devdata(spi->master);printk("[SPI_IMX_TRACE] transfer: len=%u, usedma=%d\n",transfer->len,spi_imx->usedma);if(spi_imx->usedma)returnspi_imx_dma_transfer(spi_imx,transfer);elsereturnspi_imx_pio_transfer(spi,transfer);}

6.2 PIO模式传输(中断方式)

staticintspi_imx_pio_transfer(structspi_device*spi,structspi_transfer*transfer){structspi_imx_data*spi_imx=spi_master_get_devdata(spi->master);printk("[SPI_IMX_TRACE] pio_transfer: ENTER\n");/* 1. 设置缓冲区指针 */spi_imx->tx_buf=transfer->tx_buf;spi_imx->rx_buf=transfer->rx_buf;spi_imx->count=transfer->len;spi_imx->txfifo=0;/* 2. 重新初始化完成量 */reinit_completion(&spi_imx->xfer_done);/* 3. 填充TX FIFO并启动传输 */spi_imx_push(spi_imx);// ↓// while (txfifo < FIFO_SIZE && count > 0) {// spi_imx->tx(spi_imx); → 写TX寄存器// txfifo++;// }// spi_imx->devtype_data->trigger(spi_imx); → 启动硬件/* 4. 使能TX FIFO空中断 */spi_imx->devtype_data->intctrl(spi_imx,MXC_INT_TE);/* 5. 等待传输完成 */timeout=wait_for_completion_timeout(&spi_imx->xfer_done,...);printk("[SPI_IMX_TRACE] pio_transfer: EXIT, status=%d\n",timeout?0:-ETIMEDOUT);returntimeout?transfer->len:-ETIMEDOUT;}

6.3 中断处理(核心!)

staticirqreturn_tspi_imx_isr(intirq,void*dev_id){structspi_imx_data*spi_imx=dev_id;printk("[SPI_IMX_TRACE] ISR: ENTER\n");/* 1. 读取所有RX FIFO中的数据 */while(spi_imx->devtype_data->rx_available(spi_imx)){spi_imx->rx(spi_imx);// → 读RX寄存器// ↓// val = readl(spi_imx->base + MXC_CSPIRXDATA);// if (spi_imx->rx_buf) {// *(u8 *)spi_imx->rx_buf = val;// spi_imx->rx_buf++;// }spi_imx->txfifo--;// TX FIFO空出一个位置printk("[SPI_IMX_TRACE] ISR: Received byte, txfifo=%u\n",spi_imx->txfifo);}/* 2. 如果还有数据要发送 */if(spi_imx->count){spi_imx_push(spi_imx);// 继续填充TX FIFOreturnIRQ_HANDLED;}/* 3. 如果TX FIFO还有数据未发送完 */if(spi_imx->txfifo){/* 使能RX中断,等待最后的接收 */spi_imx->devtype_data->intctrl(spi_imx,MXC_INT_RR);returnIRQ_HANDLED;}/* 4. 传输完成 */spi_imx->devtype_data->intctrl(spi_imx,0);// 关闭中断complete(&spi_imx->xfer_done);// 唤醒等待线程printk("[SPI_IMX_TRACE] ISR: Transfer complete!\n");returnIRQ_HANDLED;}

七、推荐的分析步骤

步骤1:添加probe调试

staticintspi_imx_probe(structplatform_device*pdev){printk("[SPI_IMX_TRACE] ========== PROBE START ==========\n");// ...原有代码...spi_imx->devtype_data=of_id->data;printk("[SPI_IMX_TRACE] probe: devtype=%d, intctrl=%pS, config=%pS\n",spi_imx->devtype_data->devtype,spi_imx->devtype_data->intctrl,spi_imx->devtype_data->config);// ...spi_imx->bitbang.txrx_bufs=spi_imx_transfer;printk("[SPI_IMX_TRACE] probe: Registered txrx_bufs=%pS\n",spi_imx->bitbang.txrx_bufs);ret=spi_bitbang_start(&spi_imx->bitbang);printk("[SPI_IMX_TRACE] probe: spi_bitbang_start returned %d\n",ret);printk("[SPI_IMX_TRACE] probe: master->transfer_one_message=%pS\n",master->transfer_one_message);printk("[SPI_IMX_TRACE] ========== PROBE END ==========\n");}

步骤2:添加传输路径跟踪

staticintspi_imx_transfer(structspi_device*spi,structspi_transfer*transfer){printk("[SPI_IMX_TRACE] ==> spi_imx_transfer: len=%u\n",transfer->len);/* 原有代码 */printk("[SPI_IMX_TRACE] <== spi_imx_transfer: ret=%d\n",ret);returnret;}

步骤3:添加硬件操作跟踪

staticvoidmx51_ecspi_trigger(structspi_imx_data*spi_imx){u32 reg=readl(spi_imx->base+MX51_ECSPI_CTRL);printk("[SPI_IMX_TRACE] trigger: BEFORE ctrl=0x%08x\n",reg);if(!spi_imx->usedma)reg|=MX51_ECSPI_CTRL_XCH;writel(reg,spi_imx->base+MX51_ECSPI_CTRL);printk("[SPI_IMX_TRACE] trigger: AFTER ctrl=0x%08x (XCH=%d)\n",reg,!!(reg&MX51_ECSPI_CTRL_XCH));}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/5 10:29:18

从0构建 3D GIF动画,看清计算机运行机制

从0构建 3D GIF动画&#xff0c;看清计算机运行机制 在《从 0 构建 WAV 文件》中&#xff0c;我们通过了解wav文件的结构与格式&#xff0c;学会了如何用朴素的方式构建声音文件&#xff1b;在《从 2D 转 3D 的本质》中&#xff0c;我们领悟了游戏中所谓三维世界&#xff0c;不…

作者头像 李华
网站建设 2026/4/7 14:56:38

C#多线程工业源码:超强大的工控解决方案

C#多线程工业源码&#xff0c;可技术咨询 0, 纯源代码。 1, 替代传统plc搭载的触摸屏。 2, 工控屏幕一体机直接和plc通信。 3, 功能强大&#xff0c;多级页签。 4, 可以自由设定串口或以太网通信。 5, 主页。 6, 报警页。 7&#xff0c;触摸键盘模拟输入。 8, 系统设定页。 9, …

作者头像 李华
网站建设 2026/4/12 2:35:28

计算机Java毕设实战-基于springboot的社区诊所在线挂号与排队就诊系统【完整源码+LW+部署说明+演示视频,全bao一条龙等】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/11 17:28:58

华为云国际版-东南亚篇

华为云国际版在东南亚凭借完善的本地数据中心、强政企与运营商生态、低延迟网络与合规适配&#xff0c;成为中企出海与本地数字化转型的主流选择&#xff0c;泰国、新加坡、印尼等核心市场表现突出&#xff0c;以下从布局、性能、客户与生态、成本与合规、挑战与选型建议展开详…

作者头像 李华
网站建设 2026/4/12 14:42:32

【开题答辩全过程】以 景区游乐管理系统的设计与实现为例,包含答辩的问题和答案

个人简介 一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等 开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。 感谢大家…

作者头像 李华
网站建设 2026/4/10 10:07:31

吐血推荐本科生必用TOP8 AI论文网站

吐血推荐本科生必用TOP8 AI论文网站 2026年本科生AI论文工具测评&#xff1a;为什么你需要这份榜单 在当前学术写作日益依赖AI技术的背景下&#xff0c;本科生群体面临着从选题构思到格式排版的一系列挑战。面对海量文献、复杂的引用规范以及严格的查重要求&#xff0c;一款高效…

作者头像 李华