> **作者**: 郑智乾
> **职位**: LINUX驱动开发
> **日期**: 2026-02-01
> **平台**: RK3566 LubanCat 开发板
> **系统**: Ubuntu 20.04 (Focal) Lite
> **内核**: Linux 6.1
## 前言
这是一篇完整的技术复盘文章,记录了我在 RK3566 LubanCat 开发板上配置 USB Gadget 功能(NCM 网卡 + ADB 调试)的全过程。从最初的需求分析,到中途踩过的各种坑,再到最终成功解决问题,整个过程充满了曲折。希望这篇文章能帮助到同样在嵌入式 Linux USB 功能配置上遇到困难的开发者。
## 第一章:需求分析与背景
### 1.1 我的需求是什么?
我有一块 RK3566 LubanCat 开发板,希望实现以下功能:
| 功能 | 描述 | 用途 |
|------|------|------|
| **USB NCM 网卡** | 通过 USB OTG Type-C 连接电脑后,在电脑上创建一个虚拟网卡 | 方便调试,不依赖网线 |
| **ADB 调试** | 支持 Android Debug Bridge | 文件传输、命令行调试 |
| **USB ACM 串口** | USB 虚拟串口 | 日志输出、串口调试 |
| **以太网 DHCP** | 插网线后自动获取 IP | 正常上网 |
| **SSH 远程登录** | 允许 root 用户通过 SSH 登录 | 远程管理 |
### 1.2 网络架构设计
```
┌─────────────────┐
│ 路由器 │
│ (DHCP 服务器) │
└────────┬────────┘
│ eth0 (DHCP: 如 192.168.1.100)
│ ← 上网用,默认网关
┌──────────────────┐ │
│ Windows/Mac │ ┌────┴────┐
│ 192.168.100.2 │ │ RK3566 │
└────────┬─────────┘ │ 开发板 │
│ └──────────┘
└──────────────┤
usb0 (NCM)
192.168.100.1 (无网关,本地通信)
```
设计要点:
-
**以太网 eth0**:通过 DHCP 自动获取 IP,用于上网,有默认网关
-
**USB NCM usb0**:静态 IP 192.168.100.1,仅用于开发板与电脑的本地通信,**无网关**(避免路由冲突)
### 1.3 技术背景知识
什么是 USB Gadget?
USB Gadget 是 Linux 内核提供的一种机制,允许设备作为 USB 从设备(Device)与主机(Host)通信。常见的 USB Gadget 功能包括:
| 功能 | 缩写 | 说明 |
|------|------|------|
| Android Debug Bridge | ADB | 安卓调试桥,支持文件传输和命令行 |
| Network Control Model | NCM | USB 网卡,macOS/Linux 原生支持 |
| Remote NDIS | RNDIS | USB 网卡,Windows 原生支持 |
| USB Video Class | UVC | USB 摄像头 |
| Abstract Control Model | ACM | USB 虚拟串口 |
| Mass Storage | MSC | USB 大容量存储 |
LubanCat SDK 构建系统
LubanCat SDK 是野火针对 RK3566/RK3568 芯片定制的开发套件,基于 Rockchip 官方 SDK。其构建流程大致如下:
```
./build.sh defconfig → 加载板卡配置
./build.sh kernel → 编译内核
./build.sh rootfs → 编译根文件系统
./build.sh all → 编译全部
./rkflash.sh → 烧录到开发板
```
## 第二章:第一次尝试------修改错误的配置文件
### 2.1 最初的错误思路
一开始,我直接去修改 `output/.config` 文件,以为这样就能改变编译配置:
错误的做法!
vim output/.config
添加 RK_USB_NCM=y 等配置
然后重新编译... 结果发现配置没有生效。
### 2.2 问题分析
`output/.config` 是编译时**自动生成**的临时配置文件,每次执行 `./build.sh defconfig` 都会被覆盖。真正的永久配置文件在:
```
device/rockchip/.chips/rk3566_rk3568/LubanCat_rk3566_ubuntu_lite_defconfig
```
这是一个关键的认知错误,浪费了不少时间。
### 2.3 正确的做法
修改板卡的 defconfig 文件:
device/rockchip/.chips/rk3566_rk3568/LubanCat_rk3566_ubuntu_lite_defconfig
USB Gadget functions
RK_USB_NCM=y
RK_USB_ACM=y
RK_USB_UVC is not set # 不启用摄像头功能
**教训一**:永远要搞清楚配置文件的层级关系,修改源头配置而非生成的临时文件。
## 第三章:第二次尝试------内核缺少 NCM 支持
### 3.1 新的错误信息
修改 defconfig 后重新编译,启动开发板发现新的错误:
```
Your kernel doesn't support USB gadget: ncm
Please enable: CONFIG_USB_CONFIGFS_NCM
```
### 3.2 问题分析
SDK 的 defconfig 只是告诉编译系统"我需要 NCM 功能",但内核本身也需要支持这个功能。内核配置和 SDK 配置是**两套独立的系统**。
### 3.3 解决方案
需要同时修改内核的 defconfig:
kernel-6.1/arch/arm64/configs/lubancat_linux_rk356x_defconfig
添加以下配置
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
然后重新编译内核:
./build.sh kernel
**教训二**:USB Gadget 功能需要**双重配置**------SDK defconfig + 内核 defconfig。
## 第四章:第三次尝试------Ubuntu rootfs 不执行 overlay 脚本
### 4.1 又一个坑
按照 Rockchip 官方文档,USB Gadget 的运行时配置应该放在:
```
device/rockchip/common/overlays/rootfs/usb-gadget/
```
然后通过 `post-hooks` 机制在编译时复制到 rootfs 中。
我按照这个思路创建了配置文件,编译后挂载 rootfs 镜像检查... 配置文件不存在!
### 4.2 深入分析
通过阅读编译脚本,我发现了一个关键事实:
> **Ubuntu rootfs 的构建流程不会执行 `device/rockchip/common/post-hooks/` 中的脚本!**
这个机制只对 Buildroot 和 Debian 系统生效。对于 Ubuntu 系统,overlay 文件必须直接放在:
ubuntu20.04/overlay/
这个目录下的文件会被**直接复制**到 rootfs 根目录。
### 4.3 正确的文件结构
ubuntu20.04/overlay/
├── etc/
│ ├── profile.d/
│ │ └── usbdevice.sh # USB 功能环境变量
│ ├── usbdevice.d/
│ │ └── ncm.sh # NCM 网卡 IP 配置
│ ├── ssh/
│ │ └── sshd_config # SSH 配置
│ ├── netplan/
│ │ └── 01-network-manager-all.yaml # 以太网 DHCP
│ └── systemd/system/
│ └── sysinit.target.wants/
│ └── usbdevice.service # 软链接,启用服务
**教训三**:不同的 rootfs 类型有不同的 overlay 机制,一定要阅读构建脚本确认。
## 第五章:第四次尝试------usbdevice 服务未启用
### 5.1 服务没有自动启动
配置文件都放对位置了,编译烧录后开机... USB 功能还是不工作。
在开发板上检查:
systemctl status usbdevice
结果:inactive (dead)
服务没有启动!
### 5.2 问题根源
虽然 `usbdevice.service` 文件存在于 `/lib/systemd/system/` 目录,但 systemd 不会自动启用它。需要创建一个软链接来告诉 systemd "开机时启动这个服务"。
### 5.3 解决方案
在 overlay 中创建启用软链接:
mkdir -p ubuntu20.04/overlay/etc/systemd/system/sysinit.target.wants
ln -sf /lib/systemd/system/usbdevice.service \
ubuntu20.04/overlay/etc/systemd/system/sysinit.target.wants/usbdevice.service
这个软链接会被复制到 rootfs 中,系统启动时 systemd 就会自动启动 usbdevice 服务。
**教训四**:systemd 服务需要显式启用,即使服务文件存在也不会自动运行。
## 第六章:第五次尝试------UVC 导致整个 USB Gadget 崩溃
### 6.1 最棘手的问题
这是整个调试过程中最难发现的问题。
之前为了"以后可能会用到",我启用了 UVC(USB 摄像头)功能:
export USB_FUNCS="adb acm uvc ncm"
结果开发板连接电脑后,Windows **完全没有任何反应**。以前至少会有"咚"的一声提示音,现在连这个都没有了。
### 6.2 诊断过程
在开发板上查看日志:
cat /tmp/usbdevice.log
发现大量错误:
Starting functions: uvc adb acm ncm
/usr/bin/usbdevice: 369: cd: can't cd to .../uvc.gs6/...
ln: failed to create symbolic link '...': Device or resource busy
mkdir: cannot create directory '480p': Operation not permitted
mkdir: cannot create directory '720p': Operation not permitted
### 6.3 问题分析
UVC 功能需要硬件支持(实际的摄像头传感器)。当开发板上没有连接摄像头时,UVC 驱动初始化失败。
关键问题在于:**UVC 初始化失败会导致整个 USB Gadget 配置过程中断**,后续的 ADB、ACM、NCM 功能也无法正常工作。
### 6.4 解决方案
移除不需要的 UVC 功能:
ubuntu20.04/overlay/etc/profile.d/usbdevice.sh
export USB_FUNCS="adb acm ncm" # 移除 uvc
同时修改 defconfig:
device/rockchip/.chips/rk3566_rk3568/LubanCat_rk3566_ubuntu_lite_defconfig
RK_USB_NCM=y
RK_USB_ACM=y
RK_USB_UVC is not set # 禁用 UVC
**教训五**:不要启用不需要的功能!一个功能的失败可能导致整个子系统崩溃。YAGNI(You Aren't Gonna Need It)原则在嵌入式开发中尤为重要。
## 第七章:SSH 连接卡顿问题
### 7.1 意外发现的问题
在调试 USB 问题的过程中,我发现了另一个问题:SSH 连接特别慢,ping 开发板也很卡,好几秒才响应一次。
### 7.2 问题分析
这是一个经典的 SSH 配置问题。SSH 服务端默认会对客户端 IP 进行反向 DNS 查询,用于日志记录和访问控制。
但在局域网环境中,私有 IP(如 192.168.x.x)在 DNS 服务器上没有记录,查询会超时。每次操作都要等待 DNS 超时,导致严重卡顿。
### 7.3 解决方案
禁用 SSH 的 DNS 反向查询:
ubuntu20.04/overlay/etc/ssh/sshd_config
UseDNS no
GSSAPIAuthentication no
-
**UseDNS no**:禁止 SSH 对客户端 IP 做反向 DNS 查询
-
**GSSAPIAuthentication no**:禁用 Kerberos 认证(开发板用不到)
这两个选项对开发板场景没有任何负面影响,却能显著提升 SSH 响应速度。
**教训六**:遇到网络卡顿问题,优先考虑 DNS 相关配置。
## 第八章:最终配置清单
经过多次调试,以下是最终成功的完整配置:
### 8.1 SDK defconfig
**文件**: `device/rockchip/.chips/rk3566_rk3568/LubanCat_rk3566_ubuntu_lite_defconfig`
RK_UBUNTU_FOCAL=y
RK_ROOTFS_SYSTEM_UBUNTU=y
RK_ROOTFS_TARGET_LITE=y
RK_KERNEL_PREFERRED="6.1"
RK_KERNEL_CFG="lubancat_linux_rk356x_defconfig"
... 其他配置 ...
USB Gadget functions
RK_USB_NCM=y
RK_USB_ACM=y
RK_USB_UVC is not set
### 8.2 内核 defconfig
**文件**: `kernel-6.1/arch/arm64/configs/lubancat_linux_rk356x_defconfig`
CONFIG_USB_CONFIGFS_NCM=y
CONFIG_USB_CONFIGFS_ECM=y
CONFIG_USB_CONFIGFS_RNDIS=y # 已有
### 8.3 USB 功能环境变量
**文件**: `ubuntu20.04/overlay/etc/profile.d/usbdevice.sh`
USB Gadget configuration
export USB_FUNCS="adb acm ncm"
export USB_VENDOR_ID="0x2207"
export USB_FW_VERSION="0x0310"
export USB_MANUFACTURER="Rockchip"
export USB_PRODUCT="rk3xxx"
### 8.4 NCM 网卡 IP 配置
**文件**: `ubuntu20.04/overlay/etc/usbdevice.d/ncm.sh`
#!/bin/sh
NCM USB Network Configuration
ncm_post_start_hook()
{
Wait for usb0 interface to appear
for i in $(seq 50); do
if ip link show usb0 >/dev/null 2>&1; then
break
fi
sleep 0.1
done
Configure static IP for the device side
ip addr add 192.168.100.1/24 dev usb0 2>/dev/null || true
ip link set usb0 up
}
### 8.5 ADB 配置
**文件**: `ubuntu20.04/overlay/etc/profile.d/adbd.sh`
-x /bin/bash \] \&\& export ADBD_SHELL=/bin/bash export ADB_TCP_PORT=5555 #### ### 8.6 SSH 配置 \*\*文件\*\*: \`ubuntu20.04/overlay/etc/ssh/sshd_config\` # LubanCat SSH Server Configuration PermitRootLogin yes PasswordAuthentication yes PermitEmptyPasswords no ChallengeResponseAuthentication no UsePAM yes X11Forwarding yes PrintMotd no AcceptEnv LANG LC_\* Subsystem sftp /usr/lib/openssh/sftp-server # Performance optimization - disable DNS reverse lookup UseDNS no GSSAPIAuthentication no #### ### 8.7 以太网 DHCP 配置 \*\*文件\*\*: \`ubuntu20.04/overlay/etc/netplan/01-network-manager-all.yaml\` \`\`\`yaml network: renderer: NetworkManager ethernets: eth0: dhcp4: true optional: true eth1: dhcp4: true optional: true #### ### 8.8 usbdevice 服务启用 \*\*文件\*\*: \`ubuntu20.04/overlay/etc/systemd/system/sysinit.target.wants/usbdevice.service\` 这是一个软链接,指向 \`/lib/systemd/system/usbdevice.service\` --- ### ## 第九章:编译与验证 #### ### 9.1 编译命令 cd /home/zzq/workspace/rk3566/LubanCat_SDK # 清理缓存(重要!确保使用最新配置) rm -f output/ubuntu/.stamp\* rm -f ubuntu20.04/ubuntu-rk356x-lite-rootfs.img # 编译 rootfs ./build.sh rootfs # 烧录 ./rkflash.sh rootfs #### ### 9.2 验证步骤 开发板重启后,进行以下验证: # 1. 检查 USB 功能配置 echo $USB_FUNCS # 预期输出: adb acm ncm # 2. 检查 usbdevice 服务状态 systemctl status usbdevice # 预期: active (running) # 3. 检查 USB 网卡 ifconfig usb0 # 预期: 192.168.100.1 # 4. 在 Windows 电脑上 # - 设备管理器应出现 NCM 网卡 # - 执行 adb devices 应能看到设备 # 5. 测试 SSH 连接速度 # 应该秒连,不再卡顿 ### ## 第十章:经验总结 #### ### 10.1 踩过的坑 \| 序号 \| 问题 \| 原因 \| 解决方案 \| \|------\|------\|------\|----------\| \| 1 \| 修改 output/.config 无效 \| 临时生成文件,会被覆盖 \| 修改 defconfig 源文件 \| \| 2 \| 内核不支持 NCM \| 内核 defconfig 未配置 \| 添加 CONFIG_USB_CONFIGFS_NCM=y \| \| 3 \| overlay 文件未生效 \| Ubuntu 不使用 post-hooks \| 使用 ubuntu20.04/overlay/ 目录 \| \| 4 \| usbdevice 服务未启动 \| 缺少 systemd 启用软链接 \| 创建 sysinit.target.wants 软链接 \| \| 5 \| USB 设备完全无法识别 \| UVC 初始化失败导致全崩 \| 移除不需要的 UVC 功能 \| \| 6 \| SSH 连接卡顿 \| DNS 反向查询超时 \| 添加 UseDNS no \| #### ### 10.2 核心原则 通过这次调试,我深刻体会到几个软件工程原则的重要性: 1. \*\*KISS(Keep It Simple, Stupid)\*\* - 不要过度设计,能用简单方案解决的问题不要复杂化 - USB 功能只启用需要的,不要"以备将来使用" 2. \*\*YAGNI(You Aren't Gonna Need It)\*\* - 不需要的功能就不要启用 - UVC 的教训:一个不需要的功能导致整个系统崩溃 3. \*\*分层调试\*\* - 遇到问题先确定是哪一层的问题 - SDK 配置 → 内核配置 → 运行时配置 → 服务启动 4. \*\*阅读源码\*\* - 不要完全依赖文档,构建脚本才是真相 - Ubuntu 不执行 post-hooks 这个坑,只有读脚本才能发现 #### ### 10.3 调试技巧 1. \*\*检查日志\*\* cat /tmp/usbdevice.log dmesg \| grep -i usb journalctl -u usbdevice 2. \*\*验证配置是否生效\*\* # 挂载 rootfs 镜像检查 sudo mount -o loop,ro xxx.img /tmp/check cat /tmp/check/etc/profile.d/usbdevice.sh sudo umount /tmp/check 3. \*\*清理缓存\*\* rm -f output/ubuntu/.stamp\* rm -f ubuntu20.04/ubuntu-rk356x-lite-rootfs.img ## ## 结语 嵌入式 Linux 开发的魅力和痛苦并存。一个看似简单的"配置 USB 网卡"需求,实际上涉及到: - SDK 构建系统 - Linux 内核配置 - USB Gadget 子系统 - systemd 服务管理 - 网络配置 - SSH 服务优化 每一层都可能出问题,每一个细节都需要正确配置。 但正是这种复杂性,让问题解决后的成就感更加强烈。希望这篇复盘文章能帮助到遇到类似问题的开发者,少走一些弯路。 \*\*记住\*\*:当你觉得"配置应该没问题"的时候,问题往往就藏在那些被忽略的细节里。 --- ## ## 附录:完整文件清单 ubuntu20.04/overlay/ ├── etc/ │ ├── profile.d/ │ │ ├── usbdevice.sh # USB 功能环境变量 │ │ └── adbd.sh # ADB 配置 │ ├── usbdevice.d/ │ │ └── ncm.sh # NCM IP 配置脚本 │ ├── ssh/ │ │ └── sshd_config # SSH 优化配置 │ ├── netplan/ │ │ └── 01-network-manager-all.yaml # 以太网 DHCP │ └── systemd/system/ │ └── sysinit.target.wants/ │ └── usbdevice.service # 服务启用软链接 device/rockchip/.chips/rk3566_rk3568/ └── LubanCat_rk3566_ubuntu_lite_defconfig # SDK 板卡配置 kernel-6.1/arch/arm64/configs/ └── lubancat_linux_rk356x_defconfig # 内核配置