资讯详情

资讯详情

AP6212 Linux驱动移植:Wi-Fi/蓝牙SDIO与UART配置排障

简介这是一份面向嵌入式Linux开发者与系统集成人员的AP6212无线芯片驱动资料包围绕WiFi与蓝牙功能的内核适配、编译和移植展开适合具备一定内核基础、需要将驱动整合进目标板或排查硬件识别、模块加载、连接稳定性问题的人群。包内共14个文件以txt移植笔记与问题汇总、pdf用户指南、c源码及bz2、tgz、rar压缩源码包为主另附jpg版本对照图整体约5.31MB兼顾源码查阅与文档参考。已有2035人学习反映出该芯片在嵌入式项目中应用较广。其价值在于把驱动源码、移植步骤与实践中遇到的问题和解决办法集中在一起读者可据此理解内核接口、设备树配置和编译选项对照WiFi与蓝牙用户指南完成模块集成并借助问题汇总定位硬件识别失败、驱动加载异常、网络不稳等常见故障减少重复试错。1. 一块焊死 AP6212 的板子Wi-Fi 和蓝牙为什么双双失踪新板子第一次上电ip -br link只刷出lo和eth0dmesg | grep mmc1后面干干净净很多人第一反应是模块虚焊或者买到了坏料。实际情况多半是 AP6212 这套 SDIO Wi-Fi 加 UART 蓝牙的组合没配齐内核里brcmfmac没编进去、设备树里WL_REG_ON没人拉、固件文件名对不上任何一个环节断掉表现都是“没有无线网卡”。这份 AP6212 Linux 驱动资料要处理的就是这条链路——把内核配置、板级设备树、固件与 NVRAM、用户态工具四段接起来让 Wi-Fi 与蓝牙同时工作。适合正在给自研板子补无线功能或者接手一块无线功能残缺的板子、需要从零定位的人。2. 模块与选型SDIO 数据面加 UART 控制面两条链路各管一摊AP6212 是一个把 Wi-Fi 和蓝牙封在同一块屏蔽罩里的模组但它在 Linux 里根本不是“一个设备”而是两条互不相干的内核路径。搞清楚这一点后面看日志才不会把蓝牙的报错当成 Wi-Fi 的故障去查。2.1 硬件链路SDIO 走数据、UART 走控制、PCM 走语音Wi-Fi 侧挂在 SDIO 总线上用 CLK、CMD 和 DAT0~DAT3 四根数据线主机侧就是一个普通的 mmc host 控制器所以你在dmesg里看到的第一条日志永远是mmc1: new high speed SDIO card at address 0001这类 mmc 子系统的输出而不是wlan。蓝牙侧走的是 UART四线制 TX/RX/CTS/RTS波特率上到 1.5M 甚至 3M语音通话场景才会用到 PCM 那组引脚做数据联网用不上。除了数据线还有三组必须接对的信号。第一组是两个独立使能WL_REG_ON管 Wi-Fi 上电BT_REG_ON管蓝牙上电它们互相不联动所以“Wi-Fi 好了蓝牙没好”是完全正常的现象。第二组是两个唤醒WL_HOST_WAKE和BT_HOST_WAKE在 out-of-band 中断模式下芯片有数据要上报时靠这两根 GPIO 打断主机而不是靠 SDIO 总线的中断能力。第三组是 32.768kHz 的低功耗时钟LPOWi-Fi 和蓝牙共用缺了它两块功能可能都能枚举成功但一进省电模式就各种异常这是移植里最难查的一类问题。提示先拿万用表确认WL_REG_ON和BT_REG_ON在系统起来后是不是都被拉到了模块要求的高电平再去改驱动。硬件没上电软件怎么调都是白费功夫。2.2 内核态选型为什么只能走 brcmfmac 这条 fullmac 路无线网卡驱动在 Linux 里分两派。一派是 softmac例如常见的 USB 网卡驱动扫描、关联、加密、聚合这些活全在主机 CPU 上跑通过mac80211框架完成另一派是 fullmac协议栈的一大半被塞进芯片内部的固件里主机侧驱动只负责搬命令、收事件、转发以太网帧。AP6212 属于后者主机侧对应的是drivers/net/wireless/.../brcmfmac这个驱动它直接对接cfg80211不经过mac80211。这个选型带来的直接后果是内核配置里必须打开CONFIG_BRCMFMAC和CONFIG_CFG80211但不需要给这块网卡配速率控制算法、也不需要开CONFIG_MAC80211的软件加密。好处是小核 SoC 上跑 AP 模式时 CPU 占用很低几百兆主频的芯片也能带几路并发代价是出问题时你几乎没有中间层可以下钩子只能靠dmesg、/sys/kernel/debug/brcmfmac/下的计数器和固件自己吐的日志来定位。我一般会把这个判断写进项目笔记如果换型时选了 SDIO 接口的 softmac 模组hostapd的配置、功耗表现、甚至iw能看到的能力集都会变不能拿 AP6212 的调优参数直接套过去。2.3 资料包的组成与阅读顺序这类驱动资料通常由四块组成驱动源码目录brcmfmac、hci_bcm、btbcm相关文件很多是上游代码加板级补丁、板级设备树片段SDIO 节点、pwrseq、UART 下的蓝牙子节点、固件与 NVRAM.bin、.txt、.clm_blob、.hcd、以及一组上电验证脚本。阅读顺序不要从驱动源码开始那样容易被几千行 SDIO 收发逻辑困住。正确的顺序是先看设备树确认这颗模组在板子上到底怎么供电、怎么复位、中断接在哪根 GPIO再看内核 config 片段确认协议层有没有被裁掉然后看固件目录结构和命名规则最后才回头看brcmf_sdio_probe这条链理解每一个日志点是在哪一步打印的。按这个顺序读源码只是用来解释日志而不是用来背的。3. 编译与上电从 defconfig 到第一帧 brcmfmac 日志这一章是从零让板子认出无线网卡的全部动作。每一步都有明确的验收标准做完一步看一次日志别一口气全改完再上电那样出了问题你不知道是哪一步引入的。3.1 内核 config别把 BCDC 协议关掉SDIO 和 USB 接口的 brcmfmac 走的是 BCDC 协议PCIe 接口走的是 MSGBUF 协议这两个是互斥的编译选项。很多 SoC 厂商给的默认 defconfig 是为自家 PCIe 模组准备的你直接拿来编 SDIO 模组brcmfmac能编出来但 probe 阶段会卡在协议初始化上。# Wi-Fi 侧SDIO fullmac 必须走 BCDC CONFIG_CFG80211m CONFIG_BRCMFMACm CONFIG_BRCMFMAC_SDIOy CONFIG_BRCMFMAC_PROTO_BCDCy # CONFIG_BRCMFMAC_PROTO_MSGBUF is not set # 给 PCIe 用的SDIO 上开了也没意义 # 蓝牙侧UART 传输 厂商私有协议负责下补丁和写地址 CONFIG_BTm CONFIG_BT_HCIUARTm CONFIG_BT_HCIUART_SERDEVy CONFIG_BT_HCIUART_BCMy CONFIG_BT_BCMm # 基础设施少一个都会出现莫名其妙的空目录或 blocked CONFIG_RFKILLy CONFIG_MMCy CONFIG_MMC_SDHCIy CONFIG_MMC_SDHCI_PLTFMy逐条说下容易漏的CONFIG_BRCMFMAC_SDIO是总线胶水层关掉就只有 PCIe 版本CONFIG_BT_HCIUART_SERDEV决定蓝牙能不能从设备树自动绑定缺了它你只能在用户态用hciattach手动挂串口CONFIG_RFKILL影响的是rfkill list能不能看到设备没有它即使驱动加载成功wlan0也可能永远处于 blocked 状态而且你连“谁把它 block 了”都查不到。如果产品要求根文件系统挂载前就能联网比如网络恢复、OTA可以把固件编进内核CONFIG_EXTRA_FIRMWAREbrcm/brcmfmac43430-sdio.bin brcm/brcmfmac43430-sdio.txt配合CONFIG_EXTRA_FIRMWARE_DIR。代价是内核体积变大而且改 NVRAM 里的 MAC 必须重新烧内核量产阶段我一般不用这招。3.2 设备树SDIO 节点、pwrseq、OOB 中断、蓝牙 serdev设备树写错是这类模组最常见的失败原因尤其是复位时序。模组的上电顺序要求是先给VBAT和VDDIO再拉高WL_REG_ON等待一段时间数据手册一般要求几十毫秒以上后才能访问 SDIO。这段时序手工用fixed-regulator拼经常翻车标准做法是用mmc-pwrseq-simple。/* 模组上电与复位时序WL_REG_ON 交给 pwrseq 管不要写死成 fixed-regulator */ wifi_pwrseq: wifi-pwrseq { compatible mmc-pwrseq-simple; reset-gpios gpio1 12 GPIO_ACTIVE_LOW; /* WL_REG_ON低有效表示复位 */ post-power-on-delay-ms 200; /* 上电到 SDIO 可枚举的等待时间别省 */ clocks rtc 1; /* 32.768kHz LPOWi-Fi 低功耗要用 */ clock-names ext_clock; }; mmc1 { status okay; bus-width 4; non-removable; /* 模组焊死没有 card detect 引脚 */ keep-power-in-suspend; /* 休眠不断电否则唤醒后要重新走一遍枚举 */ cap-power-off-card; max-frequency 50000000; /* 走线一般时先降到 25000000 排障 */ vmmc-supply vcc_wifi; vqmmc-supply vcc_io; mmc-pwrseq wifi_pwrseq; #address-cells 1; #size-cells 0; wifi1 { compatible brcm,bcm4329-fmac; /* SDIO fullmac 的通用绑定串 */ reg 1; /* SDIO function 1 承载 WLAN */ interrupt-parent gpio0; interrupts 28 IRQ_TYPE_LEVEL_HIGH; /* WL_HOST_WAKEOOB 中断 */ interrupt-names host-wake; }; };wifi1里的reg 1不能乱填SDIO 设备的 function 0 是 CIS 配置区function 1 才是 WLAN 数据面填 0 会直接 probe 失败。interrupts那行对应的是原理图上WL_HOST_WAKE接的那根 GPIO中断类型按模块手册一般配成电平触发这一项留空驱动也能起来但会退化成轮询模式功耗和延迟都难看。keep-power-in-suspend与cap-power-off-card要配合wifi_pwrseq一起看只写其中一个休眠唤醒后wlan0消失是大概率事件。蓝牙侧现在主流写法是 serdev把蓝牙节点挂在 UART 控制器底下内核启动时自动绑定不再依赖用户态hciattachuart3 { status okay; pinctrl-names default; pinctrl-0 uart3_pins; /* 记得把 CTS/RTS 也放进 pinctrl */ bluetooth { compatible brcm,bcm43xx-bt; /* 以资料包 dts 里的实际兼容串为准 */ shutdown-gpios gpio1 13 GPIO_ACTIVE_HIGH; /* BT_REG_ON */ device-wakeup-gpios gpio1 14 GPIO_ACTIVE_HIGH; host-wakeup-gpios gpio1 15 GPIO_ACTIVE_HIGH; max-speed 1500000; /* 补丁打完切高速率 */ clocks rtc 1; clock-names lpo; vbat-supply vcc_wifi; vddio-supply vcc_io; }; };几个关键属性shutdown-gpios极性写反的直接后果是蓝牙永远上不了电日志里连hci0都不会出现max-speed是打完补丁后切换的目标波特率写 1500000 之前先确认你的 UART 控制器分频能整除device-wakeup-gpios和host-wakeup-gpios接反不会立刻报错但会出现“能配对、一传数据就断”的怪现象。3.3 固件与 NVRAM文件名是拼出来的不是随手起的brcmfmac的固件名不是硬编码常量而是根据芯片识别结果和总线类型拼接出来的brcm/ 驱动前缀 芯片 ID -sdio 后缀。如果你在设备树里加了板级标识NVRAM 的文件名还会再带上板型后缀。所以“固件明明放在目录里却加载不到”这个问题正确做法不是反复改名字试而是去看dmesg里驱动自己打印的完整路径。# 芯片 ID 从 dmesg 里的 chip BCM43430/1 认出来不要靠猜 CHIP43430 BOARDap6212 sudo install -d /lib/firmware/brcm sudo install -m 644 firmware/brcmfmac${CHIP}-sdio.bin /lib/firmware/brcm/ sudo install -m 644 firmware/brcmfmac${CHIP}-sdio.txt /lib/firmware/brcm/ # NVRAM板级参数 sudo install -m 644 firmware/brcmfmac${CHIP}-sdio.clm_blob /lib/firmware/brcm/ # 法规表缺省也能起 sudo install -m 644 firmware/BCM${CHIP}A1.hcd /lib/firmware/brcm/ # 蓝牙补丁 # 想给不同板子用不同 NVRAM就再做一份软链名字跟 dts 里的 board-type 对齐 sudo ln -sf brcmfmac${CHIP}-sdio.txt /lib/firmware/brcm/brcmfmac${CHIP}-sdio.${BOARD}.txt sync sudo modprobe brcmfmac sudo modprobe hci_uart文件作用缺失后果*-sdio.bin芯片固件本体直接 probe 失败wlan0不出现*-sdio.txtNVRAMMAC、板型、PA 参数、晶振频率能起但 MAC 异常、发射功率不对、吞吐差*-sdio.clm_blob各国法规信道表信道列表残缺某些 5G 或高信道不可用*.hcd蓝牙补丁文件hci0能建出来但功能残缺或直接超时.txt里的macaddr那一行决定量产时是否每台机器 MAC 相同。研发阶段随手复制一份 NVRAM量产后发现整批设备 MAC 冲突这个坑我见过不止一次血泪经验是样机阶段就用脚本把 MAC 写进板级分区开机时再拼进 NVRAM。3.4 上电验证三连dmesg、iw、hciconfig烧完固件重启按顺序看三个地方能快速定位卡在哪一层。# 第一层总线与驱动是否认到设备 dmesg | grep -iE mmc1|brcmfmac|hci0|Bluetooth # 第二层网络接口是否建出来 ip -br link iw dev # 第三层蓝牙控制器状态 sudo hciconfig -a rfkill list健康的输出大致是这个节奏先mmc1: new high speed SDIO card at address 0001接着brcmf_fw_alloc_request: using brcm/brcmfmac43430-sdio for chip ...然后brcmf_c_preinit_dcmds打印固件版本最后ieee80211 phy0建出wlan0蓝牙侧是Bluetooth: hci0: BCM: chip id xx加一行补丁加载完成。任何一步断了就去对应章节找原因不要在后面的步骤里打转。网络侧接上去的验证用wpa_supplicant热点的验证用hostapd# STA 模式 wpa_passphrase MySSID MyPassphrase /etc/wpa_supplicant.conf wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf udhcpc -i wlan0 # AP 模式hostapd.conf 关键几行 # interfacewlan0 # drivernl80211 # hw_modeg # channel6 # wmm_enabled1 hostapd -B /etc/hostapd.confdriver一定写nl80211写老的wext会直接失败hw_modeg配channel6是最保守的组合先在 2.4G 单信道跑通再去动带宽和信道绑定。4. 核心代码拆解brcmfmac 探测链路上的四段关键逻辑日志看懂了接下来要能在源码里找到对应位置。下面这几段是去掉错误分支和锁之后的骨架保留主干方便对照内核树阅读。4.1 probe从 SDIO function 到 bus 的挂接SDIO 设备枚举成功后mmc 子系统会回调驱动的 probe。这一步做的事情比想象中多申请私有结构、使能 function、协商块大小、注册带外中断最后把控制权交给公共层。/* 骨架brcmf_sdio_probe对应 drivers/net/wireless/.../brcmfmac/sdio.c */ static int brcmf_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; int ret; /* 1. 私有数据挂在 func-dev 上后面中断线程、固件下载都从这里取上下文 */ sdiodev devm_kzalloc(func-dev, sizeof(*sdiodev), GFP_KERNEL); sdiodev-func1 func; sdiodev-dev func-dev; /* 2. 使能 function 并协商块大小块大小谈不拢会导致后续下载固件极慢甚至超时 */ sdio_claim_host(func); ret sdio_enable_func(func); sdio_release_host(func); if (ret) return ret; /* 3. 注册 OOB 中断数据到达靠 WL_HOST_WAKE 那根 GPIO不是 SDIO 中断 */ ret brcmf_sdiod_intr_register(sdiodev); if (ret) goto err_disable; /* 4. 交给公共层识别芯片、请求固件、下载、注册 802.11 接口都在里面 */ ret brcmf_sdio_probe_attach(sdiodev); if (ret) goto err_intr; return 0; }读这段要抓住一点probe 返回成功不等于 Wi-Fi 可用。固件下载是异步的brcmf_sdio_probe_attach只是把请求发出去真正建出wlan0是在固件下载完成的回调里。所以你在dmesg里看到brcmf_sdio_probe成功却始终没有phy0问题一定在固件或 NVRAM 上而不是 SDIO 时序。err_disable、err_intr这些错误分支在实际代码里很完整华为、瑞芯微这类厂商的 BSP 往往会加自己的补丁比如失败重试、复位后重新枚举读的时候注意区分上游代码和厂商改动。4.2 芯片识别与固件请求名字是怎么拼出来的公共层先读芯片 ID 和版本再按总线类型拼固件名。这个拼法决定了你该把文件放成什么名字。/* 骨架brcmf_fw_alloc_request 的调用点位于 bus.c / sdio.c */ ret brcmf_fw_alloc_request(bus-chip, bus-chiprev, brcmf_firmware_sdio, /* { brcmfmac, -sdio } */ BRCMF_FW_REQF_OPTIONAL, /* clm_blob 允许缺省 */ fwreq); if (ret) return ret; /* 请求的每一项bin 必须成功txt 与 clm_blob 走可选分支 */ for (i 0; i fwreq-num; i) { brcmf_dbg(INFO, requesting fw %s\n, fwreq-items[i].path); request_firmware_nowait(..., fwreq-items[i].path, ...); }参数含义第一个参数是芯片 ID第二个是芯片版本号两个合起来决定文件名里的数字部分第三个参数是总线前缀表SDIO 传的就是带-sdio的那一份第四个标志位允许.clm_blob缺失时不中断流程因为不是所有模组都带法规表。fwreq-items[i].path就是完整的固件路径前面 3.3 节说的“照dmesg打印的名字对齐”指的就是它。如果设备树里写了板型标识路径里会再插入一段板型后缀驱动先找带后缀的找不到再退回通用名。这个回退机制很实用同一份镜像可以带多个板级的 NVRAM靠设备树区分。4.3 下载与合并NVRAM 是怎么塞进固件的固件下载完成后驱动会把.txt里的键值对解析出来一部分喂给固件通过固件下载命令的附加数据段一部分留在驱动侧比如 MAC 地址、板型。这一步出错的表现很隐蔽网卡能起、能扫描但连不上或者一连就断。/* 骨架NVRAM 处理核心是 brcmf_fw_nvram_strip */ static int brcmf_fw_nvram_strip(const struct firmware *nv, struct brcmf_fw_request *fwreq) { /* 逐行解析 keyvalue注释行以 # 开头 */ while (parse_next_line(p, key, value)) { if (!strcmp(key, macaddr)) { /* MAC 单独拎出来全 0 或非法值会被替换成随机地址 */ brcmf_fw_nvram_set_mac(fwreq, value); continue; } /* 其余键值按 keyvalue\0 形式追加到下载缓冲区 */ append_to_download_buffer(fwreq, key, value); } return 0; }两个要点NVRAM 里的注释和空行必须被正确跳过很多手工改坏的.txt就是在这里崩的macaddr被单独拦截意味着你改这个键不需要重新生成固件镜像但必须重启生效。如果 NVRAM 里没有合法 MAC驱动会生成随机地址同一台机器每次重启 MAC 都变路由器的 DHCP 租约表会被刷满。4.4 蓝牙侧补丁下发与 serdev 附着蓝牙的初始化链比 Wi-Fi 短但更依赖时序。内核侧的hci_bcm负责供电和 GPIObtbcm负责下补丁、写地址、切波特率。/* 流程草图net/bluetooth/btbcm.c 的初始化序列 */ static int btbcm_setup(struct hci_dev *hdev) { /* 1. 复位控制器读本地版本号判断要不要下补丁 */ btbcm_reset(hdev); /* 2. 读私有版本信息拿到芯片 ID 与 ROM 版本 */ btbcm_read_verbose_config(hdev); /* 3. 按芯片 ID 找 /lib/firmware/brcm/BCMxxxx.hcd分片下发 */ btbcm_setup_patchram(hdev); /* 4. 写 BD_ADDR把全 0 的出厂地址换掉否则配对信息存不住 */ btbcm_set_bdaddr(hdev, bdaddr); /* 5. 切到设备树里 max-speed 指定的波特率 */ btbcm_set_baudrate(hdev, 1500000); return 0; }注意第 4 步和第 5 步的顺序。写地址必须在切波特率之前切完之后部分固件版本会拒绝私有命令。另外这套流程只在 serdev 自动绑定时才会走如果你还在用用户态hciattachmax-speed是在命令行里传的两套机制抢同一个 UART 会互相踢掉对方的hci0日志里表现为hci0反复创建又销毁。5. 避坑与排查五类高频故障的现象、原因与处置下面五条是移植这块模组时最常撞上的按“现象 → 原因 → 解决”记遇到直接对号入座。5.1 卡在 SDIO 枚举mmc1: error -110 whilst initialising SDIO card现象dmesg里只有mmc1: error -110后面再没有new high speed SDIO card这一行brcmfmac完全没有日志。原因有三类模组根本没上电WL_REG_ON没人拉、regulator 没使能、复位时序不对拉高使能后立刻访问 SDIO模块内部还没准备好、或者vqmmc电压与 IO 电平不匹配。解决先量电压确认VBAT、VDDIO、WL_REG_ON三个点位再检查设备树是不是用mmc-pwrseq-simple管的复位post-power-on-delay-ms给到 200ms 以上最后降max-frequency到 25000000 排除走线问题。-110是超时-84是 CRC/位错误后者基本可以确定是信号完整性问题跟驱动无关。5.2 固件加载失败Direct firmware load ... failed with error -2现象驱动打印了brcmf_fw_alloc_request: using brcm/brcmfmac43430-sdio for chip ...紧接着就是failed with error -2wlan0不出现。原因文件不在/lib/firmware/brcm/下、文件名与驱动拼出来的不一致、文件权限不对或者根文件系统用了 squashfs 导致固件目录是只读的软链。解决把dmesg里那行完整路径原样复制出来ls -l确认文件存在且可读注意有的发行版固件目录是/lib/firmware/而不是/lib/firmware/brcm/驱动会尝试两个位置但只认其中一个如果用了 initramfs要确认固件也打进去了否则网络模块在切换根文件系统前就已经加载失败。5.3 能扫描能连上但一传数据就断现象iw dev wlan0 scan正常wpa_supplicant也能拿到 IP但跑 iperf 或者下载几分钟就掉线dmesg里出现brcmf_sdio_readframes: SDIO read failed。原因SDIO 时钟过高、4 位数据线走线过长或没有等长、cap-power-off-card与供电能力不匹配或者电源在瞬时电流下掉压。解决先把max-frequency降到 25000000 观察是否还掉掉线消失说明是信号或时序问题要从 PCB 和供电上解决再看 dmesg本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →