news 2026/5/10 21:30:34

[Android6.0][RK3399] EC20 4G模块 PCIe 接口驱动移植与 RIL 调试实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[Android6.0][RK3399] EC20 4G模块 PCIe 接口驱动移植与 RIL 调试实战

1. 项目背景与核心挑战:当RK3399遇上EC20

大家好,我是老张,在嵌入式这行摸爬滚打十几年了,从功能机时代玩基带芯片到现在折腾各种智能硬件,踩过的坑比走过的路还多。今天想和大家聊聊一个非常具体、但又让很多开发者头疼的问题:在RK3399这块性能强悍的平台上,为Android 6.0系统,把EC20 4G模块通过PCIe接口给跑起来,并且打通从驱动到RIL(无线接口层)的整个链路。

你可能要问,RK3399不是有原生USB接口吗,为啥要走PCIe?这其实是个很实际的工程选择。RK3399的PCIe接口带宽高、延迟低,对于需要稳定高速数据吞吐的4G模块来说,能提供比传统USB转接更可靠的物理连接。尤其是当你做的是工业平板、车载中控、智能零售终端这类对网络稳定性要求极高的设备时,PCIe的优势就体现出来了。EC20模块本身支持PCIe形态,直接对接能省去一个USB转换芯片,既节约成本又减少了一个潜在的故障点。

但这条路走起来并不轻松。Android 6.0虽然经典稳定,但其内核版本(比如常用的4.4)对较新的PCIe网卡类设备支持并不完善,特别是涉及到4G模块这种“上网卡”+“调制解调器”二合一的复杂设备。你需要面对的是一连串的挑战:内核需要正确的驱动来识别这个“新网卡”;系统需要配置好PPP拨号来建立数据连接;最后,Android特有的RIL框架必须正确对接,让上层APP(比如拨号、流量监控)能感知和控制4G网络。这就像给一个新房接水电,不仅要管道通(驱动),还要水龙头能出水(PPP),最后还得确保屋里的电器(APP)知道怎么用水(RIL)。我刚开始做的时候,也是被各种日志和错误码折腾得够呛,不过趟平了之后发现,只要思路清晰,一步步来,完全能搞定。

2. 硬件识别与驱动移植:让系统“看见”你的模块

万事开头难,第一步就是让Linux内核认识插在PCIe槽上的这位“新朋友”。EC20模块通过PCIe暴露给主机的,本质上是一个USB复合设备。所以,驱动移植的核心是USB网络驱动,而不是直接的PCIe驱动。PCIe在这里只是物理通道,内核的USB子系统会通过它来枚举设备。

2.1 确认硬件身份:Vendor ID与Product ID

动手改代码前,务必先确认你手里的EC20具体型号。EC20有多个变种,常见的是EC20-CEC20-CE,它们的USB识别码不同。接上模块后,立刻用adb shell连上设备,或者通过串口查看内核日志:

dmesg | grep -i "usb.*new"

或者更精确地:

cat /proc/kmsg | grep -E "idVendor|idProduct"

你期待看到类似这样的输出:

[ 123.456789] usb 1-1.1: New USB device found, idVendor=2c7c, idProduct=0125

这里,idVendor=2c7cidProduct=0125就对应EC20-CE。如果是05c69215,那就是EC20-C。这个信息是后续所有驱动配置的基石,千万不能搞错。我见过有兄弟折腾一星期,最后发现是模块型号和驱动代码对不上,白白浪费了时间。

2.2 内核驱动配置:三选一的策略

EC20作为高通方案的模块,在内核层面通常有三种驱动模式可选,它们决定了系统以何种方式与模块的“上网”功能交互:

  1. USB Serial (CDC ACM):这是最基础的模式。驱动会把模块虚拟成几个串口设备(/dev/ttyUSB0~ttyUSB4ttyACM0~ttyACM4)。每个串口有固定分工,比如ttyUSB2用于发AT命令,ttyUSB3用于PPP拨号。这种模式通用性强,但性能和管理功能较弱。
  2. GobiNet:这是高通提供的传统内核态驱动。移植后,它会创建一个网络接口(如eth1)和一个QMI控制通道设备(/dev/qcqmi0)。数据直接走网络接口,效率高;控制命令(如网络注册、信号查询)走QMI通道。但需要手动将供应商提供的GobiNet内核源码集成到你的内核树中并编译。
  3. QMI WWAN (cdc-wdm):这是目前更推荐、也更现代的方式。它利用Linux内核自带的cdc-wdmqmi_wwan驱动。你的工作主要是确保内核配置中启用了这些驱动,并在驱动中添加你模块的USB识别码。成功后,系统会创建wwan0这样的网络接口和/dev/cdc-wdm0控制节点。Android 6.0默认内核通常已包含此驱动,因此工作量最小。

