USB描述符

USB描述符详解:设备的"身份证"和"能力说明书"

一、USB设备的多层次结构:一个身体,多重身份

核心概念:USB设备就像"变形金刚"

想象一下,一个USB设备可以像变形金刚 一样,在不同的场合展现不同的形态和能力。这种灵活性是通过USB的分层描述机制实现的。


二、生动的例子:理解"物理设备"与"逻辑功能"

例子1:4G上网卡------同一硬件,两种形态

详细解释

  • 第一次插入 :电脑识别为U盘

    • 为什么?设备激活了"配置1"(U盘模式)

    • 你可以拷贝驱动程序和安装文件

  • 安装驱动后再次插入 :电脑识别为上网卡

    • 驱动程序告诉设备:"切换到配置2"

    • 设备重新配置,变成网络设备

关键点

  • 两种配置不能同时激活,就像人不能同时是"司机"和"厨师"

  • 切换配置需要主机发送SET_CONFIGURATION命令

例子2:USB耳机------一个设备,多种功能

详细解释

  • 音频功能:处理声音的播放和录制

  • 按键控制:处理音量调节、静音按钮等

  • 为什么分开?因为不同的功能需要不同的驱动程序管理

关键点

  • 多个接口可以同时工作,就像耳机一边播放音乐一边响应按键

  • 每个接口是独立的"逻辑设备"


三、四层结构详解:从宏观到微观

结构层次关系

复制代码
USB设备(物理实体)
├── 设备描述符(我是谁)
│   └── 配置描述符(我有几种工作模式)
│       ├── 接口描述符(我能做什么功能1)
│       │   ├── 端点描述符(我的数据通道1)
│       │   └── 端点描述符(我的数据通道2)
│       └── 接口描述符(我能做什么功能2)
│           └── 端点描述符(我的数据通道3)
└── 字符串描述符(我的名字和说明)

第1层:设备描述符(Device Descriptor)

这是设备的"身份证",每个设备只有一份。

包含信息

  • 基础身份:厂商ID、产品ID、设备版本号

  • 能力信息:设备支持的配置数量

  • 通信基础:端点0的最大数据包大小(控制通道容量)

  • 可选信息:厂商名、产品名、序列号的字符串索引

现实类比

复制代码
设备描述符就像你的身份证:
姓名:XXX公司USB设备
身份证号:VID=0x1234, PID=0x5678
性别:USB2.0设备
住址:端点0最大包64字节

第2层:配置描述符(Configuration Descriptor)

这是设备的"工作模式说明书",一个设备可以有多个。

包含信息

  • 模式特性:该配置下的接口数量

  • 电源需求:供电方式(自供电/总线供电)、最大电流

  • 配置编号:用于选择该配置的编号

关键特性

  • 同一时间只能激活一个配置,就像你同一时间只能从事一份职业

  • bmAttributes字段:第6位=1表示自供电,第5位=1表示支持远程唤醒

  • bMaxPower字段:单位是2mA,值50表示需要100mA电流

第3层:接口描述符(Interface Descriptor)

这是设备的"功能说明书",一个配置下可以有多个接口。

包含信息

  • 功能分类:接口类型(音频、HID、大容量存储等)

  • 通道数量:该接口下的端点数量(不包括端点0)

  • 设置选项:备用设置(Alternate Setting)数量

重要概念

  1. 接口类(bInterfaceClass)

    • 0x08:大容量存储类(U盘)

    • 0x03:HID类(鼠标、键盘)

    • 0x01:音频类

    • 0x0E:视频类

  2. 备用设置(Alternate Setting)

    • 同一个接口可以有不同设置,比如不同的带宽要求

    • 默认设置编号为0,可以动态切换到其他设置

第4层:端点描述符(Endpoint Descriptor)

这是设备的"数据通道说明书",一个接口下有多个端点。

包含信息

  • 通道地址:端点号(0-15)+ 方向(IN/OUT)

  • 传输类型:批量、中断、同步

  • 通道容量:最大数据包大小

  • 查询频率:轮询间隔(对中断/同步端点)

端点地址编码

复制代码
端点地址 = (方向 << 7) | 端点号
例如:0x81 = 10000001B
      ↑      ↑
      方向IN  端点号1

三种端点的区别

端点类型 数据可靠性 时序要求 典型应用
批量端点 高(有重传) 无要求 U盘、打印机
中断端点 周期性查询 鼠标、键盘
同步端点 低(无重传) 固定间隔 摄像头、音响

特殊成员:端点0

  • 地位特殊:不通过端点描述符描述

  • 信息位置 :其最大包大小在设备描述符的bMaxPacketSize0字段

  • 功能固定:专门用于控制传输,所有设备都必须有


四、描述符的读取过程:主机如何了解设备

枚举过程中的描述符读取顺序

复制代码
1. 读取设备描述符(至少前8字节,获取端点0最大包大小)
2. 设置设备地址(SET_ADDRESS)
3. 再次读取完整设备描述符
4. 读取配置描述符(包括所有接口和端点描述符)
5. 根据接口类加载相应驱动程序
6. 驱动程序选择配置(SET_CONFIGURATION)

关键细节:配置描述符的"打包返回"

当主机请求配置描述符时,设备会一次性返回:

  1. 配置描述符本身(9字节)

  2. 所有接口描述符(每个9字节)

  3. 所有端点描述符(每个7字节)

  4. 可能还有其他类特定描述符

为什么打包 ?因为配置描述符的wTotalLength字段告诉主机这个配置的总长度。


五、实际数据结构示例

设备描述符实例(18字节)

