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

Linux内核中SPI 子系统的整体架构

SPI 子系统的整体架构

用户空间应用程序 ↓ spidev.c (字符设备驱动,可以用内核写好的通用字符设备驱动,也可以自己写) ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPI 核心层 (spi.c) - 注册/注销 SPI Master - 注册/注销 SPI 设备 - 管理传输队列 - 提供通用 API ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ↓ SPI Master 驱动 (spi-imx.c, spi-s3c64xx.c 等) - 硬件初始化 - 传输实现 - 中断处理 ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 硬件控制器 (SPI0, SPI1...)

核心数据结构

1. SPI Master 控制器结构

// include/linux/spi/spi.hstructspi_master{// SPI Master 控制器结构体structdevicedev;// 设备模型structlist_headlist;// 链表节点/* 控制器属性 */s16 bus_num;// 总线编号(SPI0, SPI1...)u16 num_chipselect;// 片选信号数量u16 dma_alignment;// DMA 对齐要求u16 mode_bits;// 支持的模式位u32 bits_per_word_mask;// 支持的每字长度u32 min_speed_hz;// 最小时钟频率u32 max_speed_hz;// 最大时钟频率u16 flags;// 标志位/* 传输相关 */spinlock_tbus_lock_spinlock;structmutexbus_lock_mutex;bool bus_lock_flag;/* 队列化传输支持 */bool queued;structkthread_workerkworker;structtask_struct*kworker_task;structkthread_workpump_messages;spinlock_tqueue_lock;structlist_headqueue;// 消息队列structspi_message*cur_msg;// 当前正在处理的消息bool idling;bool busy;bool running;bool rt;bool auto_runtime_pm;bool cur_msg_prepared;bool cur_msg_mapped;structcompletionxfer_completion;size_tmax_dma_len;/* 方法函数 - 由具体驱动实现 */int(*setup)(structspi_device*spi);int(*transfer)(structspi_device*spi,structspi_message*mesg);void(*cleanup)(structspi_device*spi);/* 队列化传输的方法 */int(*prepare_transfer_hardware)(structspi_master*master);int(*transfer_one_message)(structspi_master*master,structspi_message*mesg);int(*unprepare_transfer_hardware)(structspi_master*master);int(*prepare_message)(structspi_master*master,structspi_message*message);int(*unprepare_message)(structspi_master*master,structspi_message*message);/* 单个传输的方法 */int(*transfer_one)(structspi_master*master,structspi_device*spi,structspi_transfer*transfer);/* GPIO 片选 */int*cs_gpios;/* DMA 相关 */structdma_chan*dma_tx;structdma_chan*dma_rx;void*dummy_rx;void*dummy_tx;};

2. SPI 设备结构

// include/linux/spi/spi.hstructspi_device{structdevicedev;// 设备模型structspi_master*master;// 所属 masteru32 max_speed_hz;// 最大时钟频率u8 chip_select;// 片选号u8 bits_per_word;// 每字位数u16 mode;// SPI 模式intirq;// 中断号void*controller_state;void*controller_data;charmodalias[SPI_NAME_SIZE];intcs_gpio;// 片选 GPIO};

3. SPI 消息和传输结构

// SPI 传输结构(一次传输)structspi_transfer{constvoid*tx_buf;// 发送缓冲区void*rx_buf;// 接收缓冲区unsignedlen;// 传输长度dma_addr_ttx_dma;// DMA 地址dma_addr_trx_dma;structsg_tabletx_sg;// scatter-gather 表structsg_tablerx_sg;unsignedcs_change:1;// 传输后改变片选unsignedtx_nbits:3;// 发送位宽unsignedrx_nbits:3;// 接收位宽u8 bits_per_word;// 每字位数u16 delay_usecs;// 传输后延迟u32 speed_hz;// 时钟频率structlist_headtransfer_list;// 链表节点};// SPI 消息结构(多次传输)structspi_message{structlist_headtransfers;// 传输链表structspi_device*spi;// 目标设备unsignedis_dma_mapped:1;/* 完成回调 */void(*complete)(void*context);void*context;unsignedframe_length;unsignedactual_length;intstatus;structlist_headqueue;// 消息队列节点void*state;};

SPI Master 驱动框架流程

1. Master 驱动注册流程