我的选择与建议:对于RK3399 + Android 6.0这个组合,我强烈建议使用QMI WWAN方案。原因很简单:省事、稳定、社区支持好。你不需要维护额外内核代码,只需修改配置文件。具体操作是进入内核配置菜单:

make ARCH=arm64 menuconfig

确保以下选项被启用(标为*):

Device Drivers ---> Network device support ---> USB Network Adapters ---> <*> Multi-purpose USB Networking Framework <*> CDC Ethernet support (smart devices such as cable modems) <*> QMI WWAN driver for Qualcomm MSM based devices USB support ---> <*> USB Modem (CDC ACM) support

然后,找到drivers/net/usb/qmi_wwan.c文件,在qmi_wwan_pre_reset函数附近的设备列表里,添加你的EC20的USB PID/VID。例如,对于EC20-CE,你需要找到类似{QMI_GOBI_DEVICE(0x2c7c, 0x0125)}的宏,如果没有,就仿照格式添加一行。添加后重新编译内核并烧录。

2.3 驱动验证:检查设备节点

烧录新内核后,重启设备,再次查看日志,确认模块被正确识别。然后进入系统,检查设备节点是否生成:

ls -l /dev/ | grep -E "ttyUSB|cdc-wdm|wwan"

如果QMI WWAN驱动生效,你应该能看到/dev/cdc-wdm0/sys/class/net/wwan0。看到这些,恭喜你,驱动层的大门已经敲开了。如果没看到,别慌,回头仔细核对内核日志里关于qmi_wwan的加载和探测信息,最常见的问题就是VID/PID没添加对,或者内核配置选项没真正编译进去。

3. 网络连接建立:PPP拨号与APN配置

驱动让系统认识了模块,接下来就要让模块“上网”。对于4G模块,建立数据连接通常需要经过PPP(Point-to-Point Protocol)拨号。这个过程就像是给你的手机卡“激活”数据业务。

3.1 PPP拨号配置

在Android系统中,PPP拨号功能一般由rild(RIL守护进程)来管理,但底层依赖pppd(PPP守护进程)和chat(拨号脚本工具)这两个程序。你需要确保系统中有这些工具。通常,Android源码的external/ppp/目录下就有。在RK3399的SDK里,可能需要检查是否已编译。

关键的配置在于拨号脚本。这个脚本告诉pppd使用哪个串口、以什么AT命令序列去激活模块的数据承载。对于EC20,使用QMI WWAN模式时,通常不再使用传统的ttyUSB3进行PPP拨号,而是由QMI协议通过cdc-wdm0设备来管理数据连接的建立和释放,这更高效。但为了兼容性和理解流程,我们了解一下传统方式。

传统PPP脚本(如/etc/ppp/peers/quectel-chat)可能包含:

ABORT "BUSY" ABORT "ERROR" ABORT "NO ANSWER" TIMEOUT 30 "" "AT" OK "AT+CGDCONT=1,\"IP\",\"你的APN\"" OK "ATD*99#" CONNECT ""

这个脚本的意思是:发送AT命令,设置APN为“你的APN”,然后拨打*99#这个数据呼叫号码。但在QMI模式下,APN信息通常是通过RIL层下发的。

3.2 APN信息配置

APN(接入点名称)是移动网络用来识别你数据业务类型的“钥匙”。国内三大运营商的常用APN如下:

运营商4G APN名称说明
中国移动cmnet最常用的移动互联网接入点
中国联通3gnet联通4G/3G通用接入点
中国电信ctnet电信4G互联网接入点

这些信息需要正确配置。在Android系统中,APN信息通常以数据库形式存储。对于嵌入式设备,我们可以在系统编译时预置。找到你项目中的APN配置文件,路径可能类似device/rockchip/rk3399/overlay/frameworks/base/core/res/res/xml/apns_config.xml。你需要在其中添加正确的APN条目。例如,添加中国移动的:

<apn carrier="China Mobile Internet" mcc="460" mnc="00" apn="cmnet" type="default,supl" protocol="IPV4V6" roaming_protocol="IPV4V6"/>

mcc(国家码)和mnc(运营商网络码)需要根据你的SIM卡来设置。编译后,这个APN列表就会被集成到系统中。当模块注册到网络后,RIL会根据SIM卡信息自动选择匹配的APN进行数据连接。

4. RIL层集成:连接Android框架与硬件

