☰
RK3399 USB以太网驱动编译与多网口配置实战
2026/10/27 21:56:38 网站建设 项目流程

板子上能用的原生网口只有一个,串口占了调试通道,剩下的 USB 口还空着——这是我接手这个 RK3399 项目时看到的第一眼硬件布局。需求很朴素:同一台设备要同时接一路内网采集、一路外网回传,还得给调试电脑留个口,一个千兆原生口显然不够。翻了一圈方案,加交换机、换带多网口的核心板、上 USB 以太网适配器,三条路里最省钱、改动最小、也最容易回退的就是最后一条:插一个 USB 以太网网卡,把内核里 usbnet 那一套驱动跑起来。听起来像插上就能用,实际动手才发现,RK3399 这种嵌入式平台上"USB 以太网编译和安装"这件事,卡人的地方从来不是插拔,而是内核配置没开、模块版本对不上、固件没放对位置、开机不自动加载这几步。这篇就把我从零到跑通、再到稳定压测的完整过程摊开写,包括选型时踩的坑、只编单个模块的省时做法、以及在板子上做双网口路由的那点经验,适合正在折腾 RK3399 或类似 ARM64 平台、需要扩展网络接口的同行参考。

1. 先把需求捋清楚:为什么非得给板子挂个 USB 以太网

1.1 板载原生网口不够用的三个真实场景

很多教程一上来就讲编译,跳过了"为什么",结果读者照着敲完命令,驱动加载了,却不知道自己要拿它干什么,后面配 IP、配路由全抓瞎。我先说说我这边的三个场景,基本能覆盖大部分人的需求。

第一种是多网段隔离。设备一头接现场的内网采集设备,网段是 192.168.1.x,另一头要把数据回传到上层服务器,网段是 10.10.x.x,两个网段物理隔离、不能互通。只有一个网口的话,要么加一台交换机再做 VLAN,要么就得来回改配置,前者增加成本,后者根本没法同时在线。

第二种是调试口独立。开发阶段我习惯用 SSH 连板子,但一旦我在原生网口上改 IP、改路由、重启网络服务,SSH 必断,人就被踢出去了,只能跑去接串口。多一个 USB 网口专门留给调试链路,改另一个口的配置时心里踏实得多,这个习惯我保持了很多年,救过不少次现场。

第三种是功能验证与备用链路。有些项目要求主链路故障时能自动切到备用通道,USB 网卡插上去就是现成的第二张网卡,配个路由 metric 就能做主备切换,验证成本极低。而且 USB 网卡坏了大不了换一个,几十块钱的事,比返修主板划算太多。

把这三个场景想明白,后面所有的配置选择才有依据——你要的是"能通",还是"能扛住长时间跑",决定了你是随手 insmod 一下,还是老老实实做开机自动加载和稳定性测试。

1.2 芯片选型:从 AX88772 到 RTL8153 的取舍

USB 以太网适配器市面上五花八门,外壳长得都差不多,里面的主控芯片差别很大。买之前一定看商品详情页有没有写芯片型号,没写的直接跳过,那种大概率是便宜货,内核里驱动支持得零零碎碎。我手上这几类都用过,整理成一张表:

芯片方案接口速率内核配置项是否需固件实际体验
AX88772 系列百兆CONFIG_USB_NET_AX8817X否老而稳,几乎不需要折腾,适合只要通网的场景
AX88179 系列千兆CONFIG_USB_NET_AX88179_178A否千兆里兼容性最好的之一,USB3.0 下能跑起来
RTL8152/8153 系列百兆/千兆CONFIG_USB_RTL8152部分型号需要性能不错,但固件缺失时会加载失败,坑最多
CDC ECM / NCM 类视设备而定CONFIG_USB_NET_CDCETHER / CDC_NCM否免驱类,通用性好,性能一般

