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主机驱动程序,都必须熟练掌握描述符的相关知识。

相关推荐
铜豌豆_Y17 小时前
【实用】GDB调试保姆级教程|常用操作|附笔记
linux·c语言·驱动开发·笔记·嵌入式
嵌入式×边缘AI:打怪升级日志20 小时前
USB协议详解:从物理连接到数据传输的完整解析
网络·学习·usb
星源~1 天前
VsCode-单片机开发环境配置指定编译器
ide·vscode·单片机·物联网·嵌入式
迷人的星空1 天前
用对这些常识,嵌入式文件系统少踩坑?
物联网·嵌入式
charlie1145141912 天前
现代嵌入式C++教程:C++98——从C向C++的演化(2)
c语言·开发语言·c++·学习·嵌入式·教程·现代c++
dulu~dulu2 天前
机器学习题目总结(一)
人工智能·神经网络·决策树·机器学习·学习笔记·线性模型·模型评估与选择
月光技术杂谈2 天前
Linux发展到6.0了,其在嵌入式应用中,实时性方面有没有一些改进?
linux·嵌入式·实时性
Just_Paranoid2 天前
【Settings】Android 常见外设检测机制
android·sd·usb·camera·keyboard·sim
四谎真好看4 天前
MySQL 学习笔记(进阶篇2)
笔记·学习·mysql·学习笔记