// drivers/spi/spi-xxx.c (具体的 Master 驱动)staticintspi_xxx_probe(structplatform_device*pdev){structspi_master*master;structspi_xxx_data*data;intret;/* 1. 分配 spi_master 结构 */master=spi_alloc_master(&pdev->dev,sizeof(*data));if(!master)return-ENOMEM;/* 2. 获取私有数据 */data=spi_master_get_devdata(master);/* 3. 设置 master 属性 */master->mode_bits=SPI_CPOL|SPI_CPHA|SPI_CS_HIGH;master->bits_per_word_mask=SPI_BPW_RANGE_MASK(1,32);master->bus_num=pdev->id;// 总线号master->num_chipselect=4;// 支持 4 个片选master->dev.of_node=pdev->dev.of_node;/* 4. 设置传输方法(二选一)*/// 方式1:老式接口master->transfer=spi_xxx_transfer;// 方式2:队列化接口(推荐)master->transfer_one_message=spi_xxx_transfer_one_message;// 或者master->transfer_one=spi_xxx_transfer_one;/* 5. 设置其他方法 */master->setup=spi_xxx_setup;master->cleanup=spi_xxx_cleanup;master->prepare_transfer_hardware=spi_xxx_prepare_hw;master->unprepare_transfer_hardware=spi_xxx_unprepare_hw;/* 6. 硬件初始化 */data->regs=devm_ioremap_resource(&pdev->dev,res);data->clk=devm_clk_get(&pdev->dev,NULL);clk_prepare_enable(data->clk);/* 7. 注册中断 */ret=devm_request_irq(&pdev->dev,irq,spi_xxx_irq,0,dev_name(&pdev->dev),data);/* 8. 注册 spi_master */ret=devm_spi_register_master(&pdev->dev,master);if(ret){dev_err(&pdev->dev,"register master failed\n");gotoerr;}platform_set_drvdata(pdev,master);return0;err:spi_master_put(master);returnret;}

2. SPI 核心层的关键函数

// drivers/spi/spi.c/** * spi_register_master - 注册 SPI master 控制器 */intspi_register_master(structspi_master*master){staticatomic_tdyn_bus_id=ATOMIC_INIT((1<<15)-1);structdevice*dev=master->dev.parent;intstatus;/* 1. 分配总线号 */if(master->bus_num<0){master->bus_num=atomic_dec_return(&dyn_bus_id);}/* 2. 设置设备类型 */master->dev.class=&spi_master_class;master->dev.bus=&spi_bus_type;/* 3. 初始化队列 */INIT_LIST_HEAD(&master->queue);spin_lock_init(&master->queue_lock);spin_lock_init(&master->bus_lock_spinlock);mutex_init(&master->bus_lock_mutex);/* 4. 如果支持队列化传输 */if(master->transfer){// 使用老式传输方法}else{// 使用队列化传输master->queued=true;spi_master_initialize_queue(master);}/* 5. 注册设备 */dev_set_name(&master->dev,"spi%u",master->bus_num);status=device_add(&master->dev);if(status<0)gotodone;/* 6. 从设备树扫描子设备 */of_register_spi_devices(master);/* 7. 从 ACPI 扫描子设备 */acpi_register_spi_devices(master);done:returnstatus;}/** * spi_sync - 同步传输 */intspi_sync(structspi_device*spi,structspi_message*message){DECLARE_COMPLETION_ONSTACK(done);intstatus;message->complete=spi_complete;message->context=&done;message->spi=spi;/* 提交消息到队列 */status=spi_async_locked(spi,message);if(status==0){/* 等待完成 */wait_for_completion(&done);status=message->status;}message->context=NULL;returnstatus;}/** * spi_async - 异步传输 */intspi_async(structspi_device*spi,structspi_message*message){structspi_master*master=spi->master;intret;unsignedlongflags;spin_lock_irqsave(&master->bus_lock_spinlock,flags);if(master->bus_lock_flag)ret=-EBUSY;elseret=__spi_async(spi,message);spin_lock_irqrestore(&master->bus_lock_spinlock,flags);returnret;}staticint__spi_async(structspi_device*spi,structspi_message*message){structspi_master*master=spi->master;message->spi=spi;/* 验证消息 */if(list_empty(&message->transfers))return-EINVAL;/* 提交到队列 */return__spi_queued_transfer(spi,message);}

3. 队列化传输机制

