SDIO_WiFi驱动学习之SDIO架构介绍及源码分析
一、引言
因为WiFi驱动比较复杂,所以WiFi驱动的博客将多分几篇来写。
本篇博客主要介绍Linux下的SDIO架构及源码分析。
本文部分内容摘抄自网络,若有侵权,请联系删除。
二、SDIO WiFi
SDIO-Wifi模块是基于SDIO接口的符合WiFi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈,能够实现用户主平台数据通过SDIO口到无线网络之间的转换。SDIO具有传输数据快,兼容SD、MMC接口等特点。
对于SDIO接口的WiFi,首先,它是一个SDIO的卡设备,然后具备了WiFi的功能。所以,注册的时候还是先以sdio设备去注册,然后检测到卡之后再去实现它的wifi功能。显然,他是用SDIO的协议,通过发命令和数据来控制实现WiFi功能。
三、相关概念
1、MMC与SD
SD 是一种flash memory card的标准,也就是一般常见的SD记忆卡,而MMC则是较早的一种记忆卡标准,目前已经被SD标准所取代。
2、SDIO
SDIO 故名思义,就是 SD 的 I/O 接口(interface)的意思,不过这样解释可能还有点抽像。更具体的说明,SD 本来是记忆卡的标准,但是现在也可以把 SD 拿来插上一些外围接口使用,这样的技术便是 SDIO。
所以 SDIO 本身是一种相当单纯的技术,透过 SD 的 I/O 引脚来连接外围,并且透过 SD 上的 I/O 数据引脚与这些外围传输数据,而且 SD 协会会员也推出很完整的 SDIO stack 驱动程序,使得 SDIO 外围(我们称为 SDIO 卡)的开发与应用变得相当热门。
现在已经有非常多的手机或是手持装置都支持 SDIO 的功能(SD 标准原本就是针对 mobile device 而制定),而且许多 SDIO 外围也都被开发出来,让手机外接外围更加容易,并且开发上更有弹性(不需要内建外围)。目前常见的 SDIO 外围(SDIO 卡)有:
- Wi-Fi card(无线网络卡)
- CMOS sensor card(照相模块)
- GPS card
- GSM/GPRS modem card
- Bluetooth card
- Radio/TV card
2.1、SDIO的传输模式
SDIO的传输模式有三种:
- SPI mode(required)
- 1-bit mode
- 4-bit mode
依据 SD 标准,所有的 SD(记忆卡)与 SDIO(外围)都必须支持 SPI mode,因此 SPI mode 是「required」。
2.2、SDIO的硬件电路
可以看到,SDIO接口总共有七个引脚,分别是DAT0-3(数据),CLK(时钟),CMD(命令),VIO(电源)。
四、MMC子系统框架
这里的MMC子系统框架包含了Linux的SDIO驱动架构,所以姑且将其看作SDIO的驱动架构。
linux/driver/mmc目录下有三个子目录:card 、core、host,分别表示区块层、核心层、主机控制层。其中card层、core层是Linux封装好的、标准的东西,不需要修改,需要修改的是host层,这一层需要驱动开发工程师根据平台来完成。
MMC子系统框架图。
Linux MMC子系统主要分成三个部分:
- MMC核心层:完成不同协议和规范的实现,为host层和设备驱动层提供接口函数。MMC核心层由三个部分组成:MMC,SD和SDIO,分别为三类设备驱动提供接口函数;
- Host 驱动层:针对不同主机端的SDHC、MMC控制器的驱动;
- Client 驱动层:针对不同客户端的设备驱动程序。如SD卡、T-flash卡、SDIO接口的GPS和wi-fi等设备驱动。
五、SDIO总线协议
SDIO总线 和 USB总线 类似,SDIO也有两端,其中一端是HOST端,另一端是device端。所有的通信都是由HOST端发送命令开始的,Device端只要能解析命令,就可以相互通信。对于SDIO总线,它的HOST端是开发板mmc控制器,而device端则是各种带SDIO接口的模块,比如SDIO WiFi模块。
SDIO协议是由SD卡协议演化升级而来的,很多地方保留了SD卡的读写协议,同时SDIO协议又在SD卡协议之上添加了CMD52和CMD53命令。由于这个,SDIO和SD卡规范间的一个重要区别就是增加了低速标准。低速卡的目标应用是以最小的硬件来支持低速I/O能力。
SD总线通信是基于指令和数据比特流,起始位开始和停止位结束。SD总线通信有三个元素:
- Command:由host发送到卡设备,使用CMD线发送;
- Response:从card端发送到host端,作为对前一个CMD的相应,通过CMD线发送;
- Data:即能从host传输到card,也能从card传输到host,通过data线传输。
1、SDIO信号
所谓SDIO信号,其实就是SDIO的pin脚所发出的信号。
SDIO有哪些pin脚在上面介绍SDIO时已经介绍过了,现在来说一下它们所发出的信号有哪些:
- CLK信号:HOST给Device的时钟信号;
- CMD信号:双向的信号,用于传送命令和反应;
- DAT0-DAT3信号:四条用于传输数据;
- VDD信号:电源信号;
- VSS1/2信号:电源地信号。
SDIO总线协议规定:在1BIT模式下,DAT0用来传输数据,DAT1用作中断线。在SDIO的4BIT模式下DAT0-DAT3用来传输数据,其中DAT1复用做中断线。
2、SDIO命令
以下是用于控制卡设备的指令类型,每个command都是固定的48位长度:
- broadcast commands(bc),no response:广播类型的指令,不需要有响应;
- broadcast commands with response(bcr):广播类型的指令且需要响应;
- addressed(point-to-point) commands(ac):由HOST发送到指定的卡设备,没有数据的传输;
- address(point-to-point) data transfercommands(adtc):由HOST发送到指定的卡设备且伴随有数据传输。
指令格式:
3、SDIO寄存器
下面介绍几个主要的寄存器:
- Operation condition register(OCR):32位的OCR包含卡设备支持的工作电压表;
- Card identification number register (CID):包含用于在卡识别阶段的卡信息,包括制造商ID,产品名等;
- Card specific data register(CSD):CSD寄存器提供了如何访问卡设备的信息,包括定义了数据格式,错误校验类型,最大访问次数,数据传输率等;
- Relative card address register(RCA):存放在卡识别阶段分配的相对卡地址,缺省相对卡地址为0000h;
- SD card configuration register(SCR):SCR是一个配置寄存器,用于配置SD memory card的特殊功能。
4、SDIO Reponse
所有的response都通过CMD线发送到host端,R4和R5响应类型是SDIO中特有的:
- R1(normal response command):用来响应常用指令;
- R2(CID,CSD register):用来响应CMD2和CMD10或CMD9,并把CID或CSD寄存器作为响应数据;
- R3(OCR register):用来响应ACMD41指令,并把OCR寄存器作为响应数据;
- R4(CMD5):响应CMD5,并把OCR寄存器作为响应数据;
- R5(CMD52):CMD52是一个读写寄存器的指令,R5用于CMD52的响应;
- R6(published RCA response):分配相对卡地址的响应;
- R7(card interface condition):响应CMD8,返回卡支持的电压信息;
六、MMC子系统的注册流程分析
1、注册虚拟总线
mmc_bus,sdio_bus 两条虚拟总线 : 在 drivers/mmc/core/core.c 中创建 mmc、sdio两条总线。
static int __init mmc_init(void)
{int ret;ret = mmc_register_bus();//创建 mmc总线ret = sdio_register_bus();//创建 sdio总线return ret;
}
subsys_initcall(mmc_init);
2、初始化并挂载设备驱动
初始化注册块设备,即card设备 : 在 drivers/mmc/card/block.c 中注册一个块设备,并将他挂载到前面创建的 mmc总线上。
static struct mmc_driver mmc_driver = {.drv = {.name = "mmcblk",},.probe = mmc_blk_probe,.remove = mmc_blk_remove,.suspend = mmc_blk_suspend,.resume = mmc_blk_resume,
};static int __init mmc_blk_init(void)
{int res;res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");//块设备注册res = mmc_register_driver(&mmc_driver);//注册 mmc_driver 即 card driver 到 mmc总线上return res;
}
初始化注册sdio设备驱动,在 drivers/mmc/card/sdio_uart.c 中注册一个sdio设备,并将他挂载到前面创建的 sdio总线上。
static struct sdio_driver sdio_uart_driver = {.probe = sdio_uart_probe,.remove = sdio_uart_remove,.name = "sdio_uart",.id_table = sdio_uart_ids,
};static int __init sdio_uart_init(void)
{int ret;...ret = sdio_register_driver(&sdio_uart_driver);return ret;
}
3、初始化注册主控驱动
初始化注册主控驱动 : drivers/mmc/host/sunxi-mmc.c//匹配 linux-3.10/arch/arm/boot/dts/sun8iw17p1-carvout.dtsi : compatible = "allwinner,sunxi-mmc-v4p1x";
static const struct of_device_id sunxi_mmc_of_match[] = {...{.compatible = "allwinner,sunxi-mmc-v4p1x",},//有!!...{ /* sentinel */ }
};MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);static struct platform_driver sunxi_mmc_driver = {.driver = {.name = "sunxi-mmc",.of_match_table = of_match_ptr(sunxi_mmc_of_match),.pm = sunxi_mmc_pm_ops,},.probe = sunxi_mmc_probe,.remove = sunxi_mmc_remove,.shutdown = sunxi_shutdown_mmc,
};module_platform_driver(sunxi_mmc_driver);//注册 host driver
4、Host驱动的prob
此时Host的设备和驱动都已经创建完成,platform_bus_type总线匹配其driver 和 device 后调用Host对应的prob函数,该函数内部会进行检测初始化slave等操作,检测sdio设备,并且创建 card 设备。
具体内容有:
static int sunxi_mmc_probe(struct platform_device *pdev)
{struct sunxi_mmc_host *host; //平台控制器信息结构体struct mmc_host *mmc;//描述卡控制器 结构体int ret;/* 4.1 1、分配 struct mmc_host结构体空间2、设置 host->detect = mmc_rescan 延期工作的任务初始化*/mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);//定义于drivers/mmc/core/host.c/*4.21、sunxi_mmc_host资源初始化 !!! 2、注册中断线程化 并完成 中断函数 以及 线程处理函数*/ret = sunxi_mmc_resource_request(host, pdev);.../*4.31、卡控制器 操作函数集合初始化*/mmc->ops = &sunxi_mmc_ops;
.../*4.41、添加mmc_host !!! core : host.c中提供*/ret = mmc_add_host(mmc);...
}
4.1、mmc_alloc_host
分配 struct mmc_host结构体空间;
设置 host->detect = mmc_rescan 初始化延期工作的任务,该任务的工作是扫描插入的卡并添加卡。
a : mmc_alloc_host
说明:经过分析代码 mmc_alloc_host()的主要工作只有两个,如下:
1、struct mmc_host结构体空间
2、初始化延期工作的任务:mmc_rescan()2.1、调用关系如下: 检测是否有卡,有的话扫描该卡,并将该卡设备添加到设备模型mmc_rescan()mmc_rescan_try_freq()mmc_attach_sdio()mmc_add_card()device_add()//挂载到sdio总线mmc_alloc_host()定义于 drivers/mmc/core/host.c。struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{int err;struct mmc_host *host;// 分配 struct mmc_host结构体空间host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
...//设置 host->detect = mmc_rescan 延期工作的任务初始化//注意:在这里只是初始化,并没有去调用它INIT_DELAYED_WORK(&host->detect, mmc_rescan);...
}EXPORT_SYMBOL(mmc_alloc_host);
b: mmc_rescan
注意:在4.1步骤只是去初始化这个工作任务,并没有实际去调用它,调用的地方在4.2注册的中断函数和4.4mmc_add_host中。
mmc_rescan 定义于 drivers/mmc/core/core.c,经过分析,它大概做了两件事:
第一 : 检测卡是否插入有效
第二 : 扫描该卡/*
host扫描
*/
void mmc_rescan(struct work_struct *work)
{/* 1、检测卡是否插入有效 */ ....../* 2、扫描该卡 */for (i = 0; i < ARRAY_SIZE(freqs); i++) {//扫描该卡if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) {...}...}mmc_release_host(host);}
c : 分析 mmc_rescan_try_freq
看一下 mmc_rescan_try_freq是怎么扫描的,可以看出,主要的工作就是按顺序检测 sdio设备、sd设备、mmc设备。我们的WiFi是SDIO设备,所以只需要关注mmc_attach_sdio(host) 就可以了。
/*
检测卡类型添加卡设备
*/
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
.../* 检测卡的类型 : SDIO类 SD类 MMC类host在扫描卡的过程中,其识别的顺序为SDIO SD MMC */if (!mmc_attach_sdio(host))return 0;if (!mmc_attach_sd(host))return 0;if (!mmc_attach_mmc(host))return 0;mmc_power_off(host);//掉电return -EIO;
}
d : mmc_attach_sdio
mmc_attach_sdio()定义于 drivers/mmc/core/sdio.c。/*host 扫描检测SDIO的识别过程 :
*/
int mmc_attach_sdio(struct mmc_host *host)
{struct mmc_card *card;1、向卡发送CMD5命令,该命令有两个作用第一,通过判断卡是否有反馈信息来判断是否为SDIO设备(只有SDIO设备才对CMD5命令有反馈,其他卡是没有反馈的)第二,如果是SDIO设备,就会给host反馈电压信息,就是说告诉host,本卡所能支持的电压是多少多少。2、host根据SDIO卡反馈回来的电压要求,给其提供合适的电压。3、初始化该SDIO卡 : mmc_sdio_init_card();card->type = MMC_TYPE_SDIO;//设置卡的类型为 MMC_TYPE_SDIO4、注册SDIO的各个功能模块5、添加SDIO卡err = mmc_add_card(host->card);}
e : mmc_add_card
drivers/mmc/core/bus.cint mmc_add_card(struct mmc_card *card)
{int ret;//将card注册进linux设备模型 注册结果就是可以在/sys/bus/sdio/devices目录下见到card 的名字ret = device_add(&card->dev); //struct device dev;return 0;
}
4.2、sunxi_mmc_resource_request
sunxi_mmc_host 资源初始化 ;
注册中断线程化并完成中断函数以及线程处理函数。其中中断函数中调到最后其实就是mmc_schedule_delayed_work(&host->detect, delay);即执行延期工作的任务调用host->detect = mmc_rescan 检测扫描添加 mmc设备。此处中断的扫描操作应该是为了识别那些在mmc_host 被添加之前插入的卡。
1、资源初始化
……
2、注册中断线程函数
devm_request_threaded_irq(&pdev->dev, host->irq, sunxi_mmc_irq,sunxi_mmc_handle_bottom_half, 0,"sunxi-mmc", host);sunxi_mmc_irq()mmc_detect_change(host->mmc, msecs_to_jiffies(500));//检测卡插入情况mmc_schedule_delayed_work(&host->detect, delay);//延迟 delay 调用host->detect = mmc_rescan 检测扫描添加 mmc设备
4.3、mmc->ops = &sunxi_mmc_ops
卡控制器,操作函数集合初始化。
4.4、mmc_add_host
创建host->class_dev设备(为代表当前设备host的device节点),并注册它到sysfs中;开始添加host,mmc_start_host(host),该函数调用到最后也mmc_schedule_delayed_work(&host->detect, delay) 即执行延期工作的任务,调用host->detect = mmc_rescan 检测扫描添加 mmc设备。
ret = mmc_add_host(mmc); //主要工作只有两个
1、创建一个设备,并注册它到sysfs中
2、开始添加host : mmc_start_host(host)mmc_start_host(host)mmc_detect_change(host, 0);mmc_schedule_delayed_work(&host->detect, delay);//延迟 delay 调用host->detect = mmc_rescan 检测 mmc设备
int mmc_add_host(struct mmc_host *host){int err;WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&!host->ops->enable_sdio_irq);//创建一个设备,并注册它到sysfs中 。 struct device class_dev; 代表本节点 host->class_dev为代表当前设备的 device节点err = device_add(&host->class_dev);
.../*开始添加host ,定义于drivers/mmc/core/core.c */mmc_start_host(host);
...return 0;
}void mmc_start_host(struct mmc_host *host)
{...mmc_detect_change(host, 0);//调用host->detect 检测 mmc设备
}void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
...mmc_schedule_delayed_work(&host->detect, delay);//延迟 delay 调用host->detect = mmc_rescan 检测 mmc设备
}EXPORT_SYMBOL(mmc_detect_change);
5、匹配设备与驱动,并调用驱动prob
经过以上步骤,mmc虚拟总线、sdio虚拟总线和host主机控制器驱动就注册好了。
检测是否有sdio设备的是4.1中初始化的工作任务,当调用这个工作任务检测到设备时,就会将这个设备注册添加到相应的虚拟总线上。注册添加成功之后,系统最终会找到设备(实现WLAN功能)的驱动,调用它的probe。
至此,SDIO的驱动架构/MMC子系统就介绍完毕了,具体的WiFi功能相关的设备驱动下一篇博客再作介绍。
SDIO_WiFi驱动学习之SDIO架构介绍及源码分析相关推荐
- Spring_AOP架构介绍与源码分析(含事务深度分析)
请见链接:http://edu.51cto.com/course/16573.html?source=so 第1章课程介绍6分钟1节 1-1课程介绍[免费试看]06:11 第2章AOP深入分析52分钟 ...
- Netty网络框架学习笔记-16(心跳(heartbeat)服务源码分析)
Netty网络框架学习笔记-16(心跳(heartbeat)服务源码分析_2020.06.25) 前言: Netty 作为一个网络框架,提供了诸多功能,比如编码解码等,Netty 还提供了非常重要的一 ...
- Stable Diffusion 原理介绍与源码分析(一)
Stable Diffusion 原理介绍与源码分析(一) 文章目录 Stable Diffusion 原理介绍与源码分析(一) 前言(与正文无关,可以忽略) 总览 说明 Stable Diffusi ...
- GAT 算法原理介绍与源码分析
GAT 算法原理介绍与源码分析 文章目录 GAT 算法原理介绍与源码分析 零. 前言 (与正文无关, 请忽略) 广而告之 一. 文章信息 二. 核心观点 三. 核心观点解读 四. 源码分析 4.1 G ...
- 10年大厂程序员是如何高效学习使用redis的丨redis源码分析丨redis存储原理
10年大厂程序员是怎么学习使用redis的 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表和B+树详细对比分析 视频讲解如下,点击观看: 10年大厂程序员是如何高效学 ...
- SRS流媒体服务器架构设计及源码分析丨音视频开发丨C/C++音视频丨Android开发丨嵌入式开发
SRS流媒体服务器架构设计及源码分析 1.SRS流媒体服务器架构设计 2.协程-连接之间的关系 3.推流-转发-拉流之间的关系 4.如何手把手调试SRS源码 视频讲解如下,点击观看: SRS流媒体服务 ...
- Ui学习笔记---EasyUI的EasyLoader组件源码分析
Ui学习笔记---EasyUI的EasyLoader组件源码分析 技术qq交流群:JavaDream:251572072 1.问题1:为什么只使用了dialog却加载了那么多的js http: ...
- Redis 的 Sentinel哨兵介绍与源码分析(1):初始化部分
http://www.redis.cn/topics/sentinel.html redis-6.0.8 本文是在官方中文文档的基础上进行的源码分析,其中包含完整的原文,并在此基础上,添加源码介绍. ...
- python Django之 DRF(一)框架介绍、源码分析
文章目录 一.django rest framework 框架的介绍 1.什么是RESTful规范? 2.RESTful API的介绍 二.drf框架源码解读 1.drf框架的使用 2.APIView ...
最新文章
- vue响应式给数组中的对象添加新属性
- 后台开发必读书籍--算法导论
- 第十九讲 拉普拉斯变换引入
- 关于学习的一则小故事
- eclipse maven项目 class类部署不到tomcat下_Spring Boot的两种部署方式:jar包和war包
- Java编程基础08——面向对象_构造方法静态static
- asp.net 中的 主从 新增,修改,删除。
- 全网首发:warning: #warning “Using deprecated NumPy API, disable it by “ “#defining NPY_NO_DEPRECATED_API
- Opencv特征点检测
- python计算机视觉库_荐 python计算机视觉入门
- hex与bin文件及hex2bin
- 推荐6款好用、免费的远程控制软件【远程管理工具】
- 不知道怎样计算权重?告诉你8种确定权重方法
- Java HotSpot(TM)64位服务器虚拟机警告
- 老人拿家谱自称傅友德后代,学者:朱元璋诛九族却放过了六岁小孩
- 软件测试1 软件测试分类
- 我的世界 Unity3D MineCraft 用Unity3D制作类似MineCraft我的世界的游戏 正经梳理一下开发01
- 【UE4_蓝图】map函数以及Clamped和Unclamped的区别
- 电脑文件管理,批量提取文件名到excel表格,一招搞定
- ubuntu(服务端)+windows(客户端)搭建iscsi