Hi3516 + WS73 SDIO Wi-Fi 适配笔记

发布时间:2026/7/12 2:07:37
Hi3516 + WS73 SDIO Wi-Fi 适配笔记 一、编译过程WS73 驱动的编译入口一般是执行make platform wifi它本质上会分别进入 WS73 的 platform 和 wifi 驱动目录然后借用 Linux 内核的外部模块编译机制类似执行make -C /path/linux-5.10.y ARCHarm CROSS_COMPILEarm-v01c02-linux-musleabi- M/path/ws73/driver/platform modules。这里-C /path/linux-5.10.y表示进入内核源码/构建目录M...表示当前要编译的是外部模块目录。Linux 顶层 Makefile 发现M参数后会按外部模块方式处理这个目录读取该目录下的Makefile/Kbuild再通过scripts/Makefile.build根据obj-m、xxx-objs、obj-y等规则递归编译各个.c文件生成.o并把多个.o合成为模块主.o。之后scripts/Makefile.modpost会对模块做符号检查、依赖检查、license 检查、CRC/version 处理并生成.mod.c、.mod.o、Module.symvers等中间文件最后再由模块最终链接阶段把模块主.o和.mod.o链接成真正的.ko文件例如plat_soc.ko或wifi_soc.ko。所以整体可以理解为WS73 Makefile 只是组织编译入口和传参真正的.c - .o - .ko流程是借用 Linux Kbuild 外部模块机制完成的。二、加载plat_soc.ko1、加载plat_soc.ko的主要作用不是立即下载固件而是初始化WS73的平台资源、电源管理、HCC通信框架和SDIO通道并为后续固件下载做好准备2、如果Linux MMC核心启动时尚未发现WS73原来的hisi_sdio_rescan()会要求对应的mmc_host再次扫卡但现在系统已经生成了对应的mmc_card/sdio_func因此可以屏蔽这一步。3、随后sdio_register_driver()根据枚举阶段获得的vendor0x12D1、device0x0073在Linux设备模型中匹配WS73这个匹配过程只是软件比较ID并不会向Wi-Fi硬件发送数据匹配成功后调用sdio_probe()再由sdio_enable_func()通过MMC核心和mmc_host-ops发送SDIO CMD52命令使能WS73的Function 1同时设置块大小、FIFO和SDIO中断。4、真正的固件下载通常在首次打开WLAN、BLE或SLE业务时触发Linux先从/etc/ws73/ws73.bin等文件读取数据再经过firmware_send_func() → hcc_bus_patch_write() → hcc_sdio_patch_write() → oal_sdio_writesb()通过SDIO Function 1的FIFO把下载命令和固件数据分块写入WS73内部RAM因此固件下载也是走SDIO但使用的是启动/补丁下载协议不是正常网络数据协议。三、加载wifi_soc.kowifi_soc.ko的作用不是单纯注册wiphy而是同时完成 WiFi 控制面和数据面的接入。1、在控制面上wifi_soc.ko会注册wiphy并把驱动中的cfg80211_ops挂接到 Linux 的cfg80211无线管理框架中。这样用户态的iw、wpa_supplicant、hostapd等工具就可以通过nl80211/cfg80211向 WiFi 驱动下发控制命令这就会调用wiphy里面的ops。这类控制命令最终会被驱动封装成 WS73 固件能够识别的命令消息再通过 HCC/SDIO 层调用mmc_host使用 SDIO CMD52/CMD53 等方式发送给 WS73 芯片。2、在数据面上wifi_soc.ko还会注册wlan0这个net_device并绑定net_device_ops。当应用程序通过 socket 发送 TCP、UDP、ICMP 等网络数据时数据会先经过 Linux TCP/IP 协议栈协议栈根据路由选择wlan0作为出口然后调用wlan0对应的net_device_ops-ndo_start_xmit()。驱动在这个发送函数中拿到skb数据包再经过 WAL/HMAC/HCC 等层进行封装最后通过 SDIO 框架调用mmc_host使用 CMD53 把数据写入 WS73 芯片的发送队列或数据窗口中之后由 WS73 固件和 WiFi MAC/PHY 负责把数据通过无线空口发送出去。3、当 WS73 向主机发送数据时流程则是反过来的。WS73 固件收到无线数据包或者产生控制事件后会先把数据放到芯片内部的 RX FIFO、buffer 或 mailbox 中然后通过 SDIO 中断通知主机。主机侧的mmc_host/ uSDHC 控制器检测到 SDIO 中断后会进入 WiFi 驱动的中断处理流程。驱动通常先读取中断状态和数据长度然后通过 SDIO CMD53 从 WS73 的 RX 数据窗口中把数据读出来。读出的数据再经过 HCC 解包和 WAL/HMAC 分发如果是网络数据包就构造成skb通过netif_rx()或 NAPI 交给 Linux TCP/IP 协议栈。4、因此可以总结为wiphy/cfg80211主要负责 WiFi 的控制面wlan0/net_device_ops主要负责 WiFi 的数据面无论是控制命令还是网络数据最终都会经过驱动内部的 WAL/HMAC/HCC 层并通过 SDIO/mmc_host 与 WS73 固件进行通信。1. 适配的第一步确认硬件不是先编译拿到 WS73 驱动后第一件事应查看原理图确认 WS73 接到了 Hi3516 的哪一路 SDIO/MMC 控制器例如mmc0或mmc1。需要确认SDIO 的CLK、CMD、DAT0~DAT3分别接到哪些 SoC 引脚。模块供电电压、IO 电压、EN、RESET_N、唤醒 GPIO、是否有外部中断。CMD 和 DAT0~DAT3 的上拉电阻是否存在、阻值与电源是否正确。设备树中对应 MMC/SDIO host 是否启用pinmux、总线位宽、最大频率、电压能力是否与原理图一致。Wi-Fi 模块完成供电、解除复位、引脚复用配置后Linux 的 MMC/SDIO 框架应能枚举出设备。例如/sys/bus/sdio/devices/mmc0:0001:1 | | | | | -- SDIO Function 1 | --------- 卡 RCA通常为 0001 -------------- mmc0 主机控制器只有出现这个 SDIO 设备后WS73 的sdio_register_driver()才有对象可以匹配。若连mmc0:0001:1都没有问题仍在供电、复位、DAT0、引脚复用、设备树或 MMC 控制器枚举阶段尚未进入 Wi-Fi 驱动。MMC 相关配置需要按实际 Hi3516 host 驱动确认CONFIG_MMCy CONFIG_MMC_SDHCIy // 仅当实际 host 使用 SDHCI 框架时需要 CONFIG_MMC_BLOCKy // 对 SD 卡块设备有用SDIO Wi-Fi 本身不依赖它 CONFIG_CFG80211y // Wi-Fi 控制框架必需cfg80211不是把 AT 命令封装给 SDIO 的框架。它负责 Linux 无线控制面例如 scan、connect、disconnect、设置密钥等WS73 驱动再把这些请求封装成自身固件协议消息经 HCC/SDIO 发给芯片。2. WS73 的编译方式通常使用make wifi_clean platform_clean make platform wifi本质是借用 Linux Kbuild 编译外部模块WS73 Makefile | -- make -C linux-kernel Mws73-driver-dir modules | |-- 编译各 .c 文件为 .o |-- 按 obj-m / xxx-objs 合并模块对象 |-- modpost 检查符号、依赖、许可证、CRC -- 链接生成 plat_soc.ko、wifi_soc.ko 等模块模块必须使用与板端运行内核完全一致的内核源码、.config、内核头文件和Module.symvers编译。否则即使能加载也可能出现符号找不到、struct wireless_dev布局不一致或运行时崩溃。3. plat_soc.ko平台与 SDIO 通道准备plat_soc.ko的主要职责不是注册wlan0而是准备 WS73 的平台资源、HCC 通信通道和 SDIO Function。insmod plat_soc.ko | |-- 初始化平台资源 | // 电源、GPIO、HCC 总线、SDIO host 相关资源。 | |-- 必要时触发 SDIO 重扫描 | // 只有尚未枚举到 SDIO 卡时才有意义。 | |-- sdio_register_driver() | // 注册 WS73 的 SDIO 驱动及 vendor/device ID 表。 | -- sdio_probe() | |-- 匹配 vendor0x12D1、device0x0073 | // 只是 Linux 设备模型的软件 ID 比较。 | |-- sdio_enable_func() | // 经 MMC host 发 CMD52使能 Function 1。 | |-- 设置 block size、FIFO、SDIO 中断 -- 建立 HCC/SDIO 通信通道如果系统已经有mmc0:0001:1把旧的hisi_sdio_rescan()改成空操作可以绕过未导出符号问题但这只是“设备已经成功枚举”的前提下的兼容处理。若 SDIO 设备根本没有枚举不能靠屏蔽扫描函数解决硬件问题。固件下载一般发生在首次启动 WLAN、BLE 或 SLE 业务时具体时机以 WS73 源码为准。下载路径通常类似读取 /etc/ws73/*.bin | v firmware_send_func() | v hcc_bus_patch_write() | v hcc_sdio_patch_write() | v oal_sdio_writesb() | -- SDIO Function 1 FIFO // 分块写入启动命令、补丁和固件到 WS73 内部 RAM。固件下载也走 SDIO但属于启动/补丁协议不是普通网络收发协议。4. wifi_soc.ko控制面与数据面wifi_soc.ko同时接入无线控制面和网络数据面。wifi_soc.ko | |-- 创建 wiphy | | | |-- 注册 cfg80211_ops | | // scan、connect、disconnect、set_key 等控制入口。 | | | -- 注册到 cfg80211 | |-- 创建 net_devicewlan0 | | | |-- 注册 netdev_ops | | // ndo_open、ndo_stop、ndo_start_xmit 等网络收发入口。 | | | -- 注册到 Linux 网络协议栈 | -- 创建 wireless_devwdev | |-- wdev-wiphy wiphy |-- wdev-netdev wlan0 |-- wdev-iftype NL80211_IFTYPE_STATION -- wlan0-ieee80211_ptr wdev // 将 wlan0 与 cfg80211 无线对象关联。控制面路径wpa_supplicant / iw | v nl80211 | v cfg80211 | v wiphy-cfg80211_ops | v WS73 WAL/HMAC/HCC | -- SDIO CMD52/CMD53 - WS73 固件数据面发送路径应用程序 socket | v Linux TCP/IP 协议栈 | v wlan0-netdev_ops-ndo_start_xmit() | v WS73 WAL/HMAC/HCC | v SDIO CMD53 | -- WS73 TX FIFO/队列 - Wi-Fi MAC/PHY - 无线空口接收路径WS73 收到无线数据或控制事件 | |-- 写入内部 RX FIFO / mailbox -- SDIO 中断通知主机 | |-- 驱动读取状态与数据长度 |-- CMD53 读取 RX 数据 |-- HCC/WAL/HMAC 解包 -- netif_rx() / NAPI - Linux TCP/IP 协议栈5. wiphy、wlan0 与 wdev 的关系wdev不是驱动私有数据而是驱动与 cfg80211 共用的无线对象。真正的私有数据通常在wiphy_priv(wiphy); netdev_priv(net_dev);如果开启CONFIG_CFG80211_WEXTycfg80211 可能在wlan0被拉起时进入 WEXT 兼容流程并访问wdev-wext字段。若 WS73 模块与当前内核的struct wireless_dev布局不一致或者驱动没有使用当前内核要求的初始化流程便可能访问异常内存并崩溃。因此要点是CONFIG_CFG80211是现代nl80211/wpa_supplicant的基础。CONFIG_CFG80211_WEXT是旧 WEXT 兼容层不应因为“需要 Wi-Fi”而默认开启。若 WS73 依赖 WEXT 符号则需保证内核配置、模块编译环境和wdev初始化方式完全一致。“关闭 WEXT 不崩溃”只能说明问题位于 WEXT 兼容路径仍应检查wdev的分配、初始化和内核 ABI 一致性。6. SDIO 枚举失败error -16的含义以下日志说明问题发生在 Wi-Fi 驱动加载前CMD5 成功 CMD3 成功 CMD7 成功 card_busy timeout opcode52 arg0x00000000 sdio_read_cccr err-16 mmc_sdio_init_card err-16-16是-EBUSY不是 CMD5 无响应超时。日志中的第一次CMD5 err-110是单独的超时现象后面 CMD5 已成功并不代表 SDIO 数据线已经验证成功。opcode52是 CMD52arg0x00000000表示CMD52 read Function 0 address 0x00000 读取 CCCR_CCCR也就是在读取 SDIO 公共控制寄存器 CCCR 的第一步失败尚未访问 Wi-Fi Function 1也尚未进入 WS73 的sdio_probe()。更准确地说失败发生在 host 准备发 CMD52 前检查到 card busy 一直未释放。主机侧通常等价于认为DAT0仍处于低电平 busy 状态CMD5/CMD3/CMD7 成功 | // 主要证明 CMD CLK 通路基本正常 v 首次 CMD52 前 host 检查 card busy | -- DAT0 长时间为低 / host busy 状态异常 | -- 返回 -EBUSY(-16)重点排查DAT0是否被模块持续拉低。DAT0 上拉电阻、电源、焊接、卡座/模组连接是否正常。CMD7 后模块是否进入异常状态。SDIO IO 电压与模块要求是否一致。SoC pinmux、DAT0 输入状态、host 的card_busy()极性或寄存器判断是否正确。模块是否真的完成上电、解除复位和时钟稳定。因为已测试过降频、1-bit、关闭 high-speed 和 1.8V 仍失败问题更偏向 DAT0、电源、复位、引脚复用或 host busy 判断而不是高速模式协商。建议使用示波器或逻辑分析仪查看 CMD7 成功后的总线空闲状态通常应为 CMD、DAT0、DAT1、DAT2、DAT3高电平 CLK由 host 驱动可能停止或保持固定状态 重点 CMD7 后 DAT0 是否一直低。