// drivers/spi/spi.c/** * spi_queued_transfer - 队列化传输 */staticint__spi_queued_transfer(structspi_device*spi,structspi_message*msg){structspi_master*master=spi->master;unsignedlongflags;spin_lock_irqsave(&master->queue_lock,flags);/* 1. 如果 master 未运行,返回错误 */if(!master->running){spin_unlock_irqrestore(&master->queue_lock,flags);return-ESHUTDOWN;}/* 2. 设置消息状态 */msg->actual_length=0;msg->status=-EINPROGRESS;/* 3. 加入队列 */list_add_tail(&msg->queue,&master->queue);/* 4. 如果空闲,启动传输 */if(!master->busy&&need_pump)kthread_queue_work(&master->kworker,&master->pump_messages);spin_unlock_irqrestore(&master->queue_lock,flags);return0;}/** * spi_pump_messages - 消息泵(内核线程) */staticvoidspi_pump_messages(structkthread_work*work){structspi_master*master=container_of(work,structspi_master,pump_messages);unsignedlongflags;bool was_busy=false;intret;/* 1. 获取下一条消息 */spin_lock_irqsave(&master->queue_lock,flags);if(list_empty(&master->queue)||!master->running){master->busy=false;master->idling=true;spin_unlock_irqrestore(&master->queue_lock,flags);return;}/* 2. 取出第一条消息 */master->cur_msg=list_first_entry(&master->queue,structspi_message,queue);list_del_init(&master->cur_msg->queue);master->busy=true;spin_unlock_irqrestore(&master->queue_lock,flags);/* 3. 准备硬件 */if(!was_busy&&master->prepare_transfer_hardware){ret=master->prepare_transfer_hardware(master);if(ret){dev_err(&master->dev,"failed to prepare hardware\n");gotoout;}}/* 4. 准备消息 */if(master->prepare_message){ret=master->prepare_message(master,master->cur_msg);if(ret){dev_err(&master->dev,"failed to prepare message\n");gotoout;}master->cur_msg_prepared=true;}/* 5. 执行传输 */ret=master->transfer_one_message(master,master->cur_msg);if(ret){dev_err(&master->dev,"failed to transfer message\n");gotoout;}return;out:/* 错误处理 */master->cur_msg->status=ret;spi_finalize_current_message(master);}

4. 具体 Master 驱动实现示例

// drivers/spi/spi-imx.c (i.MX SPI Master 驱动)structspi_imx_data{structspi_bitbangbitbang;structcompletionxfer_done;void__iomem*base;structclk*clk_per;structclk*clk_ipg;unsignedlongspi_clk;unsignedintspeed_hz;unsignedintbits_per_word;unsignedintspi_drctl;unsignedintcount;void(*tx)(structspi_imx_data*);void(*rx)(structspi_imx_data*);void*rx_buf;constvoid*tx_buf;unsignedinttxfifo;/* number of words pushed in tx FIFO *//* DMA */bool usedma;u32 wml;structcompletiondma_rx_completion;structcompletiondma_tx_completion;};/** * spi_imx_transfer_one - 传输一个 transfer */staticintspi_imx_transfer_one(structspi_master*master,structspi_device*spi,structspi_transfer*transfer){structspi_imx_data*spi_imx=spi_master_get_devdata(master);/* 1. 配置传输参数 */spi_imx->speed_hz=transfer->speed_hz;spi_imx->bits_per_word=transfer->bits_per_word;spi_imx->count=transfer->len;spi_imx->tx_buf=transfer->tx_buf;spi_imx->rx_buf=transfer->rx_buf;spi_imx->txfifo=0;reinit_completion(&spi_imx->xfer_done);/* 2. 配置硬件 */spi_imx_config(spi_imx);/* 3. 根据是否使用 DMA 选择传输方式 */if(spi_imx->usedma){ret=spi_imx_dma_transfer(spi_imx,transfer);}else{/* 启动传输(触发中断) */spi_imx_push(spi_imx);/* 等待完成 */wait_for_completion(&spi_imx->xfer_done);}return0;}/** * spi_imx_isr - 中断处理函数 */staticirqreturn_tspi_imx_isr(intirq,void*dev_id){structspi_imx_data*spi_imx=dev_id;/* 1. 读取接收 FIFO */while(spi_imx_can_read(spi_imx))spi_imx->rx(spi_imx);/* 2. 写入发送 FIFO */if(spi_imx->count){spi_imx_push(spi_imx);returnIRQ_HANDLED;}/* 3. 传输完成 */if(spi_imx_can_read(spi_imx))returnIRQ_HANDLED;/* 4. 通知完成 */complete(&spi_imx->xfer_done);returnIRQ_HANDLED;}

SPI 设备驱动使用 SPI Master

// oled_drv.cstaticstructspi_device*oled;staticintspidev_probe(structspi_device*spi){/* 保存 spi_device 指针 */oled=spi;/* 配置 SPI 模式 */spi->mode=SPI_MODE_0;spi->bits_per_word=8;spi->max_speed_hz=10000000;// 10MHzspi_setup(spi);// ... 其他初始化 ...}staticvoidspi_write_datas(constunsignedchar*buf,intlen){/* 使用 SPI 核心层API发送数据 */spi_write(oled,buf,len);// ↓// 内部调用流程:// spi_write()// → spi_sync()// → spi_async()// → __spi_queued_transfer()// → 加入队列// → spi_pump_messages()// → master->transfer_one_message()// → 具体硬件驱动的传输函数// → 中断/DMA 完成// → complete()// → spi_sync() 返回}

SPI 框架的完整调用链