选型上我的建议很直接:如果只是想给板子多加一个网口,优先选 AX88179 或 AX88772,内核里配置项单一、不依赖外部固件、加载即用,省心。如果你追求千兆吞吐并且能接受折腾,RTL8153 系列性能确实更好,但要提前把固件文件准备好,否则 modprobe 之后 dmesg 里会出现固件加载失败的报错,网卡根本出不来。

还有一个容易忽略的点:USB 口的速度等级决定上限。RK3399 平台上通常有 USB2.0 和 USB3.0 接口,千兆网卡插在 USB2.0 口上,理论带宽 480Mbps,实际跑个 280~330Mbps 就到顶了,网卡再好也没用。插之前先确认你插的是哪个口,别回头测出来只有两百多兆就怀疑驱动有问题。

1.3 "内核支持"和"能用"之间隔着好几道坎

新手最容易有的一个误解是:内核源码里明明有 r8152.c 这个文件,为什么板子上插了网卡没反应。原因很现实——源码里有不等于编译进去了。厂商给的默认 defconfig 为了控制镜像体积,通常只保留了最基础的一批驱动,USB 网络这一块经常是关的,或者是编成模块但从来没往文件系统里装过。

所以完整链路其实是四步:内核配置里把对应选项打开、编译出 .ko 文件、把 .ko 放到板子文件系统的正确目录、让系统在启动时自动加载它。任何一步断了,表现都是"插上网卡没反应",而这四种"没反应"的排查方向完全不同。后面我会按这四步分开讲,每一步都给出确认方法,这样你卡住的时候知道自己在哪一段。

顺便提一句,有些厂商的 BSP 内核会把 usbnet 编进内核(built-in),但具体某个网卡驱动编成模块。这种情况下你只要补那一个 .ko 就行,不用重编整个内核,能省下大量时间。所以在动手之前,先摸清楚现状,比闷头编译重要得多。

2. 编译前的准备:把内核源码和工具链理顺

2.1 先确认板子上跑的内核和你手上的源码是不是同一份

这一步我吃过亏,必须放在最前面说。模块和内核之间有一条硬约束:vermagic 必须完全一致,包括内核版本号、SMP 是否开启、抢占模式、模块版本校验这些信息。版本号差一个字符,insmod 就会甩给你一句 "invalid module format",然后你盯着源码看半天也看不出问题。

确认方法很简单,板子上执行:

uname -r cat /proc/version

把输出记下来,比如4.4.189或者5.10.110,然后到源码根目录看 Makefile 前几行的VERSION、PATCHLEVEL、SUBLEVEL是不是对得上。对不上就说明你手上的源码不是板子上跑的那份,要么去问厂商要对应版本的 BSP 包,要么就接受"编出来的模块装不上"这个结果。

还有一种情况更隐蔽:版本号一样,但配置文件不一样。比如板子上跑的内核开了CONFIG_PREEMPT,你手上这份源码的 .config 没开,编出来的模块 vermagic 里会多一个preempt或者少一个,照样装不上。所以最稳妥的做法是直接从板子上把当前配置抠出来:

# 板子上执行,前提是内核开了 IKCONFIG zcat /proc/config.gz > board_defconfig

如果/proc/config.gz不存在,那就只能找厂商要编译这份内核时用的 defconfig 文件。拿到之后放到源码的arch/arm64/configs/目录下,后面配置内核时直接用它做基线,能最大程度保证一致性。

2.2 交叉编译工具链的选择与验证

RK3399 是 ARM64 架构,编译要在 x86 主机上做,需要 aarch64 的交叉工具链。工具链的选择上不用太纠结,主流的两条路:一是用厂商 BSP 包里自带的预编译工具链,路径一般在prebuilts/gcc/linux-x86/aarch64/下面;二是用系统包管理器装一个,比如 Ubuntu 上的gcc-aarch64-linux-gnu。

我个人的偏好是优先用厂商自带的,理由很实际:内核里有些汇编代码对编译器版本比较敏感,换个版本可能出现奇怪的编译错误或者运行时行为差异,厂商既然验证过,就别给自己找麻烦。当然如果你只是编个 USB 网卡模块,用系统工具链一般也没问题。

