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

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));}
http://www.jsqmd.com/news/298852/

相关文章:

  • 内核日志分析:__spi_pump_messages的Caller_Optimization和KWorker_Thread
  • 2025年市场上优质的非标钣金定制企业哪个好,行业内专业的非标钣金定制哪个好睿意达诚信务实提供高性价比服务
  • Flutter for OpenHarmony:从零开始认识基础组件
  • 得物Java面试被问:RocketMQ的消息轨迹追踪实现
  • 期货交易平台数据分析系统开题报告
  • 基于大数据+Hadoop的智能电网环境下的电能质量监测系统开题报告
  • 我的ppo转头找门模型成功收敛 当它发现原地不动有分的时候
  • 浙江洁净车间厂家深度评测:百级洁净度的关键考量,净化工程公司/无尘室/无尘车间/净化工程/净化车间,洁净车间施工哪家靠谱
  • 2026年重庆等地值得选的安全阀在线检测仪服务商排名
  • 2026年电镀金加工推荐厂家怎么选,鼎亚电子优势明显
  • 2026年金属带材电镀生产企业排行榜,十大厂家有谁?
  • 2026年慢走丝机床品牌排名揭晓,上海汉霸数控以硬核实力上榜!
  • 成都婚纱摄影怎么选?2025-2026年度真实口碑排行榜单
  • 2026年润滑油泵服务商厂家排名揭晓,宁波迪奥机械名列前茅!
  • 2026年连续镀专业供应商,无锡鼎亚电子实力如何?
  • 2026年湖北电镀金灵活化加工,性价比高的厂家排名揭晓
  • 2026年探寻汉霸地区靠谱的火花机设备供应商
  • 青城腊香传千年: 青城山小农哥川味特产店-成都江堰打卡新地标
  • 计网 01 WebSocket | MDN - 教程
  • 2026年顺德有名的刀塔机采购排行,三轴机/4+4车铣/双主轴双刀塔/插补Y/36排刀机/Y轴/四轴机,刀塔机工厂找哪家
  • 2026年口碑好的管家婆软件服务商排行榜,用友 T3/好会计/税务云/协同云/易代账/好生意,管家婆软件系统推荐榜单
  • 山东临沂玻璃门工厂推荐:顶立固为何成为3000+B端客户的共同选择?
  • Redis 高可用进阶(一):主从复制核心逻辑全解析
  • P8428 [COI 2020] Pastiri 题解 / 树上覆盖经典贪心
  • dvwa靶场详细通关教程三(CSRF跨站请求伪造)
  • 详细介绍:MLOps 的CI/CD VS DevOps 的CI/CD
  • 2026年合肥东辰职业学校推荐,合肥东辰职业学校教研成果多吗?
  • 说说浙江办公家具生产企业价格,合理之选在这里
  • 盘点更值得选的防爆润滑油泵厂家,宁波迪奥机械口碑佳
  • 2026年推荐的贫瘦煤厂家盘点,选哪家更靠谱?