驱动和网络通了,但Android上层应用还无法使用4G网络。这就需要RIL出场了。RIL是Android架构中承上启下的关键层,它把Java框架的电话服务请求(如“打开移动数据”、“获取信号强度”)翻译成底层模块能懂的AT命令或QMI消息,反之亦然。

4.1 获取与集成Vendor RIL

Android的RIL分为两部分:通用框架(hardware/ril/rild)和厂商实现(vendor ril)。高通或模块厂商会提供针对特定模块的vendor ril库源码。对于EC20,你需要从移远通信获取名为Quectel_Android_RIL_xxx的源码包。这里有个大坑:不同Android版本对应的RIL源码包不同!为Android 6.0准备的包,名字可能类似Quectel_Android_RIL_SR01A41V17。用错了版本,编译会出一堆语法错误,根本过不了。

拿到正确源码后,将其替换到Android源码树的hardware/ril/reference-ril/目录下。注意备份原来的文件。然后,需要修改编译配置,确保编译系统能把它编进去。通常需要检查hardware/ril/reference-ril/Android.mk文件,确保其中的LOCAL_MODULE名字(例如libreference-ril)是唯一的,不会和系统其他库冲突。

4.2 配置RIL守护进程(rild)

替换库文件后,要告诉系统使用我们这个新的库。这需要通过修改初始化脚本来实现。传统做法是修改init.rc文件,在其中定义ril-daemon服务。你需要在你的设备特定目录下找到init.rc,例如device/rockchip/rk3399/init.rc,添加或修改如下服务定义:

service ril-daemon /system/bin/rild -l /system/lib64/libreference-ril.so -- -d /dev/ttyUSB2 class main socket rild stream 660 root radio socket rild-debug stream 666 radio system user root group radio cache inet misc audio sdcard_rw log

这里有几个关键点:

  • -l /system/lib64/libreference-ril.so:指定我们编译出来的vendor ril库的路径。注意:RK3399是64位平台,库通常在/system/lib64/下。如果是32位系统,则在/system/lib/下。这里错了,RIL完全不起作用。
  • -d /dev/ttyUSB2:指定用于AT命令通信的串口设备节点。在QMI WWAN模式下,这个参数可能不是必须的,或者需要改为-d /dev/cdc-wdm0,具体取决于你的vendor ril实现。务必参考移远提供的文档。
  • usergroup:确保rild进程有足够的权限访问串口和网络设备。上面配置的radio组通常拥有这些权限。

4.3 解决编译冲突与路径覆盖

编译时你可能会遇到一个经典错误:

build/core/base_rules.mk:157: *** hardware/ril/reference-ril: MODULE.TARGET.EXECUTABLES.chat already defined by external/ppp/chat。

这是因为移远的RIL源码包里自带了一个chat工具,和Android源码external/ppp/下的chat重名了。最简单的解决办法是,在编译前,删除移远源码包里的chat目录,或者修改其Android.mk中的模块名。我通常直接rm -rf hardware/ril/reference-ril/chat,一劳永逸。

另一个RK平台特有的坑是路径覆盖。即便你在init.rc里配好了库路径,RK的构建系统可能会在device.mksystem.prop里再次覆盖。你需要检查以下文件:

  1. device/rockchip/rk3399/device.mk
  2. device/rockchip/rk3399/system.prop
  3. device/rockchip/common/device.mk

搜索rild.libpath这个属性。例如,你可能会发现有一行:

rild.libpath=/system/lib64/libril-rk29-dataonly.so

这行代码会用RK自带的RIL库覆盖你的设置。你需要果断地注释掉或删除这一行,让系统使用我们指定的库。

5. 调试与问题排查:从日志中寻找答案

集成工作完成后,最紧张的时刻就是上电测试。不出意外的话,这时候应该会出点“意外”。别担心,通过系统日志我们能定位大部分问题。

5.1 抓取专用日志

Android日志分多个缓冲区,RIL相关的日志主要在radio缓冲区。

adb logcat -b radio -v time

这个命令会实时输出所有RIL和调制解调器交互的日志,是调试的“第一现场”。另外,内核日志也至关重要:

adb shell dmesg | tail -100

或者实时监控:

adb shell cat /proc/kmsg

5.2 关键检查点与常见问题

