usb 驱动--1、usb 描述符
一、USB 描述符
USB 设备用 USB 描述符 来描述自己属性及用途. 所以设备端必须实现对应的描述符. 主机会在枚举此设备的时候根据设备实现的描述符去确定设备到底是一个什么样的设备、设备需要的总线资源、和设备的通讯方式等.
// ./include/uapi/linux/usb/ch9.h
#define USB_DT_DEVICE 0x01 // 设备描述符
#define USB_DT_CONFIG 0x02 // 配置描述符
#define USB_DT_STRING 0x03 // 字符串描述符
#define USB_DT_INTERFACE 0x04 // 接口描述符
#define USB_DT_ENDPOINT 0x05 // 端点描述符
#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE 0x0b // 接口关联描述符
......
1. 数据结构
1.1 设备描述符
每一个 USB 设备只有一个设备描述符,主要向主机说明设备类型、端点0最大包长、设备版本、配置数量等等。
struct usb_device_descriptor {
__u8 bLength; // 设备描述符 usb_device_descriptor 结构体的大小, 是固定值为 sizeof(struct usb_device_descriptor)
__u8 bDescriptorType; // 描述符类型编号, 设备描述符固定为 0x01
__le16 bcdUSB; // usb 的版本号, USB 1.0: 0x0100、USB 2.0: 0x0200
__u8 bDeviceClass; // 设备类, 0x08 表示 u 盘, 0xEF 表示复合设备
__u8 bDeviceSubClass; // 描述具体的设备类型, 如 0x01 表示音频设备
__u8 bDeviceProtocol; // bDeviceSubClass 的细分, 详细参考后面的表格
__u8 bMaxPacketSize0; // 端点0的最大包的大小
__le16 idVendor; // 厂商 ID
__le16 idProduct; // 设备 ID
__le16 bcdDevice; // 设备出厂编码
__u8 iManufacturer; // 厂商描述符字符串索引.索引到对应的字符串描述符. 为0则表示没有
__u8 iProduct; // 产品描述符字符串索引, 为0则表示没有
__u8 iSerialNumber; // 设备序列号字符串索引, 为0则表示没有
__u8 bNumConfigurations; // 可能的配置数量, 定义设备以当前速度支持的配置数量
} __attribute__ ((packed));
其中 bDeviceClass 、bDeviceSubClass 和 bDeviceProtocol 组合起来描述具体的设备的类型.
| bDeviceClass | bDeviceSubClass 值 | 子类别描述 | bDeviceProtocol 值 | 协议描述 |
|---|---|---|---|---|
| 设备未定义 (0x00) | 0x00 | 类别由接口指定 | 0x00 | 无协议 |
| 音频类 (0x01) | 0x00 | 不指定子类别 | 0x00 | 无协议 |
| 0x01 | 音频控制设备 | 0x00 | 无协议 | |
| 0x02 | 音频流传输设备 | 0x00 | 无协议 | |
| 0x03 | MIDI 流传输设备 | 0x00 | 无协议 | |
| 通信设备类 (0x02) | 0x00 | 不指定子类别 | 0x00 | 无协议 |
| 0x01 | 直接线路控制模型 | 0x00 | 无协议 | |
| 0x02 | 通用协议模型 | 0x00 | 无协议 | |
| 0x06 | 以太网网络设备 | 0x00 | 无协议 | |
| 人机接口设备 (HID, 0x03) | 0x00 | 无子类 | 0x00 | 无协议 |
| 0x01 | 键盘 | 0x01 | 键盘协议 | |
| 0x02 | 鼠标 | 0x02 | 鼠标协议 | |
| 物理设备类 (0x05) | 0x00 | 物理设备 | 0x00 | 无协议 |
| (无子类别和协议定义) | ||||
| 图像类 (0x06) | 0x00 | 图像控制设备 | 0x00 | 无协议 |
| 0x01 | 图像传输设备 | 0x00 | 无协议 | |
| (无其他子类和协议定义) | ||||
| 打印机类 (0x07) | 0x00 | 打印机设备 | 0x01 | 双向协议 |
| 0x02 - IEEE 1284.4 协议 | ||||
| 0x03 - IEEE 1284.4 兼容协议 | ||||
| 大容量存储类 (0x08) | 0x00 | 未定义 | 0x00 | 无协议 |
| 0x01 | 只读存储设备 (RBC) | 0x00 | 无协议 | |
| 0x02 | ATAPI 命令块设备 | 0x00 | 无协议 | |
| 0x04 | UFI(通用软盘接口设备) | 0x00 | 无协议 | |
| 0x05 | SFF-8070i | 0x00 | 无协议 | |
| 0x06 | SCSI 传输协议 | 0x50 | Bulk-Only Transport | |
| 集线器类 (0x09) | 0x00 | 集线器设备 | 0x00 | 全速集线器(FS) |
| 0x01 - 高速集线器(HS) | ||||
| 复合设备类 (0xEF) | 0x02 | 常规复合设备 | 0x01 | FCP (功能复合协议) |
| 应用特定类 (0xFE) | 0x01 | DFU(设备固件升级) | 0x01 | 设备固件升级协议 |
| 0x02 | IrDA 桥接设备 | 0x00 | 无协议 | |
| 0x03 | USB 测试和测量类设备 | 0x00 | 无协议 | |
| 供应商自定义类 (0xFF) | 0x00 | 供应商自定义设备 | 0x00 | 无协议 |
1.2 配置描述符
一个设备可以有多个配置描述符, 大部分 usb 只有一个配置描述符.
struct usb_config_descriptor {
__u8 bLength; // 配置描述符占用多少字节, 固定值
__u8 bDescriptorType; // 描述符的类型编号. 配置描述符固定为 USB_DT_CONFIG 0x02
__le16 wTotalLength; // 该配置中所有相关描述符的总长度,单位是字节。它不仅包括配置描述符本身的长度,还包括该配置下的所有接口描述符、端点描述符和其他可能的描述符(例如 HID 描述符)的长度总和
__u8 bNumInterfaces; // 该配置下的接口描述符的数量
__u8 bConfigurationValue; // 对应配置的编号, 不同的配置拥有不同的编号, 第一个为1 , 第二个为2. 通过这个编号选择不同的配置
__u8 iConfiguration; // 对应的字符串描述符的编号, 通过这个编号拿到对应的字符串描述符.
__u8 bmAttributes; // 设备配置的电源特性和功能, 7位保留,始终为1. 6位是否支持自供电, 1自供电、5是否支持远程唤醒,1支持、4-0保留始终为0
__u8 bMaxPower; // 设备从总线提取的最大电流
} __attribute__ ((packed));
1.3 接口描述符
struct usb_interface_descriptor {
__u8 bLength; // 接口描述符的长度
__u8 bDescriptorType; // 描述符类型编号, 固定为 USB_DT_INTERFACE 0x04
__u8 bInterfaceNumber; // 接口编号
__u8 bAlternateSetting; // 备用的接口编号
__u8 bNumEndpoints; // 该接口使用的端点数量
__u8 bInterfaceClass; // 接口类别, 即前面的设备描述中的设备类型的具体细分
__u8 bInterfaceSubClass; // 描述具体的设备类型, 如 0x01 表示音频设备
__u8 bInterfaceProtocol; // 进一步细分接口类型
__u8 iInterface; // 描述该接口的字符串描述符索引
} __attribute__ ((packed));
主要用于描述复合设备中的接口关联.
struct usb_interface_assoc_descriptor {
__u8 bLength; // 接口关联描述符的长度
__u8 bDescriptorType; // 描述符型固定为 USB_INTERFACE_ASSOCIATION_DESCRIPTOR_TYPE 0x0b
__u8 bFirstInterface; // 第一个接口编号
__u8 bInterfaceCount; // 接口总数
__u8 bFunctionClass; // 定义该接口关联描述符的功能类别,帮助主机识别设备的功能类别。对于复合设备,bFunctionClass 描述了整个关联接口的类别,让主机能够加载相应的驱动程序
__u8 bFunctionSubClass; // 功能的子类
__u8 bFunctionProtocol; // 定义该功能的协议
__u8 iFunction; // 接口关联描述符的字符串描述符索引
} __attribute__ ((packed));
1.4 端点描述符
struct usb_endpoint_descriptor {
__u8 bLength; // 端点描述符的长度
__u8 bDescriptorType; // 描述符的类型, 固定值 USB_DT_ENDPOIN 0x05
__u8 bEndpointAddress; // 端点地址, bit0-3 表示端点号, bit7表示方向, 1 输入, 0 输出
__u8 bmAttributes; // 端点的传输类型 bit0-1: 00, 控制, 01 同步, 02 批量, 03终端.
__le16 wMaxPacketSize; // 端点, 收发的最大包大小
__u8 bInterval; // 表示主机查询端点的时间间隔,以帧为单位
__u8 bRefresh; // 刷新频率,用于音频同步端点, 仅音频使用
__u8 bSynchAddress; // 指定同步端点的关联端点地址, 仅音频使用
} __attribute__ ((packed));
其中 bmAttributes 的详细说明如下
| 位位置 | 描述 | 值 | 含义 |
|---|---|---|---|
| 位 0-1 | 传输类型 | 00 | 控制传输(Control) |
| 01 | 同步传输(Isochronous) | ||
| 10 | 批量传输(Bulk) | ||
| 11 | 中断传输(Interrupt) | ||
| 位 2-3 | 同步类型(同步传输时有效) | 00 | 无同步 |
| 01 | 异步 | ||
| 10 | 自适应 | ||
| 11 | 同步 | ||
| 位 4-5 | 使用类型(同步传输时有效) | 00 | 数据端点 |
| 01 | 反馈端点 | ||
| 10 | 隐式反馈数据端点 | ||
| 11 | 保留 | ||
| 位 6-7 | 保留位 |
Linux 内核中的 USB Core 是 USB 驱动子系统的核心部分,负责管理整个 USB 子系统的初始化、配置、驱动程序的加载, 它包括 usb 设备驱动管理, USB 主机控制器驱动(Host Controller Driver, HCD) 、usb hub、USB Gadget、 urb数据传输、USB 设备驱动程序等服务.
1.5 usb_host_config
设备用来管理配置描述符, 一般情况一个设备只有一个配置描述符.
struct usb_host_config {
struct usb_config_descriptor desc; // 配置描述符
char *string;
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS]; // 关联描述符
struct usb_interface *interface[USB_MAXINTERFACES]; // 接口设备
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES]; // 接口描述符缓冲区, 该配置下的接口描述符保存在 intf_cache 中
unsigned char *extra;
int extralen;
};
1.6 usb_interface_cache
用来管理当前配置描述符下所有的接口信息, 使用了柔性数组的技巧, 在 altsetting 数组中保存管理接口描述符的结构体 usb_host_interface
struct usb_interface_cache {
unsigned num_altsetting; // altsetting 的数量, 等于接口的数量
struct kref ref;
struct usb_host_interface altsetting[0]; // 存放管理接口描述符的数据结构
1.7 usb_host_interface
用来管理单独的接口信息.
struct usb_host_interface {
struct usb_interface_descriptor desc; // 接口描述符
int extralen;
unsigned char *extra;
struct usb_host_endpoint *endpoint; // 保存管理端点描述符的数组
char *string;
};
1.8 usb_host_endpoint
用来管理单独的接口描述符.
2. 描述符的构建
usb core 中通过函数 usb_get_configuration 来构建描述符信息.
- 检查配置描述符 dev->descriptor.bNumConfigurations 的数量是否合理, 配置描述符只能 1-7 最多 8 个最少 1 个.
- 首先通过函数 usb_get_descriptor 获取配置描述符的信息, 以及该配置中所有相关描述符的总长度 desc->wTotalLength.
- 第二次通过第一次获取到的 desc->wTotalLength 长度, 再次通过 usb_get_descriptor 获取到所有的配置描述符, 保存到 dev->rawdescriptors[cfgno] 中, 这里面包含所有的描述符, 被称为原始描述符.
- 调用 usb_parse_configuration 解析原始描述符, 解析完之后就会构建出完整的设备描述符信息, 注意这里只构建描述符信息, 并不会创建对应的接口设备. 代码太长就不贴了, 解析完之后的数据如下图所示
补充说明: 内核中设备描述符的构建是自动的
第一个是 root hub, root hub 本身也是一个 usb 设备, 他是在 root hub 创建的时候自动调用时调用 usb_get_configuration 构建的描述符
第二种是连接在 hub 上的设备是在设备插入的时候由 hub 调用 usb_get_configuration 构建描述符. 后文叙述这个过程.

浙公网安备 33010602011771号