装好之后先验证一下:

aarch64-linux-gnu-gcc --version

能打印出版本信息就说明 PATH 里能找到。如果用的是厂商工具链,通常需要手动加路径:

export PATH=$PWD/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin:$PATH export CROSS_COMPILE=aarch64-linux-gnu- export ARCH=arm64

这三个环境变量建议写进一个envsetup.sh脚本里,每次开新终端 source 一下,省得反复敲。ARCH=arm64这一条千万别漏,漏了的话 make 会按主机架构去编译,报一堆莫名其妙的错误。

2.3 一次干净的基线编译,别省这一步

在改配置之前,我强烈建议先做一次原样编译,确认工具链、源码、配置这套组合本身是能跑通的。这步看着多余,其实是在排除变量:如果基线都编不过,你改完配置编不过,就分不清是配置改错了还是环境本来就有问题。

命令大致是这样,具体目录名按你的源码结构来:

cd kernel make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- your_defconfig make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image dtbs

第一次编译时间会比较长,四核八线程的机器上大概二三十分钟,看源码规模和机器性能。编完检查一下arch/arm64/boot/Image有没有生成,有就说明基线没问题。

如果这一步就报错,常见原因是缺依赖包。Ubuntu 上一般需要装bison、flex、libssl-dev、libncurses5-dev、bc这几个,缺哪个报错信息里都会写,照着装就行。还有个小坑:有些老版本内核配新版 gcc 会报-Werror相关的警告直接中断编译,这种可以在 make 后面加KCFLAGS="-Wno-error"临时绕过,但更推荐换个匹配的工具链版本。

3. 内核配置:把 USB 以太网驱动真正打开

3.1 menuconfig 里的关键路径与选项含义

配置入口用make menuconfig,图形界面里按这个路径找:Device Drivers→Network device support→USB Network Adapters。进去之后你会看到一长串选项,第一次看容易懵,我把几个关键的说清楚:

配置项含义是否必选
CONFIG_USB_USBNETusbnet 框架本体,所有 USB 网卡驱动的底座必选
CONFIG_MII媒体独立接口库,usbnet 依赖它必选(通常默认已开)
CONFIG_USB_NET_AX8817XAX88772 系列百兆方案按需
CONFIG_USB_NET_AX88179_178AAX88179 系列千兆方案按需
CONFIG_USB_RTL8152RTL8152/8153 系列按需
CONFIG_USB_NET_CDCETHERCDC ECM 免驱方案按需
CONFIG_USB_NET_CDC_NCMCDC NCM 免驱方案按需
CONFIG_USB_NET_SMSC75XXSMSC 75xx 系列按需

CONFIG_USB_USBNET是底座,没有它上面那些具体驱动一个都编不出来,这个依赖关系在新手眼里经常被忽略——明明勾了 AX88179,编出来没有 .ko,就是因为它爹没开。

实际选的时候不用全开,按你手上的网卡芯片勾对应的就行,多开了只会让镜像变大、编译变慢。我一般会顺手把CONFIG_USB_NET_CDCETHER和CONFIG_USB_NET_CDC_NCM也打开编成模块,理由很朴素:免驱类设备太常见了,同事随手拿个网卡过来测试、或者临时要接个带网口的扩展坞,有这两个模块在手能省很多事。

3.2 编成模块还是直接编进内核

这个问题每次都会被问到,我的答案取决于你的使用场景:

编成模块(=m),好处是不用重新烧写内核镜像,把 .ko 拷到板子上 modprobe 就能用,出问题也好回退。缺点是文件系统里得留位置放 .ko,而且开机自动加载要多配一步。开发调试阶段我几乎全部选这个。

编进内核(=y),好处是开机就有,不用管模块加载,适合量产固件。缺点是每次改动都要重新烧内核,调试周期长,而且会让内核镜像变大几十到上百 KB。

