
简介移远EC20 4G模块驱动源码包面向Android/Linux嵌入式开发者解决EC20模块在系统层面的识别、联网与语音通道复用问题适配LTE/UMTS/GSM多模网络。包内共59个文件、约43.63MB25个PDF涵盖硬件规格与软件设计规范11个C与8个H构成USB、GobiNet、CMUX接口的核心驱动另有zip/rar工程包、makefile编译脚本和txt说明文档便于直接移植与交叉编译。已有1520人学习下载。源码演示了从设备枚举、中断处理到数据通道建立的完整流程并给出Linux内核模块加载、Android HAL层集成及dmesg/adb logcat日志定位技巧。配套API定义和配置指南能帮助开发者快速上手在不同嵌入式平台上实现稳定高效的4G通信能力。1. 先从驱动框架说起EC20在Linux里到底驱动谁拿到移远EC20模块很多人的第一反应是去下载一个官方提供的驱动源码觉得装上这个驱动就能用了。这个思路不能说错但容易把你带进坑里。EC20本质上是一个USB接口的4G通信模块它的主控芯片遵循的是Qualcomm的MDM9x07方案数据通道走的是USB所以真正在Linux内核里干活的其实是内核自带的 USB 网络驱动框架而不是某一个单独属于移远的神秘驱动模块。先理清EC20在系统里长什么样。插上USB线后它会被枚举成一个USB复合设备里面同时暴露了好几类接口常见的有QMI/ECM/RNDIS 网络接口用来跑数据业务AT命令通道通常表现为 /dev/ttyUSB2 或 /dev/ttyUSB3诊断口、GPS口分别对应 /dev/ttyUSB0、/dev/ttyUSB1 这类节点还原/升级用的QDL端口这个只有在模块进入下载模式时才会出现这里有一个关键选择点EC20支持多种数据通道模式QMI模式、ECM模式、RNDIS模式、甚至老式的PPP拨号模式。不同的模式对应着内核里不同的驱动。现在的主流方案是QMI因为它的吞吐量、稳定性以及对多PDNPDP上下文的支持都明显优于老式PPP。对应的内核驱动就是qmi_wwan它属于内核标准驱动并不需要你去改什么源码只需要在内核配置里把它打开。那移远EC20的4G模块驱动程序源码这个主题下我们到底需要研究什么我认为核心有三件事理解内核里qmi_wwan、option等驱动与EC20的绑定关系搞清楚VID/PID是怎么匹配的学会源码级别的调试手段当设备节点不出现、数据通道起不来时如何在内核日志里定位问题掌握把驱动编译进内核或编译成模块并根据实际硬件平台做裁剪和适配的能力换句话说这份源码更像是一张地图你要学会看、学会走而不是把它背下来。顺便说一个很多新手容易误判的点EC20在出厂时默认的VID/PID可能是2c7c:0125但如果你之前用QTool工具切过模块的工作模式或者烧过不同的固件VID/PID可能会变化变成2c7c:0121之类。也就是说驱动能不能匹配成功和你手上模块的固件版本强相关排查问题时要先确认这一项。2. 源码阅读主线从USB枚举到Modem注册的完整流程假设你决定从内核源码角度去追踪EC20从插入到可用的完整流程我建议你按下述主线去读这条线是我自己梳理过的比从头平铺直叙看代码效率高很多。2.1 入口usb_device_id匹配表内核里几乎每一个USB驱动都会声明一张我支持哪些设备的匹配表。qmi_wwan.c里的匹配表长这样static const struct usb_device_id products[] { { USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x0125, USB_CLASS_VENDOR_SPEC, 0xff, 0xff), .driver_info (unsigned long)qmi_wwan_info, }, ... };可以看到0x2c7c就是移远的厂商ID。当你把EC20插入USB口后内核的USB core会遍历所有驱动把设备的VID/PID、接口信息跟这张表做匹配。匹配上之后就会调用.probe回调函数。建议你在这个文件里搜一下自己手头模块的PID如果找不到那么大概率是两种原因驱动版本太老或者模块固件改动过接口描述。找不到就自己按格式补一条记录重新编译这就是最常见的改源码场景。2.2 数据通道QMI接口与qmi_wwan_probeqmi_wwan_probe做的事情比较清晰读取接口描述判断当前接口是不是QMI控制口为这个接口分配一个struct net_device名字一般是wwan0注册一个cdc-wdm设备用于承载QMI控制信令对应/dev/cdc-wdm0注册netdev_ops里面实现了ndo_open、ndo_stop、ndo_start_xmit等底层数据收发函数这里有个概念要捋一下QMI协议的数据分两路控制消息走cdc-wdm字符设备IP数据包走wwan0网络设备。你在应用层可以不用关心cdc-wdm0因为像libqmi、quectel-CM这些工具会自己打开它但如果你的应用需要直接下发自定义QMI请求那就得了解cdc-wdm的读写格式。2.3 数据通道qmi_wwan如何把IP包塞进USB在qmi_wwan驱动里真正干脏活累活的是qmi_wwan_netdev_ops。它做的事情是static netdev_tx_t qmi_wwan_ndo_start_xmit(struct sk_buff *skb, struct net_device *dev) { // 取出控制结构体 struct usbnet *dev netdev_priv(net); // 可选的QMAP多路复用封装 if (qmap) { // 加4字节QMAP头支持多PDN } // 交给usbnet框架的tx流程 return usbnet_start_xmit(skb, net); }这里有一个重要概念叫QMAPQualcomm MSM/Modem Multiplexing Protocol。通俗讲它允许在一条USB物理链路上同时跑多路IP数据流每一路用不同的流ID区分。如果你的业务需要同时走多个APN比如一个走公网、一个走专网QMAP就非常有用。启用方式是在驱动模块加载参数里带上qmap或者用Quectel提供的工具去配置而不是改源码。2.4 控制通道qmi_wwan与cdc-wdm在qmi_wwan.c源码里你会看到它调用usbnet_cdc_bind、alloc_netdev但它本身并不直接处理QMI控制消息的格式。控制消息是由drivers/net/usb/qmi_wwan.c通过注册一个wdm子设备来间接处理的/* 注册一个 cdc-wdm 设备 */ rv usbnet_probe(intf, id); if (rv 0) return rv; ... usb_driver_set_configuration(dev, 1);具体的控制通路实际上会落到drivers/usb/class/cdc-wdm.c这个文件中去处理。读到这里的人很容易懵明明我查的是qmi_wwan怎么又冒出cdc-wdm其实好记qmi_wwan负责网络数据包cdc-wdm负责控制信令两者相互配合这就是复合设备的典型分工。2.5 老式PPP通道option驱动如果模块配置成使用老式PPP拨号例如在EC20上跑pppd call gprs那么它需要的就不是qmi_wwan而是drivers/usb/serial/option.c。这个驱动把USB串口转换成多个ttyUSB*节点拨号时pppd会通过这些串口节点发送AT命令并承载PPP帧。当前移远官方早就转向QMI了EC20也用不到PPP这种低效模式了。但你在源码里依然能看到这些历史遗留的匹配条目因为内核要保证兼容市面上还在用的老模块。3. 适配一台具体设备时必须打开的内核选项与设备树配置源码读得再熟最终还是要落到你的实际板子上。这里我直接给出一份基于内核5.10 LTS的适用配置建议并说明每一项目的是什么方便你自己做取舍。3.1 内核配置项的取舍清单配置项建议状态作用说明CONFIG_USB_NET_DRIVERSy/mUSB网络驱动总开关qmi_wwan依赖它CONFIG_USB_NET_QMI_WWANy/mQMI上网核心驱动EC20必须CONFIG_WWANy内核5.13以后需要旧内核可忽略CONFIG_USB_SERIAL_OPTIONy/mAT口和串口通道建议打开CONFIG_USB_SERIAL_QUALCOMMy/m部分固件下会用到这类绑定CONFIG_USB_ACMy/mCDC-ACM设备支持部分AT口走这个类CONFIG_USB_SERIAL_GENERICy通用串口驱动防止枚举失败建议你把QMI_WWAN和OPTION直接编译进内核而不是编成模块。原因很简单模块如果放在根文件系统里而根文件系统又必须经过网络才能挂载就会陷入鸡生蛋的困境。嵌入式设备调试时编进内核可以少掉一大半启动阶段的麻烦。3.2 设备树里的USB接口描述如果你用的是带USB Host控制器的ARM平台还需要确认设备树里USB Host节点状态正常并且有独立的供电引脚和复位引脚控制。EC20常用的一招是用GPIO控制模块的RESET和PWRKEY引脚。设备树里通常不直接描述EC20本身因为它是USB枚举设备但会定义USB口和GPIOusbotg1 { status okay; dr_mode host; /* 必须host模式 */ vbus-supply reg_ec20_power; /* 模块供电 */ }; pinctrl_ec20: ec20grp { fsl,pins /* RESET引脚 */ MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0x80000000 /* PWRKEY引脚 */ MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0x80000000 ; }; ec20_power: gpio-regulator { compatible regulator-fixed; regulator-name ec20_3v8; regulator-min-microvolt 3800000; regulator-max-microvolt 3800000; gpio gpio1 3 GPIO_ACTIVE_HIGH; enable-active-high; };这里有个容易踩的坑EC20的典型工作电压是3.3V到3.8V部分核心板在启动瞬间会产生较大的电流跌落。如果供电设计不够稳最直接的表现是模块能枚举成功但一拨号就死掉或重启。别急着去改驱动源码先测量模块VCC引脚的纹波和压降很多驱动问题其实是硬件供电问题。3.3 如何给新PID添加源码头条记录有时候你的模块固件升级后PID变了内核源码里没有对应记录。改源码的方式就是在qmi_wwan.c的products[]表里追加一条{ USB_DEVICE_AND_INTERFACE_INFO(0x2c7c, 0x1255, USB_CLASS_VENDOR_SPEC, 0xff, 0xff), .driver_info (unsigned long)qmi_wwan_info, },在option.c里也要记得加{ USB_DEVICE(0x2c7c, 0x1255) },这两处都改好驱动重新编译新的接口才会被正确识别。别只改一个地方那种AT口能出网络口不出的怪问题往往就是只改了option表、漏了qmi_wwan表导致的。4. 编译、加载与常见排错设备节点不出现的根因定位这一节是实战环节也是出问题最多的地方。我按排查顺序来写方便你复现。4.1 第一步先确认USB枚举是否成功插上模块执行lsusb如果看到ID 2c7c:0125 Quectel Wireless Solutions Co., Ltd. EC20说明USB枚举正常。这一步看不到设备后面的驱动工作都无从谈起。如果设备出现但VID/PID不正常比如只有一个原始的Qualcomm下载口05c6:9008说明模块被误触发了下载模式。解决办法是用QFlash工具重新刷正常固件或者在启动阶段把模块的BOOT_MODE引脚拉回正常工作电平。4.2 第二步查看内核日志执行dmesg | grep -i qmi dmesg | grep -i option dmesg | grep -i wwan dmesg | tail -n 50正常日志里应该能看到类似usb 1-1: new high-speed USB device number 2 using ci_hdrc usb 1-1: New USB device found, idVendor2c7c, idProduct0125 qmi_wwan 1-1:1.4: cdc-wdm0: USB WDM device qmi_wwan 1-1:1.4 wwan0: register qmi_wwan at usb-ci_hdrc.0-1, Qualcomm WWAN modem如果日志只到 New USB device found 就断了后面没有qmi_wwan相关行那么基本可以断定驱动没匹配上。这时再回头看内核配置里CONFIG_USB_NET_QMI_WWAN是否打开以及PID是否在匹配表中。4.3 第三步对比 /dev/ 节点出现的规律正常稳定状态下EC20在/dev下会有一组设备节点/dev/ttyUSB0 - 诊断口(DIAG) /dev/ttyUSB1 - GPS 口 /dev/ttyUSB2 - AT 口 /dev/ttyUSB3 - AT/PPP 口 /dev/cdc-wdm0 - QMI控制口但这里有个时序问题你心里要有数模块开机后需要用PWRKEY引脚触发上电或者通过AT指令重启。如果你的板子上电后直接给模块供了电模块自己就会开机节点出现的时机可能在系统起来之后几百毫秒到几秒不等这取决于固件。做开机脚本时千万别在系统起来后立即去操作这些节点加一个等待和探测机制更稳妥。4.4 第四步确认工作模式是否处于QMI数据模式如果AT口是正常的你可以用串口工具发AT指令确认模块当前工作模式ATQCFGusbnet返回0表示ECM模式返回2表示QMI模式。如果你想用QMI上网需要返回2。切换方法ATQCFGusbnet,2 ATCFUN1,1 # 重启射频栈改完后模块需要重启重启后PID或接口描述可能会变化重新dmesg确认一次。4.5 第五步分析驱动源码匹配表到这一步依然没出来的话就回到了上一节的操作对照源码里的usb_device_id手动把当前接口信息补进去。怎么获取接口信息cat /sys/bus/usb/devices/1-1/1-1:1.4/bInterfaceClass cat /sys/bus/usb/devices/1-1/1-1:1.4/bInterfaceSubClass cat /sys/bus/usb/devices/1-1/1-1:1.4/bInterfaceProtocol这三个值决定驱动匹配走的是哪个判断分支。QMI接口的信息通常是ff/ff/ffAT串口走的是02/02/01。如果签名信息不对也会匹配不上。再往下就是更细的调试手段了比如挂上USB总线trace、打开内核CONFIG_USB_DEBUG、用usbmon抓包。不过一般情况下前面四步已经能覆盖九成以上的问题场景。5. 拨号不止发ATD协议栈、拨号工具以及保活机制驱动层通了模块枚举出来了wwan0也出现了但这仅仅代表路修好了真正要让网通还需要在协议栈层面拨号。EC20的典型拨号方式有三种我按推荐顺序列出来。5.1 方式一使用官方quectel-CM工具最推荐quectel-CM是移远官方开源的一个连接管理工具专门处理QMI拨号、IP地址获取、DNS配置、数据模板设置等动作免去你直接裸调QMI协议。编译方式简单git clone https://github.com/QuectelOpenSourcelib/quectel-CM cd quectel-CM make ./quectel-CM -s cmet.3gpp # cmet.3gpp替换成实际APN工具运行后正常情况下wwan0就会拿到IP地址ip addr show wwan0它做的事情总结起来就几步打开/dev/cdc-wdm0发送QMI请求设置IP模式和数据模板再向模块发起拨号最后配置本地IP、路由和DNS。细节上它会自己处理qmi接口上的WDSWireless Data ServiceSet Up Packet不需要你再额外跑DHCP。5.2 方式二使用ModemManagerNetworkManager适合桌面或通用LinuxModemManager 是Linux系统级调制解调器管理框架它对QMI的支持也很好。在Yocto或Debian系系统里开启ModemManager后它能自动识别EC20并以移动宽带连接的形式出现在NetworkManager里。好处是标准化坏处是调试不容易出问题时你看到的只是上层连接失败不知道底层的真正原因。5.3 方式三AT指令 PPP老方法不推荐pppd call ec20-ppp这种方式在EC20上性能差、稳定性差且配置繁琐。尤其是EC20对PPP模式的支持现在已经很弱了很多人配了半天出现 LCP: timeout sending Config-Requests 就直接放弃。所以不用再走这条老路。5.4 拨号链路的保活与断线重连实网环境下模块偶尔会掉线原因可能是信号不稳、运营商侧释放资源、网络长时间无流量导致空闲断开。我建议你在应用层做这样几件事定期监测wwan0是否仍有IP地址周期性 ping 一个公网地址判断链路是否真实可用检测到链路异常时先发AT指令查询ATQENGservingcell看驻网状态如果模块没驻网执行ATCFUN1,1做射频重启如果驻网正常但还是不通把quectel-CM进程拉起来重新拨号另外提示一下如果你用的是带锁卡的物联网卡APN必须写对很多云平台物联网卡需要使用专属的APN这个可以在移远工具里用ATCGDCONT1,IP,apn_name预设也可以在quectel-CM启动参数里动态传入。6. 实测中遇到的坑与规避天线、供电、散热与USB信号完整性写到这里我要从源码和配置角度跳出来聊聊驱动真正跑起来之后容易踩的物理层坑。这些常见问题不是源码能解决的但往往比源码问题更致命。6.1 天线布局对驱动稳定性的影响我调试过一个设备现象很诡异模块能拨号成功但一放在金属外壳里几分钟内必然掉线。后来发现板载天线调试不到位驻波比偏高导致射频灵敏度下降。这个和驱动完全无关纯粹是天线问题。所以如果你发现驱动都正常了网络还是很差先怀疑天线和馈线别折腾源码。天线测试的简单方法在开阔无遮挡环境用AT指令ATCSQ看信号值数值范围0到31越小信号越差。如果贴近底板和离开底板时信号值差很多就去处理天线区域的地铜和净空吧。6.2 供电纹波模块重启类问题的最常见根因EC20在发射瞬间电流容易冲到2A以上如果供电链路内阻偏大或电容储备不足VCC上的电压会瞬间跌落超出模块要求的电压下限导致模块自动重启。重启后USB口重新枚举驱动重新匹配这时你会发现系统日志里多了一大段重新枚举的日志。如果这时你正好在连续拨号就会产生拨号拨一半就死了的假象。解决办法VBAT引脚附近至少布局几十微法的大电容确保从主供电到模块的走线足够粗、接触阻抗低有条件的情况下用独立的开关电源芯片给模块供电不要和其他大电流外设共用一个LDO。6.3 散热问题会折寿也影响驱动行为EC20在满载收发数据时发热明显如果整机散热不好模块内部温度保护启动后会强制降频甚至断网。表面看起来像是驱动挂了实际上回一条ATQTEMP就能看到温度值异常。量产设备一定要做好散热或者在业务层做温控逻辑在温度过高时主动降速。6.4 USB信号完整性的坑EC20的USB是高速USB 2.0信号完整性很敏感。USB D/D-走线要尽量短、等长、差分并且做好包地。如果你的板子USB走线过长或参考地不连续会出现一种很磨人的问题模块在低温或稍微振动后掉USB枚举重新插拔才能恢复。这种问题是原理图上看不出来的只有上示波器看USB信号和测试。6.5 量产测试脚本的参考做法如果你打算把这个驱动方案做成产品一定要设计一个量产测试流程避免每一片板子都要人工折腾。我自己的做法是上电等待5秒检查/dev/cdc-wdm0是否存在用AT指令检查模块固件版本和IMEI用quectel-CM拨号等待获取到IP连续ping 20次公网地址确认丢包率小于5%跑完之后杀掉连接记录日志这套流程里驱动相关的问题会集中在第1步暴露网络相关的问题集中在第3和第4步暴露哪一环报错就直接锁定排查方向。关于驱动源码层面的内容能写的实操细节基本都在上面了。EC20这个模块本身不算复杂真正难的是把USB枚举、内核配置、拨号流程、射频硬件几个环节串起来理解。我个人的体会是在嵌入式Linux里调这类4G模块大多数时间不是在改源码而是在做设备树配置、内核裁剪和硬件排查。源码读明白能帮你快速定位是不是驱动不匹配但最终问题能不能解决往往取决于你有没有能力把从USB口到射频天线的整条链路都盘一遍。本文还有配套的精品资源点击获取