一、引言

因为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架构介绍及源码分析相关推荐

  1. Spring_AOP架构介绍与源码分析(含事务深度分析)

    请见链接:http://edu.51cto.com/course/16573.html?source=so 第1章课程介绍6分钟1节 1-1课程介绍[免费试看]06:11 第2章AOP深入分析52分钟 ...

  2. Netty网络框架学习笔记-16(心跳(heartbeat)服务源码分析)

    Netty网络框架学习笔记-16(心跳(heartbeat)服务源码分析_2020.06.25) 前言: Netty 作为一个网络框架,提供了诸多功能,比如编码解码等,Netty 还提供了非常重要的一 ...

  3. Stable Diffusion 原理介绍与源码分析(一)

    Stable Diffusion 原理介绍与源码分析(一) 文章目录 Stable Diffusion 原理介绍与源码分析(一) 前言(与正文无关,可以忽略) 总览 说明 Stable Diffusi ...

  4. GAT 算法原理介绍与源码分析

    GAT 算法原理介绍与源码分析 文章目录 GAT 算法原理介绍与源码分析 零. 前言 (与正文无关, 请忽略) 广而告之 一. 文章信息 二. 核心观点 三. 核心观点解读 四. 源码分析 4.1 G ...

  5. 10年大厂程序员是如何高效学习使用redis的丨redis源码分析丨redis存储原理

    10年大厂程序员是怎么学习使用redis的 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表和B+树详细对比分析 视频讲解如下,点击观看: 10年大厂程序员是如何高效学 ...

  6. SRS流媒体服务器架构设计及源码分析丨音视频开发丨C/C++音视频丨Android开发丨嵌入式开发

    SRS流媒体服务器架构设计及源码分析 1.SRS流媒体服务器架构设计 2.协程-连接之间的关系 3.推流-转发-拉流之间的关系 4.如何手把手调试SRS源码 视频讲解如下,点击观看: SRS流媒体服务 ...

  7. Ui学习笔记---EasyUI的EasyLoader组件源码分析

    Ui学习笔记---EasyUI的EasyLoader组件源码分析 技术qq交流群:JavaDream:251572072   1.问题1:为什么只使用了dialog却加载了那么多的js   http: ...

  8. Redis 的 Sentinel哨兵介绍与源码分析(1):初始化部分

    http://www.redis.cn/topics/sentinel.html redis-6.0.8 本文是在官方中文文档的基础上进行的源码分析,其中包含完整的原文,并在此基础上,添加源码介绍. ...

  9. python Django之 DRF(一)框架介绍、源码分析

    文章目录 一.django rest framework 框架的介绍 1.什么是RESTful规范? 2.RESTful API的介绍 二.drf框架源码解读 1.drf框架的使用 2.APIView ...

最新文章

  1. vue响应式给数组中的对象添加新属性
  2. 后台开发必读书籍--算法导论
  3. 第十九讲 拉普拉斯变换引入
  4. 关于学习的一则小故事
  5. eclipse maven项目 class类部署不到tomcat下_Spring Boot的两种部署方式:jar包和war包
  6. Java编程基础08——面向对象_构造方法静态static
  7. asp.net 中的 主从 新增,修改,删除。
  8. 全网首发:warning: #warning “Using deprecated NumPy API, disable it by “ “#defining NPY_NO_DEPRECATED_API
  9. Opencv特征点检测
  10. python计算机视觉库_荐 python计算机视觉入门
  11. hex与bin文件及hex2bin
  12. 推荐6款好用、免费的远程控制软件【远程管理工具】
  13. 不知道怎样计算权重?告诉你8种确定权重方法
  14. Java HotSpot(TM)64位服务器虚拟机警告
  15. 老人拿家谱自称傅友德后代,学者:朱元璋诛九族却放过了六岁小孩
  16. 软件测试1 软件测试分类
  17. 我的世界 Unity3D MineCraft 用Unity3D制作类似MineCraft我的世界的游戏 正经梳理一下开发01
  18. 【UE4_蓝图】map函数以及Clamped和Unclamped的区别
  19. 电脑文件管理,批量提取文件名到excel表格,一招搞定
  20. ubuntu(服务端)+windows(客户端)搭建iscsi

热门文章

  1. XAML高级教程(一)
  2. Android_气泡效果
  3. css方法实现表格表头固定,横向纵向可滑动
  4. Qt 6.0 以上版本应用 qCustomPlot
  5. WebService - WSDL报错 s:element ref=s:schema /s:any /
  6. WPS文档找回未保存文档最实用的方法(非备用中心)
  7. 88S5自己网站所许扩展笔记
  8. Rackspace开设英国最环保数据中心
  9. rackspace: openstack
  10. 阿里云栖大会的第一天,看见未来