还有一个折中做法值得推荐:usbnet 框架和 MII 编进内核(=y),具体网卡驱动编成模块(=m)。这样底座常驻,插不同网卡只需要加载对应的那一个 .ko,既灵活又不至于每次都动内核。这个组合我在好几个项目里都用了,很顺手。

配置改完记得保存,menuconfig 里选Save,默认写到.config。保险起见再确认一下:

grep -E "USB_USBNET|USB_NET_AX88179|USB_RTL8152|CONFIG_MII" .config

输出里能看到=y或=m就说明写进去了。有时候图形界面里勾了但保存失败,就是权限或者磁盘问题,用 grep 确认一遍最踏实。

3.3 只编模块的省时办法

改完配置如果只编模块,完全不用重新编整个内核,这一步能省下大量时间。正确姿势是先做一次 prepare,然后指定目录编译:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules_prepare make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- M=drivers/net/usb modules

modules_prepare的作用是准备好编译模块所需的头文件和脚本,只需要跑一次。M=指定只编drivers/net/usb这个子目录,几十秒就能出结果。

有个坑必须提前说:用modules_prepare编出来的模块,有可能因为缺少Module.symvers而在加载时报 "unknown symbol"。原因是modules_prepare不会生成完整的内核符号表。解决办法是老老实实跑一次完整的内核编译(不用烧写),把Module.symvers生成出来,然后再编模块。这也是为什么我前面强调先做一次基线编译——它顺手就把符号表准备好了。

编译完成后,产物在drivers/net/usb/目录下:

