4. ACM Gadget驱动原理:CDC ACM协议、串口仿真模型、数据流与控制流分离

本章深入剖析Linux USB Gadget子系统中ACM(Abstract Control Model)功能的实现原理。理解CDC ACM协议规范、串口仿真模型以及数据流与控制流分离的设计思想,是开发稳定、高性能USB虚拟串口驱动的基础。我们将从协议标准出发,逐步拆解Gadget侧的软件架构与数据路径。

4.1 CDC ACM协议规范与Gadget实现

CDC(Communication Device Class)是USB-IF定义的用于通信设备的类规范。ACM(Abstract Control Model)是CDC下的一个子类,专门用于模拟传统串口(如RS-232)的控制信号与数据传输。在Gadget侧,驱动需要实现CDC ACM描述符集合,以向Host侧正确枚举为一个“通信设备”。

核心概念: CDC ACM设备在USB总线上表现为两个接口(Interface)的组合——通信接口(Communication Interface) 和 数据接口(Data Interface)。通信接口负责控制信号(如DTR、RTS、Break),数据接口负责纯数据收发。

4.1.1 描述符结构

一个标准的CDC ACM Gadget需要实现以下描述符层次:

描述符类型 数量 说明
设备描述符 1 bDeviceClass=0x02(通信设备),bDeviceSubClass=0x00
配置描述符 1 包含接口关联描述符(IAD)
接口关联描述符(IAD) 1 将通信接口与数据接口绑定为一个功能
通信接口描述符 1 bInterfaceClass=0x02,bInterfaceSubClass=0x02(ACM)
端点描述符(中断IN) 1 用于通知Host串口状态变化(如CTS、DSR)
数据接口描述符 1 bInterfaceClass=0x0A(数据接口)
端点描述符(批量IN) 1 Host→Device的数据接收端点
端点描述符(批量OUT) 1 Device→Host的数据发送端点
实现提示: 在Linux内核中,drivers/usb/gadget/function/u_serial.c 和 f_acm.c 提供了CDC ACM的通用实现。开发者通常通过配置 g_serial 或 g_acm_ms 模块来启用。

4.1.2 控制请求与通知

CDC ACM定义了一系列类特定请求(Class-Specific Requests)和通知(Notifications),用于管理串口参数与信号:

  • SET_LINE_CODING (0x20): Host设置波特率、数据位、停止位、校验方式。
  • GET_LINE_CODING (0x21): Host查询当前串口参数。
  • SET_CONTROL_LINE_STATE (0x22): Host设置DTR和RTS信号状态。
  • SERIAL_STATE (0x20): Device通过中断端点主动上报CTS、DSR、RI、DCD等信号变化。
/* 内核中定义的CDC ACM请求宏 */
#define USB_CDC_REQ_SET_LINE_CODING      0x20
#define USB_CDC_REQ_GET_LINE_CODING      0x21
#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22

/* 串口状态位图定义 */
#define USB_CDC_SERIAL_STATE_DCD     (1 << 0)
#define USB_CDC_SERIAL_STATE_DSR     (1 << 1)
#define USB_CDC_SERIAL_STATE_BREAK   (1 << 2)
#define USB_CDC_SERIAL_STATE_RING    (1 << 3)
#define USB_CDC_SERIAL_STATE_FRAMING (1 << 4)
#define USB_CDC_SERIAL_STATE_PARITY  (1 << 5)
#define USB_CDC_SERIAL_STATE_OVERRUN (1 << 6)

4.2 串口仿真模型

ACM Gadget的核心任务是在Gadget侧模拟一个完整的UART(通用异步收发器)行为。这包括数据收发、波特率管理、流控信号模拟以及线路编码处理。串口仿真模型将物理UART的硬件寄存器操作,映射为USB端点上的数据包与控制请求。

关键区别: 物理UART的波特率由硬件分频器决定,而ACM虚拟串口的“波特率”仅作为参数存储,实际数据传输速率由USB总线带宽决定。Host侧的应用程序看到的波特率设置,在Gadget侧通常被忽略或仅用于兼容性。

