cdc-acm与CDC-NCM:Linux USB CDC驱动匹配与调试实战

发布时间:2026/9/16 13:13:37
cdc-acm与CDC-NCM:Linux USB CDC驱动匹配与调试实战 简介这是一份面向Linux驱动开发与嵌入式工程师的USB CDC协议实现源码版本为2.13.6。压缩包共1个C源文件、约3KB体量虽小却集中覆盖CDC-NCM网络控制模型与CDC-ACM抽象控制模型两大关键子类前者让USB设备模拟网络接口卡后者实现类似串行端口的虚拟调制解调器因此非常适合蓝牙、GSM模块、USB网卡等设备在Linux下的驱动移植、功能裁剪或问题排查。通过阅读该C源码开发者可以清晰梳理CDC设备从枚举、配置、数据收发到错误处理的完整实现路径并理解内核中USB Communications Device Class定义的实际用法为后续二次开发提供直接参考。资源标签与描述相互印证明确了代码对应的Linux版本与驱动定位。目前已有681人学习下载对需要快速上手USB CDC协议或调试相关外设的软硬件工程师来说是一份小而精的核心参考资料。1. 一张 U 盘两个身份CDC-NCM_V2 与 cdc-acm 是 Linux USB 协议栈的两张脸想想看某台嵌入式设备丢给你一个带 cdc.rar 的驱动包解压后里面有 CDC-NCM_V2 的补丁另一份文档却反复强调“先装 CDC serial 驱动再调试”。很多人在这就卡住了——cdc-acm 和 CDC-NCM 到底是不是同一个东西 答案不是。它们是 USB CDCCommunication Device Class协议族里的两条平行分支cdc-acm 把 USB 封装成串口CDC-NCM 把 USB 封装成网卡。本文从 Linux 内核的驱动匹配机制讲起带你拆开 cdc.rar 里常见的补丁包把 cdc-acm 串口和 CDC-NCM_V2 网络接口分别跑通最后用 usbmon 抓包验证枚举时序适合嵌入式、工控和车载通信方向的工程师。2. USB CDC 描述符与 Linux 驱动匹配决定接口变成 ttyACM 还是 usb0 的内核规则2.1 从 bInterfaceClass 到驱动 id_tableCDC 家族的分流点所有 USB 设备在枚举时内核会逐个读取接口描述符。接口描述符里有一个三字节组bInterfaceClass、bInterfaceSubClass、bInterfaceProtocol内核靠这三个字段把接口交给对应的 usb_driver。CDC 家族的 bInterfaceClass 固定是 0x02Communication真正区分身份的是子类和协议字段。子类协议常见用途Linux 驱动0x000x00保留/无管理接口usbhid 或忽略0x02ACM0x01调制解调器、调试串口cdc_acm0x06Ethernet0x00/0x01USB 网卡ECM 模式cdc_ether0x0DNCM0x00高速网络透传cdc_ncm内核的drivers/usb/class/cdc_acm.c里acm_ids表通过USB_DEVICE_AND_INTERFACE_INFO宏把厂商/产品 ID 和接口类绑定起来。常见做法是写一个很小的内核模块把usb_device_id填成USB_INTERFACE_INFO(0x02, 0x02, 0x01)指向 ACM或USB_INTERFACE_INFO(0x02, 0x0D, 0x00)指向 NCM就能让 probe 在正确的接口上触发。用 udevadm 查看一个已连接 CDC 设备的分流结果是最快的定位手段# 先确认枚举出的网络接口名和串口节点 lsusb -d 1234:5678 -v | grep -E bInterface(Class|SubClass|Protocol) udevadm info -a -n /dev/ttyACM0 | grep -E ID_USB_CLASS|ID_USB_INTERFACE_NUM ip -br link show | grep usb这段命令的意图是lsusb -v直接把描述符原始值打印出来对照上表判断接口被归到哪类驱动udevadm 显示内核为新节点生成的属性能确认它到底走了 cdc_acm 还是 cdc_ncm 的 probe 路径。如果 bInterfaceClass 被厂商屏蔽成 0xFFudevadm 打印不出 CDC 索引设备就不会出现在 /dev/ttyACM*而是裸的 usbX 节点。2.2 probe 与特判逻辑NCM 为什么比 ACM 多一次数据接口协商CDC 设备通常成对出现一个通信接口管理配一个数据接口。cdc_acm 只认 ACM 子类调用管理接口里的SET_LINE_CODING、SET_CONTROL_LINE_STATE维护串口状态不需要读 NTB 格式。cdc_ncm 完全不同它在第一次绑定后要解析数据接口的额外描述符拿到 dwNtbInMaxSize、dwNtbOutMaxSize 以及 NDP 对齐值才知道一个传输块里最多能塞多少个网络帧。probe 阶段cdc_ncm 用cdc_parse_cdc_header遍历 CDC 功能描述符其中 NCM Datagram Interface 指示器必须存在否则 probe 直接返回-ENODEV。调试时看内核 probe 日志区分两类失败modprobe cdc_acm modprobe cdc_ncm dmesg | grep -iE cdc_acm|cdc_ncm|new full-speed|new high-speed凡是手册让“装 CDC serial 驱动”的多数是 ACM 通道被私有 VID/PID 遮蔽凡是“插上不出现网卡”的多数是 NCM 描述符里的 NTB 参数不合法或者厂商把协议值写成了 ECM 或 RNDIS。2.3 cdc-acm 与 cdc_ncm 的自动加载差异和你手动绑定的痛点Linux 的 usbcore 在发现新接口后按 modprobe 别名自动加载驱动。cdc_acm 的 alias 形如usb:v\*p\*d\*dc\*dsc\*dp\*ic\*isc\*ip\*in\*其中 ic 就是接口类cdc_ncm 走同一套动态匹配。问题在于复合设备里厂商常把调试口设为 ACM、数据口设为 NCM如果两路同时写进一个压缩包发行版内核通常两个驱动都会编上。手动强制绑定要通过 sysfs 解绑再绑定echo -n 2-1:1.0 /sys/bus/usb/drivers/cdc_acm/unbind echo -n 2-1:1.0 /sys/bus/usb/drivers/usb/unbind echo -n 2-1:1.0 /sys/bus/usb/drivers/cdc_ncm/bind这里的2-1:1.0是 bus-port:config.interface 三元组可从lsusb -t的输出拼出来。注意先解绑原来的通用驱动否则 bind 报Device or resource busy。而厂商自带的 CDC-NCM_V2 补丁常见做法是把补丁打进出drivers/net/usb/再把CONFIG_USB_NET_CDC_NCM从 m 改回 y编进内核而不是模块避免 initramfs 阶段没加载驱动导致网络口不可用。3. 从 CDC-NCM_V2 补丁到可用节点编译、装载与 cdc-acm 串口的最小复现3.1 补丁包不能随便打先看补丁头确认内核版本和变更点解压 cdc.rar 这类包后第一件事不是 make而是看补丁头。绝大多数方案商给出的补丁基于某个固定内核版本4.9、4.19 或 5.10生成直接用patch -p1打在新内核上会失败。先检查补丁里是否引用了drivers/net/usb/cdc_ncm.c再对比本地版本的struct usb_cdc_ncm_ndp16是否已经有wLength字段因为 NCM V2 描述符对 NDP 对齐的要求不一样。下面是一套不破坏主内核源码的装载流程适合包内同时给了 cdc-acm 和 cdc-ncm 两路源码的情况tar xf cdc.rar cd cdc-ncm-v2 # 用 dry-run 检查补丁能否应用 patch -p1 --dry-run ./0001-net-usb-cdc_ncm-add-ntb-v2.patch # 确认无误后打进内核源码树 cd /usr/src/linux patch -p1 /path/to/cdc-ncm-v2/0001-net-usb-cdc_ncm-add-ntb-v2.patch make olddefconfig # 把两项驱动编成模块方便替换回退 ./scripts/config -m USB_ACM -m USB_NET_CDC_NCM make -j$(nproc) drivers/usb/class/cdc-acm.ko drivers/net/usb/cdc_ncm.ko参数说明olddefconfig按当前内核默认配置补全新增 Kconfig 项scripts/config -m是内核自带的正则改配置工具比直接改 .config 更不容易写错依赖只编两个 .ko 而不是全量编译能在十分钟内完成补丁回归。若补丁改了头文件或 usb_gadget 公共接口必须先make modules_prepare再编全量模块。3.2 cdc-acm 走串口的最小验证插上设备后看 ttyACM 与回环补丁装好、重新插拔设备先用 dmesg 找节点不要凭厂商文档里固定编号modprobe cdc_acm sleep 2 dmesg | grep tty ls -l /dev/ttyACM* stty -F /dev/ttyACM0 115200 raw -echostty把串口设置成 115200、raw、不回显是为了后续用 Python 发收一个字节做回环测试。如果设备不自环就用一根杜邦线把 TX/RX 短接import serial ser serial.Serial(/dev/ttyACM0, 115200, timeout1) ser.write(bhello-cdc\r\n) line ser.readline() print(line) ser.close()这段脚本用 pyserial 打开 ttyACM0写一行数据后读回同一串。回环读到hello-cdc说明 cdc_acm 数据通路完整。若 open() 报 Permission denied说明 udev 权限规则没覆盖该节点写入/etc/udev/rules.d/99-cdc.rulesSUBSYSTEMtty, ATTRS{idVendor}1234, MODE0666重新插拔后再测试。3.3 CDC-NCM_V2 网络节点的两行命令与链路状态验证NCM 驱动加载成功后系统里会出现 usb0或按接口序号递增。它本质是虚拟以太网网卡但链路状态与底层 NTB 协商相关先查 ethtool 信息再配 IPethtool -i usb0 ip link set usb0 up ip addr add 192.168.7.2/24 dev usb0 ping -c 3 192.168.7.1ethtool -i输出的 driver 一栏应当是 cdc_ncm如果显示 cdc_ether 或空说明描述符子类是经典 ECM 协议走的不是 NCM 路径。ip link set up不只是启用网卡它会触发驱动执行类似 NDIS 风格的 openNCM 驱动向设备发送 SET_NTB_FORMAT 请求。如果出现 usb0 但 ping 不通优先怀疑两点一是数据接口双方对 NTB 最大长度理解不一致二是设备端要求主机下发SET_CRC_MODE而 cdc_ncm 的校验被关闭。前者改设备端描述符后者用ethtool -K usb0 rx off关闭硬件校验后再试。4. CDC-NCM V2 的参数矩阵NTB 聚合、NDP16/32 对齐与网卡 offload 的取舍4.1 为什么 NCM 比 ECM 快一个 NTB 装多个 IP 包ECM 里每个 USB 传输块只封装一帧以太网数据小包多的场景下 USB 中断调度会吃掉大量开销。NCM 把多个网络帧塞进一个 NTBNCM Transfer Block用 NDPNCM Datagram Pointer表描述每帧在 NTB 里的偏移和长度。V2 补丁常见的增强是引入 NDP16 定长对齐、支持 64KB 以上 NTB以及可选的 NDP32 扩展。带来的好处是上行吞吐提高、CPU 中断减少代价是延迟增加、驱动内存占用变大。适配时关键看三组数值参数典型值作用dwNtbInMaxSize16384 / 32768入站 NTB 最大字节数决定驱动分配 rx 缓冲上限dwNtbOutMaxSize16384 / 32768出站 NTB 上限超过后按短包拆分wNdpInDivisor / wNdpInPayloadRemainder4 / 0NDP 表地址对齐约束不匹配时驱动报错4.2 cdc_ncm 运行期可调参数sysfs 与 ethtool 的配合cdc_ncm 编译成模块时全局参数暴露在/sys/module/cdc_ncm/parameters下。最常调的是tx_timer_msecs它控制驱动把待发送数据攒进 NTB 的最大等待时长。# 查看当前发送聚合间隔 cat /sys/module/cdc_ncm/parameters/tx_timer_msecs # 缩短到 10ms降低小包场景的延迟 echo 10 /sys/module/cdc_ncm/parameters/tx_timer_msecs需要说明的是写 sysfs 只对之后创建的接口生效且很多发行版把模块参数列为只读。更稳妥的写法是放进/etc/modprobe.d/cdc_ncm.confoptions cdc_ncm tx_timer_msecs10这个参数取舍明显值大则驱动凑够整块 NTB 才发送大数据包流下吞吐高值小则小包响应更快但 USB 带宽利用率下降。弱网或高丢包链路上我会调到 40ms 换取更少重传开销。4.3 在 CDC-NCM_V2 网卡上调整 GRO 与 TSO直到吞吐不再抖动NCM 网卡走 USB 栈数据最终要过协议栈。cdc_ncm 驱动默认开启 GRO把多个网络帧合并成一个大 skb 交给协议栈降低 IP 层处理次数。但和部分模组的 NCM V2 实现搭配时GRO 会与 TCP 校验卸载产生冲突表现为 iperf3 单流吞吐上不去、双流反而正常。这时对比两张网卡的任务卸载ethtool -k usb0 | grep -E tcp-segmentation-offload|generic-receive-offload ethtool -K usb0 gro off iperf3 -c 192.168.7.1 -u -b 50M -l 1200 -t 10 # 观察丢包率变化再决定是否把 tso 一起关掉 ethtool -K usb0 tso offgro off关闭接收聚合tso off关闭 TCP 分段卸载。对 CDC-NCM 这种受 USB 带宽约束的“假网卡”TSO 是否开启只影响协议栈分解超大包时的 CPU 占用USB controller 本身有最大包长限制TSO 大包最终仍被 usbnet 按 16KB 切块切块逻辑与 NDP32 扩展冲突时dmesg 会出现连续descriptor too large报错。选参数遵循一个原则先保证传输稳定再追求吞吐。优先保证dwNtbInMaxSize不触发重新协商其次是关闭 GRO 提升小包稳定性最后才考虑 TSO/GSO 这些与 USB controller 兼容性相关的卸载能力。5. 用 usbmon 抓包验证 cdc-acm 与 cdc-ncm 的枚举时序核对你手头的 cdc.rar不管 cdc-acm 串口不出现还是 CDC-NCM_V2 网络口反复断开usbmon 抓包都是最能说明问题的验证手段。它直接从 usbcore 抓 URB 流转不依赖厂商日志绕过了驱动打印屏。启用方法modprobe usbmon mount -t debugfs none /sys/kernel/debugfs 2/dev/null mount -t debugfs none /sys/kernel/debug cat /sys/kernel/debug/usb/usbmon/1u /tmp/usbmon.cap # 重新插拔设备10 秒后 CtrlC sleep 10 kill %1 tshark -r /tmp/usbmon.cap -Y usb.capdata -T fields -e usb.bus_id -e usb.device_address -e usb.urb_type -e usb.capdata注意/sys/kernel/debug/usb/usbmon/1u里的 1 是 USB 总线号多控制器机器先ls /sys/kernel/debug/usb/usbmon再选cat 重定向输出要 root 权限普通用户会被 debugfs 挂载权限挡住。拿到抓包结果优先过滤 GET_DESCRIPTOR 阶段。一个正常枚举时序应该依次出现设备描述符、配置描述符、接口描述符然后紧跟着 CDC 功能描述符。如果接口描述符后面跳过了 CS_INTERFACE 类的功能描述符cdc_acm 和 cdc_ncm 都必然失败。只要在接口描述符里看到bInterfaceSubClass: NCM (0x0d)却没有任何 NTB 参数描述符说明设备端固件没有实现完整 NCM 协议补丁打再多也是白搭。最后一个小技巧在抓包里对照配置描述符的wTotalLength。如果主机上报的配置总长度比实际解析结果小说明设备上报长度和真实缓冲区不一致USB core 会在 get_descriptor 后因 short packet 中断枚举。这类问题在 cdc.rar 自带的 Windows 驱动下往往不爆因为 Windows 的 usbccgp 对描述符截断容忍度更高Linux usbcore 严格按上报长度解析同一个设备 Windows 能识别、Linux 枚举失败不代表 Linux 缺驱动先拿 usbmon 确认长度字段再对着描述符打补丁排障速度会快得多。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询