ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Linux SDIO驱动

Linux SDIO驱动 SDIO技术概述SDIO的基本概念与技术背景SDIOSecure Digital Input Output是一种基于SDSecure Digital存储卡标准扩展的输入输出接口标准。它在物理和电气特性上与SD卡兼容但功能上有了极大的扩展。SDIO最初的设计目的是为了让SD插槽不再局限于存储功能而是能够连接各种外设为移动设备提供了一种标准化的扩展接口。从技术本质上看SD、MMC和SDIO都属于同一技术体系都是从MMC规范演化而来其中MMC强调多媒体存储SD强调安全和数据保护而SDIO则专注于输入输出功能。在物理接口层面SDIO使用了与SD卡相同的9针接口设计包括时钟线CLK、命令线CMD、数据线DAT0-DAT3和电源线等。SDIO设备支持两种总线模式1位模式只使用DAT0和4位模式使用DAT0-DAT3。与SD存储卡主要进行大数据块传输不同SDIO设备通常需要进行多次小数据量的传输同时还需要中断机制来处理异步事件。从技术架构角度来看SDIO规范在SD基础上增加了以下关键特性I/O模式、中断机制、字节传输模式和多功能设备支持。这使得SDIO可以连接网络适配器、蓝牙模块、数码相机、GPS接收器等多样化的功能设备大大扩展了SD接口的应用范围。Linux SDIO代码框架与核心数据结构1.MMC子系统模块划分与关系Linux内核中的MMC子系统代码主要位于drivers/mmc目录下包含三个核心子目录card/、core/和host/。这种目录结构直观地反映了子系统的分层架构。host目录包含各种平台相关的SD/MMC主机控制器驱动如STM32、i.MX、OMAP等芯片的驱动实现。每个主机控制器驱动都通过struct mmc_host_ops中定义的回调函数与核心层交互。core目录实现了MMC、SD和SDIO的协议栈提供公共接口和数据结构负责总线的注册、设备的探测和驱动匹配等核心功能。card目录主要处理存储卡相关的逻辑包括块设备请求的处理、分区解析等。对于SDIO设备此部分负责其作为存储功能的相关处理。从软件架构看MMC子系统涉及三条虚拟总线platform_bus_type用于主机控制器、mmc_bus_type用于存储卡和sdio_bus_type用于SDIO功能设备。这种多总线模型使得子系统能够灵活地管理不同类型的设备和驱动。2.核心数据结构及其关系SDIO子系统的核心数据结构包括struct mmc_host、struct mmc_card、struct sdio_func和struct sdio_driver它们共同描述了系统中SDIO的各个组件及其相互关系。数据结构间的关系3.主机控制器操作与SDIO总线交互主机控制器驱动通过实现struct mmc_host_ops中定义的回调函数来与核心层交互。其中与SDIO密切相关的关键操作包括request()处理MMC请求的核心方法负责发送命令和数据传输set_ios()配置总线的电气特性如时钟频率、总线宽度和电压enable_sdio_irq()启用或禁用SDIO中断这对于SDIO设备的异步事件处理至关重要SDIO总线使用struct sdio_device_id来进行驱动和设备之间的匹配。该结构包含厂商ID、设备ID和功能号等信息。当插入SDIO设备时内核会遍历已注册的SDIO驱动并比较id_table中的信息如果匹配则调用驱动的probe函数。设备匹配与生命周期管理sdio_bus.c中定义了SDIO总线类型的具体行为这是驱动模型得以运行的关键。它确保了设备与驱动能够自动匹配并管理其整个生命周期。Sysfs属性自动化通过sdio_dev_attrs和sdio_drv_attrs等属性组内核自动为每个SDIO功能设备在**/sys/bus/sdio/devices/**下创建属性文件如vendor、device、class等。其中modalias属性格式如sdio:c00v02D0d4324尤为重要它是udev或modprobe自动加载对应驱动模块的依据。核心注册匹配逻辑SDIO驱动通过**sdio_register_driver()**函数向系统注册该函数实际上是对driver_register()的封装将驱动添加到SDIO总线驱动列表中。intsdio_register_driver(structsdio_driver*drv){drv-drv.namedrv-name;drv-drv.bussdio_bus_type;returndriver_register(drv-drv);}当驱动注册后总线会遍历所有已连接的设备尝试将设备与驱动进行匹配。匹配过程由sdio_bus_match()函数完成它通过比较sdio_device_id结构中的厂商ID、设备ID和功能号来确定是否匹配。对于主机控制器驱动其注册过程略有不同。主机控制器驱动作为平台设备驱动注册到platform_bus_type上使用平台特定的设备名称进行匹配如omap_hsmmc。一旦匹配成功主机控制器的probe函数会被调用初始化硬件并注册mmc_host对象。设备发现与初始化过程SDIO设备的发现过程始于主机控制器的探测。当主机控制器驱动加载后它会初始化硬件并启动设备检测机制。在Linux内核中这一过程通常通过延迟工作队列delayed workqueue实现定期检查总线上是否有设备插入或移除。当检测到设备插入时主机控制器会启动SDIO初始化序列这一过程包括多个步骤1.电源上电与时钟设置主机控制器为设备提供电源并设置初始时钟频率通常为400kHz2.CMD5命令发送查询设备支持的电压范围3.CMD52/CMD53命令读取设备的CCCRCard Common Control Register和FBRFunction Basic Register寄存器4.功能枚举识别设备中的各个功能并分配资源设备初始化过程中不涉及数据线路的使用仅使用时钟和命令线。但如果SDIO DAT0线路被意外置为有效设置为低电平初始化过程可能会暂停并失败因为这会被主机解读为BUSY信号。多总线模型与驱动绑定Linux MMC子系统采用多总线模型来管理不同类型的设备。如下图所示子系统涉及三条总线platform_bus_type、mmc_bus_type和sdio_bus_type它们各自负责不同层次的驱动和设备匹配。drivers/mmc/core/sdio_bus.c实现了一个二级总线模型专门用于管理SDIO卡上的多个独立功能。这是解决“一张物理卡多个逻辑设备”问题的核心设计。诞生背景与核心问题标准的MMC总线将整张存储卡视为单一设备但这无法满足SDIO Combo卡如同时包含Wi-Fi和蓝牙的需求。SDIO总线驱动的诞生就是为了在MMC总线之上为每个I/O功能Function提供独立的设备与驱动绑定模型。工作原理层次化流程1.MMC总线发现MMC核心识别到SDIO卡并与通用SDIO卡驱动绑定。2.功能枚举通用驱动读取卡的CIS卡信息结构了解卡上所有功能。3.设备注册为每个功能创建一个sdio_func设备并注册到SDIO总线上。4.驱动匹配具体的功能驱动如Wi-Fi驱动通过ID表与设备匹配触发其probe函数完成初始化。这种设计的主要优势在于高度的模块化和精细的粒度。Wi-Fi驱动和蓝牙驱动可以像独立的JavaScript模块一样开发和维护互不干扰仅通过标准的SDIO总线接口交互。总线管理还负责协调多个SDIO功能设备。一个物理SDIO卡可以包含多个功能设备sdio_func如WiFi、蓝牙和GPS集成在一个卡上每个功能都有独立的功能号Function Number通常为1-7和独立的配置空间Configuration Space。每个功能的功能号在卡出厂时即被固化用于在总线上唯一标识该功能。其配置空间则包含了该功能的CISCard Information Structure其中详细定义了功能类型如Wi-Fi、蓝牙、GPS、制造商信息、支持的SDIO标准版本、最大块大小、中断能力以及电源管理需求等关键参数。这种设计使得主机能够像访问PCI设备配置空间一样精确地查询和配置每个SDIO功能。SDIO总线负责在这些功能之间共享主机控制器资源确保它们能够协同工作。这种多总线模型使得子系统能够灵活地支持各种类型的设备同时保持各层的独立性和可扩展性。例如SDIO WiFi驱动只需要关注sdio_bus_type上的注册和匹配而不需要了解底层主机控制器的具体实现细节。SDIO初始化和通信过程深度剖析设备初始化流程与命令交互SDIO设备的初始化是一个复杂的过程涉及多个标准命令的交换和寄存器配置。当主机检测到设备插入后会执行以下初始化序列1.基础初始化阶段主机发送CMD0GO_IDLE_STATE使设备进入空闲状态主机发送CMD5IO_SEND_OP_COND查询设备支持的电压范围和IO能力设备响应其OCROperating Conditions Register内容2.设备识别阶段主机发送CMD3SEND_RELATIVE_ADDR为设备分配相对地址RCA主机发送CMD7SELECT_CARD选择特定地址的设备将其置于传输状态3.SDIO特定初始化主机使用CMD52读取CCCRCard Common Control Register了解设备公共能力主机读取FBRFunction Basic Register获取每个功能的配置信息主机使用CMD52配置设备的工作参数如总线宽度和中断模式4.功能特定配置对于每个SDIO功能主机读取其CISCard Information Structure获取厂商ID、设备ID等信息主机配置每个功能的工作参数如块大小和传输模式在整个初始化过程中CMD52IO_RW_DIRECT命令起着关键作用它用于读写单个SDIO设备的寄存器是配置设备的主要手段。而CMD53IO_RW_EXTENDED则用于大数据量的传输支持字节模式和块模式两种传输方式。数据传输机制与状态管理SDIO设备支持两种数据传输模式字节模式和块模式。字节模式适用于小数据量的寄存器访问而块模式则适用于大数据量的传输如网络数据包或文件内容。数据传输涉及三个关键状态机的协同工作命令状态机、响应状态机和数据状态机。以下Mermaid状态图展示了SDIO数据读写的典型流程在实际的数据传输过程中主机控制器负责协调命令、响应和数据的流动。对于读操作主机先发送读命令然后设备返回响应和数据对于写操作主机发送写命令后接着发送数据然后设备返回操作结果。中断处理与电源管理SDIO设备的中断机制是其区别于普通SD存储卡的重要特性之一。SDIO设备通过将DAT1线拉低来向主机发送中断请求这使得设备能够在发生特定事件时如数据就绪、状态改变主动通知主机而不需要主机轮询设备状态。主机控制器驱动通过实现enable_sdio_irq()回调函数来控制系统对SDIO中断的响应。当该函数参数enable为true时主机应配置硬件监听DAT1线的中断信号当为false时则忽略该信号。在Linux内核中SDIO中断处理通常分为两个部分硬件中断处理在主机控制器的中断服务例程中检测到SDIO中断禁用进一步的中断并调度下半部此时cpu开中断可被自身或其他中断打断处理功能驱动处理底半部通知具体的SDIO功能驱动如WiFi驱动由驱动处理中断原因并采取相应行动电源管理是SDIO子系统另一个重要方面。SDIO设备支持多种电源状态包括活动状态、睡眠状态和关机状态。主机可以通过CMD52修改设备的电源管理寄存器来控制设备的电源状态从而实现功耗的动态管理。简单SDIO设备驱动实例实现驱动框架与注册代码#includelinux/module.h#includelinux/slab.h#includelinux/sdio.h#includelinux/device.h/* 定义设备ID表用于驱动匹配这里的ID是整张SDIO Card的硬件标识符 */staticconststructsdio_device_idsimple_sdio_ids[]{{SDIO_DEVICE(0x1234,0x5678)},/* 厂商ID:0x1234, 设备ID:0x5678 */{/* 结束 */}};MODULE_DEVICE_TABLE(sdio,simple_sdio_ids);/* 驱动probe函数当设备匹配时调用 */staticintsimple_sdio_probe(structsdio_func*func,conststructsdio_device_id*id){intret;structdevice*devfunc-dev;dev_info(dev,SDIO设备已检测 (厂商:%04x, 设备:%04x)\n,func-vendor,func-device);/* 启用SDIO功能 */retsdio_enable_func(func);if(ret){dev_err(dev,无法启用SDIO功能: %d\n,ret);returnret;}/* 设置块大小 */retsdio_set_block_size(func,64);if(ret){dev_err(dev,无法设置块大小: %d\n,ret);sdio_disable_func(func);returnret;}/* 注册中断处理程序 */retsdio_claim_irq(func,simple_sdio_irq);if(ret){dev_err(dev,无法注册中断处理程序: %d\n,ret);sdio_disable_func(func);returnret;}/* 初始化设备特定数据结构 */// init_device_data(dev);dev_info(dev,SDIO设备驱动加载成功\n);return0;}/* 驱动remove函数当设备移除或驱动卸载时调用 */staticvoidsimple_sdio_remove(structsdio_func*func){structdevice*devfunc-dev;/* 释放中断 */sdio_release_irq(func);/* 禁用SDIO功能 */sdio_disable_func(func);dev_info(dev,SDIO设备驱动已卸载\n);}/* 中断处理函数 */staticvoidsimple_sdio_irq(structsdio_func*func){/* 处理设备中断 */// handle_interrupts(func);}/* 定义SDIO驱动结构 */staticstructsdio_driversimple_sdio_driver{.namesimple_sdio,.id_tablesimple_sdio_ids,.probesimple_sdio_probe,.removesimple_sdio_remove,};/* 模块初始化函数 */staticint__initsimple_sdio_init(void){intret;retsdio_register_driver(simple_sdio_driver);if(ret)pr_err(无法注册SDIO驱动: %d\n,ret);elsepr_info(SDIO驱动注册成功\n);returnret;}/* 模块退出函数 */staticvoid__exitsimple_sdio_exit(void){sdio_unregister_driver(simple_sdio_driver);pr_info(SDIO驱动已注销\n);}module_init(simple_sdio_init);module_exit(simple_sdio_exit);MODULE_LICENSE(GPL);MODULE_AUTHOR(Your Name);MODULE_DESCRIPTION(Simple SDIO Device Driver);设备操作与数据传输实现基本的设备注册后我们需要添加设备操作和数据传输功能。下面的代码展示了如何实现SDIO设备的读写操作/* 从设备读取数据 */staticintsimple_sdio_read(structsdio_func*func,unsignedaddr,void*buf,unsignedsize){intret;/* 获取主机控制器 */retsdio_claim_host(func);if(ret)returnret;/* 执行读取操作 */if(size1){/* 单字节读取 */*(u8*)bufsdio_readb(func,addr,ret);}else{/* 多字节读取 */retsdio_readsb(func,buf,addr,size);}/* 释放主机控制器 */sdio_release_host(func);returnret;}/* 向设备写入数据 */staticintsimple_sdio_write(structsdio_func*func,unsignedaddr,void*buf,unsignedsize){intret;/* 获取主机控制器 */retsdio_claim_host(func);if(ret)returnret;/* 执行写入操作 */if(size1){/* 单字节写入 */sdio_writeb(func,*(u8*)buf,addr,ret);}else{/* 多字节写入 */retsdio_writesb(func,addr,buf,size);}/* 释放主机控制器 */sdio_release_host(func);returnret;}/* 实现文件操作接口 */staticssize_tsimple_sdio_fops_read(structfile*file,char__user*buf,size_tcount,loff_t*ppos){structsdio_func*funcfile-private_data;u8*kernel_buf;intret;/* 分配内核缓冲区 */kernel_bufkmalloc(count,GFP_KERNEL);if(!kernel_buf)return-ENOMEM;/* 从设备读取数据 */retsimple_sdio_read(func,*ppos,kernel_buf,count);if(ret0)gotoout;/* 将数据复制到用户空间 */if(copy_to_user(buf,kernel_buf,count)){ret-EFAULT;gotoout;}*pposcount;retcount;out:kfree(kernel_buf);returnret;}应用场景、局限性与总结主流应用场景Wi-Fi/蓝牙Combo卡最经典用例一张卡在系统中生成独立的网络接口和蓝牙设备。嵌入式外设如SDIO接口的GPS、NFC、Zigbee模块常用于物联网设备和单板计算机。局限性性能瓶颈SDIO协议本身的带宽通常最高约50-100 MB/s和延迟不及PCIe或USB 3.0限制了高性能应用。协议复杂性初始化、中断协商等机制比纯存储卡复杂增加了驱动调试难度。适用性仅适用于SDIO设备不用于SD存储卡用MMC总线或其他总线设备。学习内容来源https://blog.csdn.net/qq_38061020/article/details/154073165https://www.cnblogs.com/yangykaifa/p/19675859
返回列表