4.2.1 数据路径仿真

数据路径的仿真通过批量端点实现,与物理UART的TX/RX引脚对应:

  • 发送路径(Gadget→Host): Gadget将数据写入批量IN端点,Host通过批量OUT端点读取。对应物理UART的TXD引脚。
  • 接收路径(Host→Gadget): Host将数据写入批量OUT端点,Gadget通过批量IN端点读取。对应物理UART的RXD引脚。

4.2.2 控制信号仿真

控制信号通过中断端点和类特定请求实现仿真:

物理信号 仿真方式 方向
DTR (Data Terminal Ready) SET_CONTROL_LINE_STATE请求 Host→Device
RTS (Request To Send) SET_CONTROL_LINE_STATE请求 Host→Device
CTS (Clear To Send) SERIAL_STATE通知 Device→Host
DSR (Data Set Ready) SERIAL_STATE通知 Device→Host
DCD (Data Carrier Detect) SERIAL_STATE通知 Device→Host
RI (Ring Indicator) SERIAL_STATE通知 Device→Host
串口仿真模型:物理UART vs ACM虚拟串口 物理UART TXD 发送 RXD 接收 RTS/CTS DTR/DSR 波特率发生器 ACM虚拟串口 批量IN端点 批量OUT端点 中断IN端点 控制请求 line_coding结构体 物理UART的硬件信号被映射为USB端点与类特定请求,实现透明串口仿真

4.3 数据流与控制流分离

CDC ACM协议最核心的设计思想是数据流与控制流分离。这一分离体现在USB端点层面、驱动软件架构层面以及Host侧设备节点层面。分离设计使得大数据量传输不受控制信号交互的阻塞,提高了虚拟串口的吞吐效率。

4.3.1 端点层面的分离

在USB描述符中,数据流与控制流使用完全不同的端点类型:

流类型 端点类型 传输类型 典型最大包长 特点
数据流 批量端点 批量传输 512字节(HS)/ 64字节(FS) 保证数据完整性,无固定带宽
控制流 中断端点 中断传输 16字节 保证低延迟,定期轮询

4.3.2 软件架构中的分离

在Linux Gadget驱动中,数据流与控制流的分离通过不同的URB(USB Request Block)处理路径实现:

  • 数据流路径: 通过 usb_ep_queue() 提交批量URB,由 gserial_alloc_request() 管理缓冲区。数据在Gadget的TTY层与USB批量端点之间直接传递。
  • 控制流路径: 通过 usb_ep_queue() 提交中断URB,用于发送SERIAL_STATE通知。控制请求(如SET_LINE_CODING)通过 setup() 回调处理,不经过数据路径。
数据流与控制流分离架构 Host侧应用(如minicom、putty) Host USB驱动(cdc_acm) 数据流:批量端点 控制流:中断端点 + 控制请求 Gadget TTY层 (gserial) Gadget ACM控制层 (f_acm) 批量IN/OUT端点 中断IN端点 + EP0控制 数据流与控制流在USB端点层和驱动层完全分离,互不阻塞

4.3.3 分离设计的优势

性能优势: 数据流使用批量传输,可以利用USB带宽进行高速数据收发;控制流使用中断传输,保证串口状态变化的实时响应。两者互不干扰,即使大数据量传输时,控制信号(如Break、DCD变化)仍能及时传递。
调试技巧: 当遇到虚拟串口“卡死”或“无响应”时,应首先区分是数据流问题还是控制流问题。可以通过 usbmon 抓取USB总线数据,观察批量端点是否有数据交换,以及中断端点是否按时发送SERIAL_STATE通知。

4.4 本章小结

本章详细阐述了ACM Gadget驱动的三大核心原理:

  • CDC ACM协议: 定义了描述符结构、类特定请求和通知机制,是Gadget与Host通信的规范基础。
  • 串口仿真模型: 将物理UART的硬件信号映射为USB端点操作和参数结构体,实现透明串口仿真。
  • 数据流与控制流分离: 通过批量端点与中断端点/控制请求的分离设计,保证了虚拟串口的高吞吐与实时响应。