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));

    其中 bDeviceClassbDeviceSubClassbDeviceProtocol 组合起来描述具体的设备的类型.

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 hubUSB Gadgeturb数据传输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 来构建描述符信息.

  1. 检查配置描述符 dev->descriptor.bNumConfigurations 的数量是否合理, 配置描述符只能 1-7 最多 8 个最少 1 个.
  2. 首先通过函数 usb_get_descriptor 获取配置描述符的信息, 以及该配置中所有相关描述符的总长度 desc->wTotalLength.
  3. 第二次通过第一次获取到的 desc->wTotalLength 长度, 再次通过 usb_get_descriptor 获取到所有的配置描述符, 保存到 dev->rawdescriptors[cfgno] 中, 这里面包含所有的描述符, 被称为原始描述符.
  4. 调用 usb_parse_configuration 解析原始描述符, 解析完之后就会构建出完整的设备描述符信息, 注意这里只构建描述符信息, 并不会创建对应的接口设备. 代码太长就不贴了, 解析完之后的数据如下图所示

补充说明: 内核中设备描述符的构建是自动的

第一个是 root hub, root hub 本身也是一个 usb 设备, 他是在 root hub 创建的时候自动调用时调用 usb_get_configuration 构建的描述符
第二种是连接在 hub 上的设备是在设备插入的时候由 hub 调用 usb_get_configuration 构建描述符. 后文叙述这个过程.

posted @ 2024-11-19 19:20  Baron-z  阅读(192)  评论(0)    收藏  举报