ls -lh drivers/net/usb/*.ko

正常情况下能看到usbnet.ko、mii.ko(如果 MII 编成模块的话)、ax88179_178a.ko或者r8152.ko。用modinfo检查一下版本信息对不对:

modinfo drivers/net/usb/ax88179_178a.ko | head -20

重点看vermagic那一行,和你板子上uname -r的输出比对一下,一致才能往下走。

3.4 RTL8153 的固件准备,这是最容易被忽略的一步

如果你选的是 RTL8152/8153 系列,还有一个额外的活要干:准备固件文件。部分型号在初始化时需要从/lib/firmware/加载固件,缺了就会加载失败。

固件文件通常是这几个名字:rtl_nic/rtl8153a-2.fw、rtl8153a-3.fw、rtl8153a-4.fw、rtl8153b-2.fw等等,具体需要哪个取决于芯片的具体版本。这些文件一般在源码树的firmware/目录或者厂商的 BSP 包里能找到,也可以从上游的 linux-firmware 仓库获取。

放的位置是:

# 板子上的目标路径 /lib/firmware/rtl_nic/

目录不存在就自己建。放好之后加载驱动,用 dmesg 确认固件加载成功。如果看到类似 "Direct firmware load failed" 的报错,八成就是文件名或者路径不对,仔细核对一下内核源码里r8152.c中写死的固件名,别猜。

4. 安装到板子:从拷贝到开机自动加载

4.1 传输通道怎么选,以及为什么别用 U 盘

把 .ko 传到板子上,常用三条路:网络(scp)、串口(通过终端软件的传输功能)、U 盘。我的建议是能用网络就别用别的,原因很实际:U 盘插拔容易碰到其他设备,而且有些文件系统挂载之后权限位会乱;串口传输速度慢还容易丢包。

前提当然是你板子现在的网络是通的。如果原生网口已经配好了,scp 一条命令搞定:

scp drivers/net/usb/*.ko root@192.168.1.100:/tmp/

如果板子还没网,那就先接串口或者用 TF 卡拷过去,这个阶段忍一忍就行。另外提醒一句,拷贝之前先在板子上建个临时目录,别一股脑往/root里扔,后面清理的时候容易漏。

4.2 目录放置与 depmod,模块依赖怎么处理

模块的正确落点是:

/lib/modules/$(uname -r)/kernel/drivers/net/usb/

注意版本号是从板子上uname -r动态取的,别手敲。放好之后跑一次depmod:

depmod -a $(uname -r)

这一步的作用是扫描所有模块,把依赖关系写进modules.dep和modules.alias。为什么要做这一步?因为ax88179_178a.ko依赖usbnet.ko,usbnet.ko又依赖mii.ko,你直接 insmod 具体驱动会报 "unknown symbol" 之类的错。跑完 depmod 之后用modprobe加载,它会自动把依赖链按顺序拉起来,省事。

验证依赖关系有没有写进去:

modinfo -F depends ax88179_178a

正常应该输出usbnet,mii之类的内容。如果是空的,说明 depmod 没扫到,检查一下文件是不是放对目录了、文件名后缀对不对。

注意:depmod跑完之后,如果你又拷了新模块进去,必须重新跑一次,否则新模块的依赖关系和别名不会更新。这个坑我见过不止一次,表现为"模块明明在目录里,modprobe 却说找不到"。

4.3 加载、卸载与开机自动加载

手动加载先验证一遍:

modprobe ax88179_178a

然后dmesg | tail -30看输出。成功的标志是能看到类似usb 1-1.2: new high-speed USB device、ax88179_178a 1-1.2:1.0 eth1: register 'ax88179_178a'这样的日志,并且ip link里能看到新接口。

卸载用modprobe -r ax88179_178a,注意如果接口还处于 up 状态,卸载会失败,先ip link set eth1 down。

开机自动加载有两种方式,我推荐第一种:

# 方式一:modules-load.d,干净利落 echo "ax88179_178a" > /etc/modules-load.d/usbnet.conf # 方式二:写入 /etc/modules(部分老系统用这个) echo "ax88179_178a" >> /etc/modules

modules-load.d是 systemd 体系下的标准做法,配置文件里一行一个模块名,开机由 systemd-modules-load 服务统一处理,不依赖其他脚本的执行顺序。比往rc.local里塞 insmod 靠谱得多——rc.local 在有些发行版里默认是关的,你改完以为生效了,重启一看白干。

还有一种情况要考虑:热插拔。如果你的使用方式是"板子先开机,网卡后插",那就不能只靠开机加载。要么让驱动常驻(编进内核或者开机就 modprobe),要么配 udev 规则触发加载。实际上 usbnet 驱动加载之后是常驻的,检测到新设备会自动绑定,所以只要驱动在内存里,热插拔没问题。我一般就是开机直接 modprobe,插拔随意。

4.4 接口命名不叫 eth1 怎么办

现在不少发行版启用了 predictable network interface names,新插的网卡可能不叫eth1,而是一串类似enx00e04c123456的名字,中间那段是 MAC 地址。这不是出错,是命名规则变了。

如果脚本里写死了eth1,就会一路报错。两个选择:一是改脚本用新名字,二是把命名规则关掉:

# 关闭 predictable names,用传统的 ethX 命名 ln -s /dev/null /etc/systemd/network/99-default.link

或者在/etc/udev/rules.d/下加一条规则,按 MAC 地址固定命名:

SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="00:e0:4c:12:34:56", NAME="eth1"

我偏向第二种,因为固定了名字之后,不管系统怎么升级、怎么改命名策略,这块网卡永远是eth1,脚本不用动。缺点是换一块网卡就得改规则,但生产环境里一般不会随便换。

5. 验证、配置与性能实测

5.1 dmesg 和 ethtool 要看哪几个关键信息

驱动加载完,第一件事是确认它真的被识别成了什么样的设备:

dmesg | grep -i -E "usb|eth" ethtool -i eth1

ethtool -i的输出里重点看三个字段:driver是驱动名,得和你加载的模块对得上;version是驱动版本;bus-info是挂在哪个 USB 总线上,形如1-1.2:1.0。这个 bus-info 后面调电源管理的时候要用。

再看链路状态:

ethtool eth1

输出里Speed显示 1000Mb/s 还是 100Mb/s、Duplex是全双工还是半双工、Link detected是不是 yes,一目了然。如果显示 100Mb/s,先别急着怀疑驱动,八成是网线的问题——老的五类线跑千兆会协商降速,换根超五类或者六类线再试。

还有一种情况是Link detected: no,但网卡确实插着,对端交换机也是好的。这种多半是 USB 供电不足,尤其在小体积的 HUB 上。表现是网卡枚举出来了,但 PHY 初始化不完整。换个带独立供电的 HUB 试试,或者直接插板子上的 USB3.0 口。

5.2 IP 配置与双网口路由

接口通了之后配 IP。先用 iproute2 那套命令,比老的 ifconfig 更清晰:

ip addr add 192.168.10.2/24 dev eth1 ip link set eth1 up ip route add 192.168.10.0/24 dev eth1

如果要配成 DHCP 客户端:

udhcpc -i eth1 # 或者 dhclient eth1,看系统里装的是哪个

双网口的核心问题是默认路由走谁。两个网口都配了网关的话,系统里会出现两条 default 路由,谁先加谁的 metric 小,流量就走谁,很容易出现"内网能通、外网不通"或者反过来。正确做法是只给一个口配默认网关,另一个口只加网段路由:

# 主链路走默认网关 ip route add default via 192.168.1.1 dev eth0 metric 100 # 副链路只走自己的网段,不抢默认路由 ip route add 192.168.10.0/24 dev eth1 metric 200

如果你需要主备切换(主链路断开自动切副链路),可以在同一条 default 上配不同 metric:

ip route add default via 192.168.1.1 dev eth0 metric 100 ip route add default via 192.168.10.1 dev eth1 metric 200

metric 小的优先,主链路 down 掉之后内核会自动切到 metric 大的那条。这套机制很简单但很可靠,我用了很多次,比装一堆路由协议软件轻量得多。

5.3 带宽实测与长跑稳定性

性能这块不测心里没底。用 iperf3 最方便,PC 端起服务端,板子上跑客户端:

# PC 端 iperf3 -s # 板子端,-t 60 表示跑 60 秒 iperf3 -c 192.168.1.50 -t 60

参考数据:AX88179 插在 RK3399 的 USB3.0 口上,千兆链路,实测单向大约 850~940Mbps,双向同时跑会掉到各自四五百兆。同样的网卡插在 USB2.0 口上,顶天 300Mbps 出头。RTL8153 在 USB3.0 下能跑到 900Mbps 上下,差距不大,但小包性能通常略好一点。

长跑测试我一般跑 30 分钟以上,观察两个指标:一是吞吐有没有随时间下降,二是dmesg里有没有出现NETDEV WATCHDOG或者 USB reset 相关的日志。出现 USB reset 基本可以确定是供电或者线材问题,驱动本身很少是这个原因。

再补充一个容易被忽略的参数:关闭 USB 自动挂起。默认情况下内核为了省电,会让空闲的 USB 设备进入低功耗状态,网卡频繁进出这个状态时可能丢包或者断连。关闭方法:

echo -1 > /sys/bus/usb/devices/1-1.2/power/autosuspend

路径里的1-1.2就是前面ethtool -i看到的 bus-info。要持久化的话,写成 udev 规则或者开机脚本,别指望手动 echo 一次就完事。

6. 踩坑实录与问题速查

6.1 常见问题速查表

下面这些是我这几年攒下来的,基本覆盖了 USB 以太网在 RK3399 上出问题的绝大多数情况:

现象大概率原因排查动作
插上网卡毫无反应,dmesg 无任何日志USB 口没供电或设备没枚举换口、换线、看lsusb有无新设备
有 USB 枚举日志,但没有 net 接口注册对应网卡驱动没加载lsmod看驱动在不在,modprobe手动加载
modprobe 报 "module not found".ko 没放到/lib/modules/$(uname -r)/或没跑 depmod检查目录、重跑depmod -a
insmod 报 "invalid module format"vermagic 不匹配对比modinfo的 vermagic 和uname -r
modprobe 报 "unknown symbol"依赖模块没加载,或 Module.symvers 缺失先加载 usbnet/mii,重编模块
驱动加载了但接口没有 IP,也拿不到 DHCP链路没起来或对端没开 DHCPethtool看 Link detected,ip link看状态
RTL8153 加载失败,日志提示固件固件文件缺失或路径不对放到/lib/firmware/rtl_nic/
千兆网卡只协商到 100Mbps网线质量或对端口速率换六类线,检查交换机端口配置
跑一段时间后断连,dmesg 有 USB reset供电不足或 USB 自动挂起换带供电 HUB,关闭 autosuspend
重启后网卡不见了没配开机自动加载写/etc/modules-load.d/配置

6.2 几条文档里不会写的经验

第一条:先lsusb,再谈驱动。很多人插上网卡第一步就去 modprobe,加载失败就开始怀疑内核配置。正确的顺序是先lsusb,看设备有没有被 USB 子系统枚举出来,能不能看到厂商 ID 和产品 ID。看不到的话,问题在硬件层——供电、线材、接口,跟驱动一点关系都没有。这一步能省掉大量无效排查。

第二条:别迷信"免驱"。商家标榜的"免驱"通常是指 Windows 下免驱,Linux 下能不能用完全看内核有没有对应的驱动。买之前查一下芯片型号在内核源码drivers/net/usb/目录下有没有对应的 .c 文件,比看商品详情页靠谱一百倍。

第三条:模块文件别只拷一个。依赖链上的usbnet.ko、mii.ko都要一起拷,少一个就加载失败。我见过有人只拷了ax88179_178a.ko,然后对着 "unknown symbol usbnet_generic_cdc_bind" 这种报错查了一下午。

第四条:改完内核配置一定要 grep 确认。menuconfig 图形界面偶尔会出现勾了没保存的情况,尤其是用 SSH 连过去跑的时候网络一抖。保存之后grep一下.config,几秒钟的事,能避免一次白编译。

第五条:量产固件里把驱动编进内核更省心。调试阶段用模块方便,量产时建议把 usbnet 和相关驱动都改成=y,省掉模块加载和文件系统依赖,减少一个可能出问题的环节。这个切换点我一般放在功能验证完成、开始做稳定性测试之前。

6.3 关于固件体积的一点取舍

最后聊个实际项目中会遇到的权衡。RK3399 平台的固件通常放在 eMMC 上,空间不算紧张,但把整个drivers/net/usb目录下所有驱动全编进去,加起来也就几百 KB,对固件体积影响很小。所以我个人的做法是:量产固件里把常见的几类 USB 网卡驱动全编进内核,包括 AX88772、AX88179、RTL8152、CDC ECM、CDC NCM。

理由是现场环境不可控,客户或者施工方随手拿来的网卡什么芯片都有可能,驱动不全就得远程指导人家换网卡,沟通成本远高于几百 KB 的存储开销。这个决定我在两个项目里做过,事后都被证明是对的——现场救急的时候,多一个驱动就少一次返工。

反过来,如果你的固件要刷到几万台设备上、升级带宽有限,那就精简到只留项目指定的那一款芯片,其余全关。这时候驱动选择就得跟着采购清单走,BOM 里写的是什么芯片,内核里就只留什么驱动,别留余地,避免现场插错网卡反而引发一堆兼容性问题的排查。

这套流程从选型到编译、从部署到压测,我在三块不同批次的板子上都走过一遍,每次卡住的地方都不太一样,但排查路径是通用的。真正让我少走弯路的不是记住了多少条命令,而是养成了"先确认现状、再动手改"的习惯——先uname -r看版本,先lsusb看设备,先lsmod看模块,把现状摸清楚,剩下的基本就是按图索骥。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询