基本知识

SD和MMC
SD (Secure Digital) 与 MMC (Multimedia Card) MMC 是较早的一种记忆卡标准,目前已经被 SD 标准取代。 SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡。
SDIO(Secure Digital I/O)
SDIO 就是 SD 的 I/O 接口的意思。 更具体的说,SD 本来是记忆卡的标准,但是现在也可以把 SD 拿来插上一些外围接口使用,这样的技术便是 SDIO。
SDIO 通过 SD 的 I/O 管脚来连接外部的外围 device 并传输数据。这些外围设备,我们称为 SDIO 卡,常见的有:
Wi-Fi card(无线网络卡)
CMOS sensor card(照相模块)
GPS card
GSM/GPRS modem card
Bluetooth card
Radio/TV card
SDIO 卡 和 SD 卡 的区别
SD卡使用的是SD卡协议,而SDIO卡使用的是SDIO协议! 协议不一样,初始化/读写方式也不一样!
SDIO-Wifi 模块
SDIO-Wifi 模块是基于 SDIO 接口的符合 wifi 无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈,能够实现用户主平台数据通过SDIO口到无线网络之间的转换。 SDIO 具有传输数据快,兼容SD、MMC接口等特点。
对于SDIO接口的wifi,首先,它是一个sdio的卡的设备,然后具备了wifi的功能。 所以,注册的时候还是先以sdio的卡的设备去注册的。然后检测到卡之后就要驱动他的wifi功能
SDIO 总线
SDIO总线 和 USB总线 类似,SDIO也有两端,其中一端是HOST端,另一端是device端。所有的通信都是由HOST端 发送 命令 开始的,Device端只要能解析命令,就可以相互通信。 CLK信号:HOST给DEVICE的 时钟信号,每个时钟周期传输一个命令。 CMD信号:双向 的信号,用于传送 命令 和 反应。 DAT0-DAT3 信号:四条用于传送的数据线。 VDD信号:电源信号。 VSS1,VSS2:电源地信号。
SDIO 命令
SDIO总线上都是HOST端发起请求,然后DEVICE端回应请求。 SDIO 命令由6个字节组成。
a – Command:用于开始传输的命令,是由HOST端发往DEVICE端的。其中命令是通过CMD信号线传送的。 b – Response:回应是DEVICE返回的HOST的命令,作为Command的回应。也是通过CMD线传送的。 c – Data:数据是双向的传送的。可以设置为1线模式,也可以设置为4线模式。数据是通过DAT0-DAT3信号线传输的。
SDIO的每次操作都是由HOST在CMD线上发起一个CMD,对于有的CMD,DEVICE需要返回Response,有的则不需要。 对于读命令,首先HOST会向DEVICE发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个读传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应。 对于写命令,首先HOST会向DEVICE发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个写传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应
WIFI 模块解析和启动流程
对于 Wifi 模组的 android 上层的分析,这篇文章讲的非常不错: http://blog.csdn.net/ylyuanlu/article/details/7711433 这篇文章将下图蓝色的和绿色的部分讲的非常详细
SDIO 接口驱动
SDIO 接口的 wifi,首先,它是一个 sdio 卡 设备,然后具备了 wifi 的功能,所以 SDIO 接口的 WiFi 驱动就是在 wifi 驱动 外面套上了一个 SDIO 驱动 的外壳。
SDIO 驱动部分代码结构如下
drivers/mmc 下有 mmc卡、sd卡、sdio 卡驱动。
SDIO驱动仍然符合设备驱动的分层与分离思想。
设备驱动层(wifi 设备):
| 核心层(向上向下提供接口)
| 主机驱动层(实现 SDIO 驱动)
我们主要关心 core 目录(CORE 层),其中是媒体卡的通用代码。包括 core.c host.c stdio.c。 CORE 层完成了:
1. 不同协议和规范的实现
2. 为 HOST 层的驱动提供了接口函数
3. 完成了 SDIO 总线注册
4. 对应 ops 操作
5. 以及支持 mmc 的代码 host 目录(HOST 层)是根据不同平台而编写的 host 驱动
Block Diagram