cs 复制代码
// 假设一个USB鼠标的设备描述符
struct {
    bLength = 18,           // 描述符长度
    bDescriptorType = 1,    // 设备描述符类型
    bcdUSB = 0x0110,        // USB 1.1
    bDeviceClass = 0,       // 类在接口描述符中定义
    bDeviceSubClass = 0,
    bDeviceProtocol = 0,
    bMaxPacketSize0 = 8,    // 端点0最大包8字节
    idVendor = 0x1234,      // 厂商ID
    idProduct = 0x5678,     // 产品ID
    bcdDevice = 0x0100,     // 设备版本1.0
    iManufacturer = 1,      // 字符串描述符索引1
    iProduct = 2,           // 字符串描述符索引2
    iSerialNumber = 0,      // 无序列号
    bNumConfigurations = 1  // 只有一个配置
};

配置描述符实例(9字节)

cs 复制代码
struct {
    bLength = 9,
    bDescriptorType = 2,    // 配置描述符类型
    wTotalLength = 34,      // 配置总长度34字节
    bNumInterfaces = 1,     // 1个接口
    bConfigurationValue = 1,// 配置编号1
    iConfiguration = 0,     // 无配置字符串
    bmAttributes = 0xA0,    // 总线供电,支持远程唤醒
    bMaxPower = 50          // 100mA (50 * 2mA)
};

接口描述符实例(9字节)

cs 复制代码
struct {
    bLength = 9,
    bDescriptorType = 4,    // 接口描述符类型
    bInterfaceNumber = 0,   // 接口0
    bAlternateSetting = 0,  // 默认设置
    bNumEndpoints = 1,      // 1个端点(除了端点0)
    bInterfaceClass = 3,    // HID类
    bInterfaceSubClass = 1, // 引导接口
    bInterfaceProtocol = 2, // 鼠标协议
    iInterface = 0          // 无接口字符串
};

端点描述符实例(7字节)

cs 复制代码
struct {
    bLength = 7,
    bDescriptorType = 5,    // 端点描述符类型
    bEndpointAddress = 0x81,// IN端点,端点1
    bmAttributes = 0x03,    // 中断传输
    wMaxPacketSize = 4,     // 最大包4字节
    bInterval = 10          // 10ms轮询间隔
};

六、重要概念澄清和易错点

1. 配置 vs 接口:什么时候用哪个?

  • 选择配置:当需要完全改变设备的工作模式时(如U盘↔上网卡)

  • 选择接口:当需要启用/禁用某个功能时(如关闭耳机的麦克风)

2. 端点0的特殊性

  • 双向性:既是IN也是OUT端点

  • 无需描述符:特性在设备描述符中定义

  • 固定用途:专用于控制传输

3. 字符串描述符的可选性

  • 不是必须:设备可以没有字符串描述符

  • 索引机制 :其他描述符中的iManufacturer等字段是字符串索引

  • 多语言支持:字符串描述符可以支持多种语言

4. 描述符的"树形结构"特性

  • 不可跳跃:主机必须按层次理解设备

  • 自包含:每个描述符都有长度和类型字段

  • 扩展性:可以通过类特定描述符扩展功能


七、实践应用:如何查看真实设备的描述符

在Windows上可以使用USBView 工具,在Linux上可以使用lsusb -v命令查看设备的完整描述符。

示例输出解析:

cs 复制代码
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0        64
  idVendor           0x1234
  idProduct          0x5678
  bcdDevice            1.00
  iManufacturer           1 "Example Corp"
  iProduct                2 "USB Mouse"
  iSerial                 0
  bNumConfigurations      1

八、总结:描述符的重要性

USB描述符是USB系统的核心元数据,它们:

  1. 实现即插即用:主机通过读取描述符自动识别设备类型

  2. 提供灵活性:允许一个物理设备支持多种功能

  3. 保证兼容性:标准的描述符格式确保不同厂商设备的互操作性

  4. 支持扩展:类特定描述符允许定义新的设备类型

记忆口诀

cs 复制代码
设备描述符:我是谁(身份)
配置描述符:我能怎么工作(模式)
接口描述符:我能做什么(功能)
端点描述符:我怎么交换数据(通道)

理解描述符的结构和含义,是理解USB设备工作原理的关键。无论是开发USB设备固件,还是编写USB主机驱动程序,都必须熟练掌握描述符的相关知识。

相关推荐
charlie11451419117 小时前
嵌入式的现代C++教程——constexpr与设计技巧
开发语言·c++·笔记·单片机·学习·算法·嵌入式
REDcker1 天前
RTCP 刀尖点跟随技术详解
c++·机器人·操作系统·嵌入式·c·数控·机床
荆楚闲人1 天前
GPIO内部结构中的施密特触发器(肖特基触发器)作用及原理
嵌入式·gpio·施密特触发器·肖特基触发器
ベadvance courageouslyミ1 天前
嵌入式硬件基础
嵌入式硬件·51单片机·嵌入式·数码管·二极管
一枝小雨1 天前
【OTA专题】15 实现App后台无感下载固件
stm32·单片机·嵌入式·ota·bootloader
凉、介2 天前
深入 QEMU Guest Agent:虚拟机内外通信的隐形纽带
c语言·笔记·学习·嵌入式·虚拟化
hugerat2 天前
在AI的帮助下,用C++构造微型http server
linux·c++·人工智能·http·嵌入式·嵌入式linux
linweidong2 天前
AUTOSAR配置文件(ARXML)版本不一致时如何管理?
嵌入式·autosar
charlie1145141913 天前
嵌入式现代C++教程: 构造函数优化:初始化列表 vs 成员赋值
开发语言·c++·笔记·学习·嵌入式·现代c++
凉、介3 天前
SylixOS 中的 Unix Socket
服务器·c语言·笔记·学习·嵌入式·sylixos