2. USB数据传输模型:端点、管道、传输类型
USB数据传输模型是理解USB Gadget驱动的基础。与I²C、SPI等传统总线不同,USB采用主机主导的轮询式通信模型,所有数据传输均由主机发起。本章将深入剖析USB数据传输的核心概念:端点、管道以及四种传输类型。
2.1 核心概念:端点与管道
2.1.1 端点(Endpoint)
端点是USB设备中唯一可寻址的物理或逻辑实体,是主机与设备之间数据通信的终点。每个USB设备都有一个唯一的设备地址,而每个端点则在设备内部通过端点号进行区分。
- 方向性:端点分为IN端点(设备→主机)和OUT端点(主机→设备)。
- 端点0:每个USB设备必须包含端点0,它是一个双向的控制端点,用于设备枚举和配置。
- 端点描述符:描述端点的属性,包括端点地址、传输类型、最大包大小、轮询间隔等。
嵌入式要点:在Linux Gadget驱动中,端点通过
struct usb_ep 结构体表示。驱动开发者需要为每个功能(如串口、网卡)分配并配置相应的端点。
2.1.2 管道(Pipe)
管道是主机软件(USB驱动程序)与设备端点之间的逻辑连接。一个管道对应一个端点,并定义了数据传输的类型、方向和流控属性。管道是主机视角的概念,而端点是设备视角的概念。
| 概念 | 视角 | 描述 |
|---|---|---|
| 端点 | 设备 | 设备内部的数据收发实体,有唯一的端点号 |
| 管道 | 主机 | 主机驱动与设备端点之间的软件抽象连接 |
2.2 四种传输类型
USB规范定义了四种传输类型,以满足不同设备对带宽、延迟和可靠性的需求。每种传输类型都有其特定的应用场景和特性。
| 传输类型 | 应用场景 | 特点 | 典型设备 |
|---|---|---|---|
| 控制传输 | 设备枚举、配置命令 | 双向、可靠、带宽小、保证送达 | 所有USB设备(端点0) |
| 批量传输 | 大数据块传输 | 单向或双向、可靠、带宽大、延迟不保证 | U盘、USB网卡 |
| 中断传输 | 小数据量、周期性查询 | 单向、可靠、保证延迟、带宽小 | USB鼠标、键盘 |
| 同步传输 | 实时数据流 | 单向、带宽大、无重传、无保证 | USB摄像头、音频设备 |
2.2.1 控制传输(Control Transfer)
控制传输是USB中最基础、最重要的传输类型。它使用端点0,是设备枚举和配置的必经之路。控制传输由主机发起,包含一个或多个阶段:Setup阶段、Data阶段(可选)和Status阶段。
- Setup阶段:主机发送8字节的Setup包,包含请求类型、请求码、参数等信息。
- Data阶段:根据Setup包的方向,传输数据。
- Status阶段:用于确认传输完成,报告状态。
Gadget驱动关键点:在Linux Gadget中,控制传输的处理函数通过
ep0 的回调实现。驱动必须正确处理标准请求(如 GET_DESCRIPTOR、SET_CONFIGURATION)和类特定请求。
2.2.2 批量传输(Bulk Transfer)
批量传输用于传输大量数据,具有可靠但非实时的特点。USB协议通过错误检测和重传机制保证数据完整性。当USB总线空闲时,批量传输可以占用尽可能多的带宽;当总线繁忙时,它会被其他传输类型抢占带宽。
- 最大包大小:USB 2.0全速为64字节,高速为512字节。
- 典型应用:U盘(BOT协议)、USB网卡(ECM/RNDIS协议)。
- Gadget实现:在Linux中,批量端点通过
usb_ep_queue()提交URB请求。
2.2.3 中断传输(Interrupt Transfer)
中断传输并非硬件中断,而是主机以固定的轮询间隔查询设备是否有数据需要传输。它保证了最大延迟,适合小数据量的周期性通信。
- 轮询间隔:在端点描述符中定义,范围从1ms到255ms。
- 最大包大小:全速最大64字节,高速最大1024字节。
- 典型应用:HID设备(鼠标、键盘)、USB游戏手柄。
常见误区:中断传输不是由设备主动发起的!设备只能被动等待主机轮询。在Gadget驱动中,中断传输的“中断”含义是“保证延迟”,而非“异步通知”。
2.2.4 同步传输(Isochronous Transfer)
同步传输用于对时间敏感的实时数据流,如音频、视频。它不保证数据完整性(无重传机制),但保证带宽和固定延迟。
- 带宽预留:在USB帧(1ms)或微帧(125μs)中预留固定带宽。
- 无握手阶段:同步传输没有Status阶段,不进行ACK/NAK握手。
- 典型应用:USB摄像头(UVC协议)、USB音频设备(UAC协议)。
2.3 传输类型对比总结
| 特性 | 控制 | 批量 | 中断 | 同步 |
|---|---|---|---|---|
| 可靠性 | 高(有重传) | 高(有重传) | 高(有重传) | 低(无重传) |
| 带宽保证 | 无 | 无(尽力而为) | 有(固定延迟) | 有(固定带宽) |
| 延迟 | 不保证 | 不保证 | 保证(轮询间隔) | 保证(固定) |
| 数据方向 | 双向 | 单向 | 单向 | 单向 |
| 典型包大小 (HS) | 64字节 | 512字节 | 1024字节 | 1024字节 |
2.4 Linux Gadget中的端点配置示例
在Linux Gadget驱动中,端点配置通常在功能驱动(如 f_serial.c、f_ecm.c)的绑定函数中完成。以下是一个典型的批量端点配置代码片段:
/* 在功能驱动的 bind() 回调中 */
static int my_function_bind(struct usb_configuration *c, struct usb_function *f)
{
struct f_my_opts *opts = container_of(f, struct f_my_opts, func);
struct usb_composite_dev *cdev = c->cdev;
struct usb_ep *ep;
int ret;
/* 分配并配置批量IN端点 */
ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_in_desc);
if (!ep)
goto fail;
opts->bulk_in = ep;
ep->driver_data = opts; /* 关联私有数据 */
/* 分配并配置批量OUT端点 */
ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_out_desc);
if (!ep)
goto fail;
opts->bulk_out = ep;
ep->driver_data = opts;
/* 启用端点 */
ret = usb_ep_enable(opts->bulk_in);
if (ret)
goto fail;
ret = usb_ep_enable(opts->bulk_out);
if (ret)
goto fail;
return 0;
fail:
pr_err("my_function: endpoint allocation failed\n");
return -ENODEV;
}
驱动开发提示:
usb_ep_autoconfig() 是Gadget框架提供的便捷函数,它会根据端点描述符自动匹配可用的物理端点资源。开发者无需手动指定端点号,提高了驱动在不同硬件平台上的可移植性。
2.5 本章小结
- 端点是USB设备内部的数据收发实体,管道是主机侧的逻辑抽象。
- 四种传输类型各有侧重:控制用于配置,批量用于大数据,中断用于低延迟小数据,同步用于实时流。
- 在Linux Gadget驱动中,端点通过
struct usb_ep管理,使用usb_ep_queue()提交数据传输请求。 - 理解传输类型的选择直接影响Gadget功能的性能和可靠性,是驱动设计的关键决策点。