模块工作原理:
无线模块需要在i.MX8MQ主机系统上加载内核驱动程序,并在基于NXP的无线模块上运行固件。当接口总线驱动程序检测到基于NXP的无线模块时,MLAN模块通过接口适配器下载固件二进制文件。NXP Wi-Fi驱动程序从内核中的cfg80211子系统加载到总线驱动程序和网络堆栈之间。NXP内核驱动程序包括一组控件和配置,用于通过以下接口之一与用户空间通信:

SDIO扫描原理:
1、drivers/mmc/core/core.c-->mmc_rescan,在3个频率下扫描设备(400k、300k、200k、100k)
2、dirvers/mmc/core/core.c-->mmc_rescan_try_freq,在扫描前先发个sdio_reset(CMD52)命令,只有sdio设备会有响应,之后按照sdio(CMD5),sd,mmc的顺序扫描,一单扫到立即返回。
如果成功检测到设备,便会进行后续的mmc_sdio_card和mmc_add_func,进行建立设备节点操作。
/*
* Starting point for SDIO card init.
*/
int mmc_attach_sdio(struct mmc_host *host)
{
int err, i, funcs;
u32 ocr, rocr;
struct mmc_card *card;
printk("%s line=%d\n",__FUNCTION__,__LINE__);
WARN_ON(!host->claimed);
.....
err = mmc_sdio_init_card(host, rocr, NULL, 0);
......
for (i = 0; i < funcs; i++, card->sdio_funcs++) {
#ifdef CONFIG_MMC_EMBEDDED_SDIO
if (host->embedded_sdio_data.funcs) {
struct sdio_func *tmp;
tmp = sdio_alloc_func(host->card);
} else {
#endif
printk("%s line=%d\n",__FUNCTION__,__LINE__);
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
}
mmc_release_host(host);
err = mmc_add_card(host->card);
if (err)
goto remove_added;
/*
* ...then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]);
if (err)
goto remove_added;
}
}
设备树配置:
&usdhc3 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_gpio>;
pinctrl-1 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_gpio>;
pinctrl-2 = <&pinctrl_usdhc3>, <&pinctrl_usdhc3_gpio>;
bus-width = <4>;
pinctrl-assert-gpios = <&lsio_gpio4 10 GPIO_ACTIVE_HIGH>;
/*follow is power management features*/
non-removable;
cap-power-off-card;
keep-power-in-suspend;
pm-ignore-notify;
//enable-sdio-wakeup;
vmmc-supply = <®_usdhc3_vmmc>;//a phandle of a regulator, supplying VCC to the card
status = "okay";
};
pinctrl_usdhc3: usdhc3grp {
fsl,pins = <
IMX8QM_USDHC3_CLK_CONN_USDHC3_CLK 0x06000041
IMX8QM_USDHC3_CMD_CONN_USDHC3_CMD 0x00000021
IMX8QM_USDHC3_DATA0_CONN_USDHC3_DATA0 0x00000021
IMX8QM_USDHC3_DATA1_CONN_USDHC3_DATA1 0x00000021
IMX8QM_USDHC3_DATA2_CONN_USDHC3_DATA2 0x00000021
IMX8QM_USDHC3_DATA3_CONN_USDHC3_DATA3 0x00000021
>;
};
pinctrl_usdhc3_gpio: usdhc3grpgpio {
fsl,pins = <
IMX8QM_USDHC3_RESET_B_LSIO_GPIO4_IO09 0x00000021/*C7*/
IMX8QM_SAI1_RXFS_LSIO_GPIO3_IO14 0x00000021/*AU3*//*pull-up:low drive strength*/
IMX8QM_USDHC3_VSELECT_LSIO_GPIO4_IO10 0x00000021/*A7*/
/*IMX8QM_ADC_IN2_LSIO_GPIO3_IO20 0x00000021/*AP8*/
>;
};
驱动加载和模块配置
驱动源码可以找供应商提供,编译完成后会生成对应文件,我们一般需要用到下面三个文件wifi_mod_para_conf、mlan.ko、maol.ko
把firmware和wifi_mod_para.conf拷贝到firmware搜索目录,这里注意linux一般是/lib/firmware,在这个目录下新建nxp目录,拷贝到/lib/firmware/nxp
Insmod mlan.ko
Insmod maol.ko mod_para=nxp/wifi_mode_para.conf drvdbg=0x0007
加载完成后dmesg的打印信息如下:
[ 466.774446] wlan: Loading MWLAN driver
[ 466.778964] wlan: Register to Bus Driver...
[ 466.783182] sdio_register_driver
[ 466.819410] mmc_sdio_init_card!
[ 466.896334] vendor=0x02DF device=0x9141 class=0 function=1
[ 466.901935] Attach moal handle ops, card interface type: 0x104
[ 466.910810] SD8997: init module param from usr cfg
[ 466.915712] card_type: SD8997, config block: 0
[ 466.920201] cfg80211_wext=0xf
[ 466.923207] wfd_name=p2p
[ 466.925815] max_vir_bss=1
[ 466.929519] cal_data_cfg=none
[ 466.932540] drv_mode = 7
[ 466.935106] ps_mode = 2
[ 466.937556] auto_ds = 2
[ 466.940930] fw_name=nxp/sdiouart8997_combo_v4.bin
[ 466.946629] SDIO: max_segs=128 max_seg_size=65535
[ 466.951450] rx_work=1 cpu_num=6
[ 466.954778] Attach mlan adapter operations.card_type is 0x104.
[ 466.955339] wlan: Enable TX SG mode
[ 466.955344] wlan: Enable RX SG mode
[ 466.958087] Request firmware: nxp/sdiouart8997_combo_v4.bin
[ 467.732864] Wlan: FW download over, firmwarelen=571312 downloaded 571312
[ 468.485944] WLAN FW is active
[ 468.488922] on_time is 468383133090
[ 468.532213] fw_cap_info=0x181c3fa3, dev_cap_mask=0xffffffff
[ 468.532242] max_p2p_conn = 8, max_sta_conn = 8
[ 468.539156] wlan: mlan0 set max_mtu 2000
[ 468.548440] wlan: uap0 set max_mtu 2000
[ 468.558506] wlan: p2p0 set max_mtu 2000
[ 468.565702] wlan: version = SD8997----16.92.21.p41-MM5X16322.p3-(FP92)
[ 468.577914] wlan: Register to Bus Driver Done
[ 468.582313] wlan: Driver loaded successfully
无线配置
驱动正常加载后ifconfig可以查看模块信息
ifconfig -a
mlan0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 192.168.1.110 netmask 255.255.255.0 broadcast 192.168.1.255
ether b4:8c:9d:0d:88:d3 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
p2p0: flags=4098<BROADCAST,MULTICAST> mtu 1500
ether b6:8c:9d:0d:88:d3 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
uap0: flags=4098<BROADCAST,MULTICAST> mtu 1500
ether b4:8c:9d:0d:89:d3 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
使用wpa配置无线网络
1、配置和连接无线只需要修改/etc/wpa_supplicant/test.confg
ctrl_interface=/var/run/wpa_supplicant
update_config=1
network={
ssid="HR-office" /*wifi热点*/
key_mgmt=WPA-PSK
psk="hairoutech@2020" /*wifi热点密码*/
}
2、设置mlan0的ip:ifconfig mlan0 172.18.150.30 netmask 255.255.255.0 up
3、连接到AP:wpa_supplicant -D nl80211 -i mlan0 -c /etc/wpa_supplicant/HR-office.conf &