应用层/驱动层调用: spi_write(spi, buf, len) ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPI 核心层 (spi.c) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ↓ spi_sync(spi, &message) ↓ spi_async(spi, &message) ↓ __spi_queued_transfer(spi, &message) ↓ list_add_tail(&message->queue, &master->queue) ↓ kthread_queue_work(&master->kworker, &master->pump_messages) ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 内核线程: spi_pump_messages() ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ↓ master->prepare_transfer_hardware(master) // 准备硬件 ↓ master->prepare_message(master, message) // 准备消息 ↓ master->transfer_one_message(master, message) // 传输消息 ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 具体 Master 驱动 (spi-imx.c) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ↓ spi_imx_transfer_one_message() ↓ 遍历 message->transfers 链表 ↓ for_each_transfer: spi_imx_transfer_one(master, spi, transfer) ↓ 配置硬件寄存器 ↓ 启动传输(写 FIFO / 启动 DMA) ↓ 等待完成(中断或 DMA 回调) ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 中断处理 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ↓ spi_imx_isr(irq, dev_id) ↓ 读取 RX FIFO 写入 TX FIFO ↓ 传输完成? ↓ complete(&spi_imx->xfer_done) ↓ spi_imx_transfer_one() 返回 ↓ 下一个 transfer 或消息完成 ↓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 回到 SPI 核心层 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ↓ spi_finalize_current_message(master) ↓ message->complete(message->context) // 回调 ↓ spi_sync() 中的 wait_for_completion() 返回 ↓ spi_write() 返回到调用者

总结

层次职责文件
SPI 核心层消息队列管理、同步/异步传输、设备注册drivers/spi/spi.c
SPI Master驱动硬件初始化、传输实现、中断/DMA处理drivers/spi/spi-imx.c
SPI 设备驱动使用 SPI API 与设备通信你的oled_drv.c

关键点

  1. 分层设计:核心层提供统一接口,具体驱动实现硬件细节
  2. 队列化传输:现代驱动推荐使用transfer_one而非老式transfer
  3. 内核线程spi_pump_messages在后台处理消息队列
  4. 同步/异步:提供spi_sync(阻塞)和spi_async(非阻塞)两种方式
http://www.jsqmd.com/news/268059/

相关文章:

  • Windows平台USB Serial驱动下载:新手教程指南
  • python之lession4
  • bge-m3能否处理代码?编程语句语义匹配实测
  • 英文文献相关研究与应用分析
  • 利用ai搜索文献:提升学术研究效率与文献检索质量的实践探讨
  • 心理健康监测与疏导服务平台小程序
  • 如何选择香港top10研究生留学中介?资质正规是关键 - 留学机构评审官
  • 渗透测试流程到底是什么?这篇给你讲清楚(超详细)从零基础入门到精通,收藏这一篇就够了!
  • 【论文自动阅读】Diffusion Reward: Learning Rewards via Conditional Video Diffusion
  • 开发者学习指南:蓝牙低功耗安全(2)
  • fastboot驱动下USB协议实现原理的全面讲解
  • 上海研究生留学机构口碑排名揭。晓,无隐形消费保障服务透明可靠 - 留学机构评审官
  • 接口加密了怎么测?
  • 2026年汕头青少年心理疏导机构权威推荐榜单:儿童心理咨询 /青少年心理咨询 /心理咨询/ 青少年心理疏导服务机构精选
  • 快速掌握Linux启动过程:像看接力赛一样简单
  • 贾子智慧理论体系解释陈述说明 Explanatory Statement of the Kucius Wisdom Theoretical System
  • 新加坡硕士留学中介,经验丰富,助您成功申请top10院校 - 留学机构评审官
  • 2026集装箱房厂家权威推荐榜单:商业街集装箱房/装配式集装箱房/网红集装箱房/创意集装箱房/工地集装箱房源头厂家精选。
  • 选择合肥研究生留学中介?top10机构稳定可靠保障 - 留学机构评审官
  • 2026年实验仪器公司TOP10:盐城安信实验仪器有限公司优势分析 - 工业品牌热点
  • Jmeter请求发送加密参数
  • 杭州市余杭临平钱塘富阳临安区英语雅思培训辅导机构推荐,2026权威出国雅思课程中心学校口碑排行榜 - 老周说教育
  • 长沙研究生留学中介top10如何选择?性价比高服务全攻略 - 留学机构评审官
  • 基于CAN总线的UDS NRC错误反馈实测操作指南
  • 亲测好用8个AI论文平台,自考学生轻松搞定毕业论文!
  • 医用离心机企业哪家专业,盐城市安信实验仪器有限公司值得关注 - 工业品牌热点
  • 使用Jmeter进行http接口测试
  • 2026智能咖啡机哪家好?值得信赖的口碑品牌推荐+选购指南 - 品牌2026
  • 2026年专业高精密螺丝优质品牌推荐 - 优质品牌商家
  • 2026厂房暖通中央空调工程选哪家?这几家实力企业值得托付 - 品牌2025