当4G网络没有如预期般出现时,按照以下清单逐项排查,能帮你快速定位:

  1. RIL进程活着吗?

    adb shell getprop init.svc.ril-daemon

    输出必须是running。如果是stoppedrestarting,说明服务启动失败,去查logcatdmesg看崩溃原因。

  2. 系统加载了谁的RIL库?

    adb shell getprop gsm.version.ril-impl

    这是最重要的属性之一。如果输出是Quectel_Android_RIL_SR...,恭喜,你的库被加载了。如果输出是RIL_RK_DATA_V3.6...,说明被RK的库覆盖了,回去检查rild.libpath的覆盖问题。如果输出为空,可能是库路径错误,或者库文件本身与平台(32/64位)不兼容。

  3. SELinux在捣乱吗?Android 6.0的SELinux可能已经处于强制模式(Enforcing),这会阻止rild进程访问设备节点。

    adb shell getenforce

    如果返回Enforcing,可以先将其设为宽容模式测试:

    adb shell setenforce 0

    如果网络随后恢复正常,说明你需要为rild和你的设备节点(如/dev/cdc-wdm0/dev/ttyUSB*)添加正确的SELinux策略。这涉及到修改*.te策略文件,是另一个深水区,但为了产品化,必须解决。

  4. 模块注册上网络了吗?radio日志中搜索REGISTEREDATTACHED等关键词。也可以使用AT命令手动查询(如果AT串口可用):

    adb shell echo -e "AT+CREG?\r\n" > /dev/ttyUSB2 cat /dev/ttyUSB2

    查看返回码,确认网络注册状态。

  5. 数据连接建立了吗?查看radio日志中是否有SETUP_DATA_CALL的成功响应。或者使用ifconfig命令查看wwan0接口是否获得了IP地址。

    adb shell ifconfig wwan0

我印象最深的一次调试,是ril-daemon一直重启。查遍日志发现是权限问题,/dev/ttyUSB2的设备节点虽然存在,但rild进程所属的radio用户组没有读写权限。最后是在ueventd.rc文件中添加规则才解决的:

/dev/ttyUSB0 0660 radio radio /dev/ttyUSB1 0660 radio radio /dev/ttyUSB2 0660 radio radio /dev/ttyUSB3 0660 radio radio

这些规则确保了设备节点在创建时就被赋予正确的权限。嵌入式开发就是这样,很多时候问题不在核心逻辑,而在这些细微的配置和权限上。把这些问题都解决了,看着设备右上角出现4G信号图标,并且能够真正上网冲浪时,那种成就感,就是驱动我们这行不断钻研下去的动力。希望我的这些经验,能帮你少走些弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 12:18:06

视觉问答新体验:用OFA模型快速构建智能客服系统

视觉问答新体验&#xff1a;用OFA模型快速构建智能客服系统 1. 引言&#xff1a;让AI看懂图片并回答问题 想象一下这样的场景&#xff1a;用户在电商平台咨询商品信息&#xff0c;不需要人工客服&#xff0c;AI系统能直接"看懂"商品图片并回答用户的问题。"这…

作者头像 李华
网站建设 2026/4/22 3:57:42

STM32F37x的SDADC配置实战:从差分模式到单端模式的完整指南

STM32F37x的SDADC配置实战&#xff1a;从差分模式到单端模式的完整指南 最近在做一个高精度的工业传感器数据采集项目&#xff0c;板子核心用的是一颗STM32F37x系列的芯片。选型时&#xff0c;除了看中它的Cortex-M4内核和DSP能力&#xff0c;更吸引我的是其内置的SDADC。这个1…

作者头像 李华
网站建设 2026/4/22 0:10:17

VSCode Python环境下Shadow Sound Hunter开发配置指南

VSCode Python环境下Shadow & Sound Hunter开发配置指南 1. 环境准备与基础配置 在开始Shadow & Sound Hunter项目开发前&#xff0c;我们需要先搭建一个高效的Python开发环境。Visual Studio Code&#xff08;简称VSCode&#xff09;作为一款轻量级但功能强大的代码…

作者头像 李华
网站建设 2026/4/22 3:42:38

Fast-Planner(五)深度解析TopologyPRM:从算法原理到代码实现

1. 为什么需要TopologyPRM&#xff1f;从“局部极小值”这个坑说起 大家好&#xff0c;我是老张&#xff0c;在无人机路径规划这个领域摸爬滚打了十来年。今天咱们来啃一块硬骨头——Fast-Planner里的TopologyPRM算法。很多朋友在用Fast-Planner做无人机实时避障时&#xff0c;…

作者头像 李华
网站建设 2026/4/24 0:52:22

GLM-4-9B-Chat-1M长文本实战:上市公司年报ESG信息抽取与评分

GLM-4-9B-Chat-1M长文本实战&#xff1a;上市公司年报ESG信息抽取与评分 1. 项目背景与核心价值 在金融投资和企业分析领域&#xff0c;ESG&#xff08;环境、社会、治理&#xff09;已经成为不可忽视的重要指标。传统的ESG分析需要专业人员花费数小时甚至数天时间阅读上百页…

作者头像 李华