最近做了一款 RK3288 工控板的项目客户要求必须用 Buildroot 做整个系统还要顺手把 4G 模块拨号上网的链路打通。这块板子上装的是移远 EC20 系列全网通模组跑的是 Buildroot 2019 左右的分支。折腾了大概三天把内核、设备树、rootfs、拨号工具整个链路都趟了一遍写篇文章记录一下给后面要搞 RK3288 Buildroot 4G 模块的朋友一个完整的参考路径。先说结论这事不难但琐碎。Buildroot 不像 Debian/Ubuntu 那种发行版很多东西不是装上包就能跑你得自己确认内核驱动、确认设备树、确认 rootfs 里有没有对应的工具最后还要选对拨号方式。整个过程可以拆成内核识别、用户态工具、拨号调优三段每一段都有各自的坑。下面我按实际推进的顺序来讲。1. 先搞清楚整条链路RK3288 Buildroot 4G模块各管哪一段拿到这个需求第一件事不是打开 Buildroot menuconfig 一通乱点而是先把 4G 模组联网的完整数据通路画清楚。只有知道系统里哪些环节是卡脖子的后面遇到问题才能快速定位。4G 模组在 Linux 系统里的工作方式大致是这样一条链路模组本身通过 USB少数通过 HSIC、SDIO接到 RK3288 的控制器上内核枚举出 ttyUSB 串口设备或者 qmi_wwan 网卡设备用户态的拨号工具通过串口发 AT 指令让模组完成 SIM 卡鉴权、APN 设置、LTE 网络附着附着成功后模组内部建立 PDN 连接数据通过 USB 网卡接口传输到 host 端host 端拿到 IP、DNS 之后路由表把流量导向这个 4G 接口系统就能上网了。听起来简单但每一环都有依赖关系。可以用一个表格先把任务拆开环节负责什么东西如果不做会怎样硬件连接模组供电、SIM卡槽、天线、USB信号线模组完全无法枚举或反复掉线内核USB驱动枚举出 ttyUSB* 或 qmi_wwan 网卡/dev 下面看不到设备内核网络驱动支持 PPP、RNDIS、QMI 等协议拨号工具无法建立网络接口设备树使能 USB Host 控制器、配置复位/供电 GPIOUSB 口没电模组不工作用户态工具pppd、quectel-CM、libqmi、udhcpc有设备但拨不上号系统脚本开机自动检测SIM、自动拨号、重启守护每次上电都要手动敲命令在 Buildroot 里做 4G 模块移植其实最花时间的不是编译而是把这张表里的每一行都验证一遍。RK3288 平台本身对 USB Host 的支持很成熟内核基本不用改什么但 Buildroot 默认的内核配置通常把很多驱动选项关掉了你需要自己确认打开。我当时用的做法是先不管 Buildroot直接用一串命令在开发板上确认硬件链路是否正常成功了再回去改 Buildroot 配置。这样可以避免编译了两小时结果发现是硬件没接对的尴尬。lsusb ls /dev/ttyUSB* dmesg | grep -i usb如果你的板子上lsusb都看不到模组的 VID/PID那后面所有软件配置都白搭先回头查硬件供电和 USB 走线。2. 让4G模组先被内核看见内核驱动裁剪与设备树调整这是移植过程中最核心的一步。Buildroot 开发环境里改内核不建议直接去改源码目录里的rk3288_defconfig更好的方式是用 Buildroot 提供的内核配置片段机制也就是BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILES。这个文件里只写你需要额外打开的选项编译时 Buildroot 会自动把片段合并到完整的内核配置里。2.1 内核必须打开的驱动选项不管你的模组是走 PPP 拨号还是 QMI 拨号下面这几个选项几乎是必须的CONFIG_USB_SERIALy CONFIG_USB_SERIAL_OPTIONy CONFIG_USB_SERIAL_GENERICy CONFIG_USB_NET_DRIVERSy CONFIG_USB_NET_QMI_WWANy CONFIG_USB_NET_CDC_NCMy CONFIG_USB_NET_CDC_ETHERy CONFIG_USB_NET_RNDIS_HOSTy CONFIG_PPPy CONFIG_PPP_ASYNCy CONFIG_PPP_SYNC_TTYy CONFIG_PPP_DEFLATEy CONFIG_PPP_BSDCOMPy CONFIG_PPPOEy这里解释一下为什么每个都要开。CONFIG_USB_SERIAL_OPTION是让模组被识别成多个 ttyUSB 串口设备的关键驱动。很多移远模组背后其实是一个 USB Modem 芯片它枚举出来会带好几个 TTY 接口分别对应 AT 命令口、Modem 口、日志口。如果没有这个驱动/dev/ttyUSB0这种设备就不会出现。CONFIG_USB_NET_QMI_WWAN是给走 QMI 方式上网的模组提供 wwan 网卡设备的驱动。如果你打算用当前比较推荐的 QMI 拨号方案这个必须打开。CONFIG_USB_NET_CDC_NCM和CONFIG_USB_NET_RNDIS_HOST是为了兼容模组厂商的 NDIS/RNDIS 模式有些模组的固件默认枚举成 RNDIS 设备如果内核不支持它就会变成一个未知设备。CONFIG_PPP相关的一堆选项是给传统 PPP 拨号用的。虽然现在已经不怎么推荐 PPP 了但打开它们成本很低万一后续要切换方案不用重新编译内核。在 Buildroot 里配置内核 fragment 的方法是在你的板级 defconfig 里加一行BR2_LINUX_KERNEL_CONFIG_FRAGMENT_FILESboard/yourboard/linux-4g.fragment然后把上面那些 CONFIG 写进board/yourboard/linux-4g.fragment文件。注意fragment 文件里不要写完整的内核配置只写增量部分Buildroot 会在 Kconfig 合并的时候自动帮你处理依赖。2.2 设备树把 USB 口和电源管理搞定RK3288 的板子4G 模组最常见的接法是通过 USB Host 口直接连接或者通过板载的 USB Hub 转接。这种情况下设备树需要改动的不多主要确认对应 USB 控制器的状态是okay并且 PHY 配置正确。比如板子上模组接的是usb_host0那设备树里至少要保证usb_host0 { status okay; };如果你的模组有单独的供电使能脚或者复位脚通常也需要在设备树里定义 pinctrl 和对应的 GPIO 控制节点。这里有个最常见的坑很多人以为只要 USB 枚举就行结果系统启动后模组根本没有被供电或者处于复位状态lsusb永远看不到设备。对于复位脚比较稳妥的做法是在内核起来之后通过 GPIO 手动拉高拉低确认模组能正常枚举再把这个时序固化到设备树或者启动脚本里。例如模组的复位脚接到 GPIO3_C7你可以先手动验证echo 183 /sys/class/gpio/export echo out /sys/class/gpio/gpio183/direction echo 1 /sys/class/gpio/gpio183/value sleep 1 echo 0 /sys/class/gpio/gpio183/value sleep 2 echo 1 /sys/class/gpio/gpio183/value lsusbGPIO 编号的计算方法是芯片的 GPIO 基址加上引脚偏移具体要看 RK3288 的 datasheet。这种先用 shell 验证再固化到配置里的顺序能帮你把硬件和软件的变量拆开不至于怀疑是驱动问题结果其实是 GPIO 没拉对。3. 把拨号工具链塞进Buildroot的rootfs内核把设备枚举出来只是第一步接下来还需要在 rootfs 里面备齐拨号工具。Buildroot 与普通发行版的最大区别在于它默认为了裁剪尺寸很多工具和运行库都不包括你必须明确告诉它要什么东西。3.1 在 menuconfig 里勾选哪些包我在这次移植中主要选择了这几个包p p p传统拨号协议栈可以作为 QMI 方式不可用时的保底方案。libqmiQMI 协议的用户态库quectel-CM 工具依赖它。usbutils提供lsusb命令调试 USB 枚举必备。picocom或microcom串口调试工具用来在开发板上直接给模组发 AT 指令。kmod内核模块管理工具如果你把某些驱动编译成模块启动后需要 modprobe 加载。dhcpcd或 busybox 自带的udhcpc用于 QMI 拨号成功后给网卡获取 IP 地址。在 Buildroot 配置界面里对应的是BR2_PACKAGE_PPPy BR2_PACKAGE_LIBQMIy BR2_PACKAGE_USBUTILSy BR2_PACKAGE_PICOMy BR2_PACKAGE_KMODy BR2_PACKAGE_DHCPCDy如果你的 rootfs 空间比较紧张dhcpcd也可以不选直接用 busybox 里的udhcpc就行但要注意 busybox 里默认的 udhcpc 脚本路径是否正确。我见过很多次QMI 网卡有 IP 了但没 DNS的情况最后发现是 udhcpc 脚本里没配置 resolv.conf 的更新权限。3.2 quectel-CM 的集成方式移远模组官方提供的拨号工具是quectel-CM它做的事情比较黑盒但确实非常省事。它会自动遍历 ttyUSB 口寻找 AT 命令口自动设置 APN然后通过 QMI 协议拨号最终在系统里生成一个网卡接口并配置上 IP。编译 quectel-CM 有两种常见路径。第一种是通过 Buildroot 的 external tree 机制把 quectel-CM 做成一个独立的 package。这种方式适合正式项目因为可以管理版本、复现构建。写一个quectel-cm.mk大概结构是这样QUECTEL_CM_VERSION v1.0 QUECTEL_CM_SITE $(call github,yourfork,quectel-CM,$(QUECTEL_CM_VERSION)) QUECTEL_CM_LICENSE MIT define QUECTEL_CM_BUILD_CMDS $(MAKE) -C $(D) CC$(TARGET_CC) endef define QUECTEL_CM_INSTALL_TARGET_CMDS $(INSTALL) -D -m 0755 $(D)/quectel-CM $(TARGET_DIR)/usr/bin/quectel-CM endef $(eval $(generic-package))第二种方式比较直接在 PC 上交叉编译好 quectel-CM 的静态二进制然后把可执行文件丢到 rootfs overlay 目录里。Buildroot 的BR2_ROOTFS_OVERLAY选项能把指定目录下的内容原样拷贝到 target 文件系统非常适合快速验证。BR2_ROOTFS_OVERLAYboard/yourboard/overlay然后在board/yourboard/overlay/usr/bin/里放上编译好的quectel-CM重新 make它就会自动出现在系统的/usr/bin/下。注意要用file quectel-CM确认它是 ARM 架构的可执行文件我见过有人把 x86 的静态程序拷贝进去跑起来才报 exec format error。3.3 rootfs overlay 里的拨号脚本除了工具本身通常还需要准备一个拨号启动脚本放在 overlay 的/etc/init.d/下或者更简单点放在/root/下手动执行。脚本内容大概是这样#!/bin/sh /usr/bin/quectel-CM -s cmnet 其中cmnet是你的 SIM 卡的 APN。如果是三大运营商的标准公网卡用cmnet、ctnet、wonet分别对应移动、电信、联通。如果是物联网卡APN 一般由卡商提供填错了会一直注册不上网络但 AT 指令查询又一切正常非常容易误判。4. PPP、QMI还是NDIS拨号方案怎么选以及我为什么用了quectel-CM在配置拨号工具前必须想清楚你的模组和实际业务适合哪种拨号方式。这三种方式我都在不同项目里用过各有优缺点选错了会直接影响上行吞吐量和稳定性。4.1 三种拨号方式对比方式网卡形态驱动依赖CPU占用吞吐量通用性PPPttyUSB 串口承载PPP option较高一般极广几乎所有模组都支持QMIqmi_wwan 虚拟网卡qmi_wwan libqmi较低较高高通平台模组支持好NDIS/RNDIS虚拟网卡rndis_host/cdc_ncm低取决于模组固件新模组常见但驱动差异大PPP 拨号最大的问题是所有 IP 数据都要经过串口 TTY 封装这个封装过程比较耗 CPU尤其是在 RK3288 这种老一点的 ARM 处理器上跑满 LTE 带宽比较吃力而且 PPP 链路的稳定性不如 QMI 直接承载。QMI 方式是模组固件直接通过 USB 传输 IP 数据开销小很多拨号状态查询也更方便。NDIS/RNDIS 方式相当于模组把自己模拟成一个有线网卡host 端用一个标准网卡驱动就能直接收发数据。这种方式对 host 最简单的但模组固件如果没做好容易遇到网卡频繁拔插、IP 分配异常的问题。4.2 为什么我最终选了 QMI quectel-CM原因有两个。第一这次用的模组是移远 EC20/EC200S 系列它们对 QMI 协议的支持非常成熟官方也把 quectel-CM 作为主推拨号工具社区用户多出问题容易查资料。第二QMI 方式下系统会出现一个真正的网卡接口比如 wwan0 或 usb0之后无论是 iperf3 测速、iptables 转发、还是给上层应用绑定接口都比 PPP 方式直观得多。比如你要在 RK3288 上做一个 4G 转 WiFi 的网关产品QMI 网卡可以直接桥接或 NAT不用额外处理 PPP 接口的 MTU、路由这类问题。quectel-CM 的用法非常简单/usr/bin/quectel-CM -s cmnet默认情况下它会自动寻找/dev/ttyUSB2作为 AT 口拨号成功后在系统里创建网卡接口通常叫ethX或者wwanX。你也可以在启动时加-v参数看详细日志这个日志在排查为什么一直拨不上号的时候特别有用。当然quectel-CM 不等于 QMI 的全部。如果你想更精细地控制 APN、鉴权方式、PDN 数量也可以直接用libqmi提供的qmi-network脚本配合udhcpcqmi-network /dev/cdc-wdm0 start udhcpc -i wwan0这两种方式本质一样quectel-CM 只是把这些步骤封装好了。对小规模量产的产品我更推荐 quectel-CM因为它的错误提示更友好比如 SIM 卡未准备好、APN 错误、网络拒绝接入都会直接打印出来现场调试方便很多。5. 从AT指令到第一个ping完整调试流程与踩坑记录内核驱动有了rootfs 工具也齐了接下来就是上电实测。这一步最能体现经验差异因为大部分问题不是出在有没有选对包而是出在怎么定位到底卡在哪一环。5.1 分阶段验证的调试链路我习惯把这套调试拆成五个阶段每个阶段都有明确的通过标准阶段一USB 枚举成功。通过标准是lsusb能看到模组的 VID:PID/dev/ttyUSB0到/dev/ttyUSB3都能看到。阶段二AT 指令通道正常。通过标准是往 ttyUSB 发AT能收到OK。阶段三SIM 卡和射频正常。通过标准是ATCPIN?返回READYATCSQ返回的信号值大于 10。阶段四拨号成功。通过标准是 quectel-CM 日志显示网络连接建立ifconfig能看到网卡且有 IP。阶段五应用层连通。通过标准是ping -I wwan0 114.114.114.114能通。不要跳阶段。我曾经因为贪快直接跑 quectel-CM结果报错一堆后来一步步查才发现是天线没接好信号值只有 0浪费了两个小时。5.2 阶段二的具体操作用 picocom 发AT指令板子上跑起来之后用串口工具连上模组的 AT 口picocom -b 115200 /dev/ttyUSB2注意很多移远模组的 AT 口默认是 ttyUSB2而不是 ttyUSB0因为 ttyUSB0 有时候是 GPS 口或者日志口。你可以逐个口试能回OK的就是 AT 口。连接后依次验证AT OK ATCPIN? CPIN: READY ATCSQ CSQ: 18,99 ATCREG? CREG: 0,1ATCSQ第一个数字是信号强度一般大于 10 才能稳定拨号。如果返回99,99说明没搜到网络优先检查天线和 SIM 卡。ATCREG?返回0,1表示已注册上 LTE 网络如果是0,0或0,3说明还在找网再等等或者检查 APN。提示picocom下按CtrlA然后按CtrlX可以退出。发 AT 指令时注意回显里可能有\r\n换行这是正常的。5.3 阶段四的常见故障QMI网卡没出现quectel-CM 执行之后如果卡在类似waiting for network的位置最常见的原因有三个。一是内核没有把模组的 QMI 接口正确绑定到 qmi_wwan 驱动。你可以查看/sys/bus/usb/devices/下面的设备目录确认模组的 USB 接口有没有被option驱动、qmi_wwan驱动分别接管。有时候需要手动解绑再重新绑定echo 2-1:1.0 /sys/bus/usb/drivers/option/unbind echo 2-1:1.0 /sys/bus/usb/drivers/qmi_wwan/bind二是模组的 APN 配置不对。用 AT 指令手动配置一次ATCGDCONT1,IP,cmnet ATQICSGP1,1,cmnet,,,1 ATD*99#如果这样能拨通说明问题出在 quectel-CM 启动脚本里的 APN 参数。三是 rootfs 缺少 netlink 相关能力。这个是 Buildroot 环境下特有的问题有些裁剪太狠的系统连 udev 都有缺失导致 qmi_wwan 网卡热插拔事件没有触发正常的网络配置。解决方法是在启动脚本里手动执行ip link set wwan0 up然后再跑 udhcpc。5.4 阶段五ping通之后的性能验证能 ping 通之后别着急收工。4G 模块移植最怕的就是能通但带宽跑不满客户验收时一测速就翻车。先用iperf3测一下上行和下行iperf3 -c 192.168.1.100 -i 1 -t 30 -R注意 4G 模组的吞吐量受信号强度影响特别大。同一个位置信号从 15 掉到 8吞吐可能直接腰斩。如果测速不达标先把模组天线位置调整到最佳再用ATCSQ看信号值。此外还要检查路由表和 DNS 配置ip route cat /etc/resolv.conf如果默认路由还是走 eth0 而不是 wwan0需要手动加默认路由ip route add default dev wwan0在 Buildroot 系统里/etc/resolv.conf可能会在重启后被覆盖。我的做法是在拨号脚本里每次拨号成功后重新写一次这个文件把运营商 DNS 填进去。5.5 绕不过去的供电问题最后必须强调一个纯硬件坑4G 模组的瞬态电流很大尤其在大功率发射的时候峰值电流能到 2A 左右。如果你的模组供电是通过 RK3288 板上的 LDO 或者小电流 DCDC 供电很容易出现系统开机时模组能枚举一拨号就重启或者掉线。排查方法很简单拨号的时候用dmesg观察 USB 设备有没有反复断开重连或者直接用万用表量模组供电引脚的电压跌落。如果是供电不足不要指望软件能解决换一个 3A 以上的 DCDC 电源轨给模组单独供电问题基本就消失了。6. 最后的小总结与经验沉淀这次 RK3288 Buildroot 添加 4G 模块的经历让我对嵌入式 Linux 下的模块移植有了一个比较系统的思路从硬件枚举到内核驱动再到用户态拨号工具再到应用层验证每一层都有独立的验证标准和坑点。很多人在这一步卡住通常是因为把4G模块移植理解成装个拨号工具其实真正的工程量分布在内核配置、设备树确认、rootfs 裁剪和拨号方式选型上。一点个人体会是做这种移植尽量使用增量验证法每完成一个阶段就固化一个验证标准。先用lsusb确认硬件再用手动 AT 指令确认模组状态再用 quectel-CM 确认拨号最后再考虑开机自启和稳定性。这样即使出了问题也能很快定位在哪一层而不是从头到尾瞎猜。最后再分享一个实用技巧Buildroot 项目里把所有内核 fragment、rootfs overlay、拨号脚本和文档都放到同一个板级目录board/yourboard/下然后用 git 管理。这样换人维护或者换板子复用的时候只需要看这一个目录就能知道整个 4G 移植涉及的所有改动比散落在源码各个角落要好维护得多。
阅读完成 · 觉得有帮助?