理解 Linux USB sysfs

七月 03, 2026 #linux #usb

lsusb 输出解读

以一个 QEMU 虚拟机的输出为例:

Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 002: ID 0627:0001 Adomax Technology Co., Ltd QEMU Tablet
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub

每行格式为 Bus XXX Device YYY: ID vvvv:pppp VendorName ProductName

字段含义
Bus 001USB 总线编号
Device 001该总线上的设备编号(按枚举顺序分配)
ID 1d6b:0002VID (Vendor ID,由 USB-IF 分配) + PID (Product ID,厂商自定义)
Linux Foundation .../usr/share/hwdata/usb.ids 查表得到的人类可读名称

以上三行表示两台设备:

lsusb -tv 树状拓扑

-t 显示层级拓扑,-v 显示详细信息:

/:  Bus 001.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/15p, 480M
    ID 1d6b:0002 Linux Foundation 2.0 root hub
    |__ Port 001: Dev 002, If 0, Class=Human Interface Device, Driver=usbhid, 480M
        ID 0627:0001 Adomax Technology Co., Ltd QEMU Tablet
/:  Bus 002.Port 001: Dev 001, Class=root_hub, Driver=xhci_hcd/15p, 5000M
    ID 1d6b:0003 Linux Foundation 3.0 root hub
字段含义
/USB 拓扑的根节点
Bus 001, Bus 002分别对应 USB 2.0 总线和 USB 3.0 总线
Port 001, Port ...物理端口号
Dev N设备编号
Class=root_hub设备类别(根集线器 / HID 等)
Driver=xhci_hcd/15p绑定驱动和下游端口数(15 个端口)
480M链路速率 480 Mbps (USB 2.0 High-Speed)
5000M链路速率 5 Gbps (USB 3.0 SuperSpeed)
|__缩进表示子设备,连接在本集线器的某个端口上
If 0Interface 0(一个设备可同时暴露多个接口)
Class=Human Interface Device接口类别为 HID(键盘/鼠标/触控类)
Driver=usbhid绑定驱动

sysfs 路径命名规则

设备在 /sys/bus/usb/devices/ 中的路径名由三部分构成:

<总线>-<端口路径>:<配置>.<接口>

bus-part(总线部分)

示例含义
usb1USB Bus 1 的虚拟根设备(唯一不带端口路径的特例)
usb2USB Bus 2 的虚拟根设备

port-path(端口路径部分)

端口路径从根集线器向下逐级编号,用 - 分隔第一个端口与总线,后续层级都用 .

示例含义
1-0Bus 1 上的根集线器本身(port 0 仅为占位符,非真实端口)
1-1Bus 1 根集线器的 Port 1
1-2Bus 1 根集线器的 Port 2
1-1.2Bus 1 → 根 Port 1 上的下游集线器 → 该集线器的 Port 2
1-1.2.3再延伸一级:Port 2 下级集线器的 Port 3
3-2.1.4Bus 3 → Port 2 → 集线器 Port 1 → 集线器 Port 4

规则概括: - 只出现一次,作为总线号与端口链的分隔符;0 仅用于根集线器作为设备自身被引用时。

config.interface(配置与接口部分)

USB 设备层次为:Device → Configuration → Interface → Endpoint。

示例含义
1-1设备级别(无配置/接口信息)
1-1:1.0设备,配置 1,接口 0
1-1:1.1同一设备的配置 1 下的接口 1(复合设备可多接口)
1-1:2.0同一设备的配置 2 下的接口 0(多配置设备)
1-0:1.0根集线器作为设备,配置 1,接口 0

综合示例

/sys/bus/usb/devices/ 中的一个多层拓扑可能长这样:

usb1                    ← Bus 1 根设备
1-0:1.0                 ← Bus 1 根集线器接口
1-1                     ← 根 Port 1 上的设备
1-1:1.0                 ← 该设备,配置 1 接口 0
1-1:1.1                 ← 该设备,配置 1 接口 1(复合设备)
1-1.2                   ← 该设备是个下游集线器,其 Port 2 上的设备
1-1.2:1.0
1-1.2.3                 ← 下游集线器 Port 3 上的设备
1-1.2.3:1.0

usb2                    ← Bus 2 根设备
2-0:1.0
2-1                     ← Bus 2 根 Port 1
2-1:1.0

端点(Endpoint)

端点是 USB 通信的最终收发单元,位于整个层次的最底层:

Device → Configuration → Interface → Endpoint

EP0:设备级的控制端点

EP0 属于设备,不属于任何接口。 每个 USB 设备强制拥有 EP0,在配置和接口建立之前就已存在。EP0 是双向控制端点,用于:

任何接口都不能声明或占用 EP0。

端点资源与分配

USB 端点地址由 4 位端点号 + 1 位方向组成,规范限制每个设备最多 16 个端点号(EP0 ~ EP15):

端点地址字节: [D7 = 方向: 1=IN, 0=OUT] [D3..D0 = 端点号]

EP1 OUT = 0x01    EP1 IN = 0x81
EP2 OUT = 0x02    EP2 IN = 0x82

扣除强制占用的 EP0,剩余 15 个端点号(EP1 ~ EP15),每个号可分配 IN 和 OUT 两个方向,因此单方向计最多 30 个端点(不含 EP0)。

端点号是设备级资源:一个端点号在给定配置内只能被一个接口独占,不能跨接口共用。

设备端点池:EP1 IN, EP1 OUT, EP2 IN, EP2 OUT, ..., EP15 IN, EP15 OUT
              │                   │
              │      Interface 0  │      Interface 1
              └───────────────────┴──────────────────────
                 各自领走端点,同一个端点号不会出现在两个接口里

注意 EP1 INEP1 OUT 虽然共用端点号 1,但是两个独立的端点,各占端点池的一个位置。

一个接口可以独占所有剩余端点(如一个复杂的 UVC 摄像头用掉 6~8 个),也可以多个接口分摊。总之是瓜分,不是共享。

端点类型

类型用途典型场景
Control设备配置/命令EP0(设备级)
Interrupt低延迟、小数据量HID 键盘/鼠标
Bulk大数据量、无延迟保证Mass Storage、串口
Isochronous固定带宽、允许丢包摄像头、音频

常见接口的端点使用

设备类型接口数端点
HID 键盘11 IN interrupt
HID 鼠标+键盘22 IN interrupt(各 1 个)
Mass Storage(U 盘)11 IN bulk + 1 OUT bulk
CDC ACM(串口)21 IN bulk + 1 OUT bulk + 1 IN interrupt(通知)
UVC 摄像头2~31 isochronous IN + alternate settings 的多组 isochronous 端点

多配置(Multiple Configurations)

USB 规范允许一个设备有多个配置(Configuration),但同一时刻只有一个配置生效,且互相排斥。

什么时候用多配置

场景配置 1配置 2
低功耗模式全功能(键盘+鼠标),耗电高仅键盘,耗电低
固件升级正常工作模式DFU (Device Firmware Upgrade)
多用途CDC ECM(网卡)其他功能
兼容性USB 3.0 全速降级 USB 2.0

实际上,99.9% 的设备只有一个配置

配置的选择与切换

设备插入后,内核自动遍历所有配置描述符,并默认激活配置 1。可以通过 sysfs 手动切换:

# 查看当前激活的配置
cat /sys/bus/usb/devices/1-1/bConfigurationValue
# → 1

# 查看设备支持几个配置
cat /sys/bus/usb/devices/1-1/bNumConfigurations
# → 2

# 切换到配置 2
echo 2 | sudo tee /sys/bus/usb/devices/1-1/bConfigurationValue

切换配置时内核会解绑旧配置下所有接口的驱动,重建新接口,再重新匹配驱动。

切换配置 ≠ 固件重枚举

更常见的场景是 usb_modeswitch 之类的工具,它不是切换配置,而是让设备固件在电气层面断开再重连,以新的 VID/PID 重新枚举:

插入设备 → 识别为 Mass Storage (PID=1001)
    ↓ usb_modeswitch 发送 SCSI/vendor 命令
设备断开 USB 连接
    ↓ 设备固件切换内部模式
设备重新上拉 D+ → 识别为 Modem/网卡 (PID=1002)
配置切换固件重枚举
VID/PID不变可不同
机制SetConfiguration()电气断开再重连
对主机而言同一设备两个独立的插入事件

xHCI 双总线架构

xHCI 控制器内部有两个独立的根集线器——USB 2.0 和 USB 3.0 各一个。一个物理端口从同一个插座同时引出两组引脚:

                     xHCI 控制器
                    ┌─────────────┐
                    │             │
   USB 2.0 总线     │  ┌───────┐  │     USB 3.0 总线
   (Bus 1, 480M)    │  │ 芯片  │  │     (Bus 2, 5000M)
       ↑            │  └───────┘  │          ↑
       │            │             │          │
       └────────────┼─────────────┼──────────┘
                 物理端口 (同一个物理插座)
              ┌───┴───┐
          D+/D-    SSTX+/SSRX+
        (USB 2.0)   (USB 3.0)

设备路由

插入设备类型使用引脚枚举到
USB 3.0/3.x (SuperSpeed)SSTX/SSRXBus 2 (USB 3.0 总线)
USB 2.0 (High/Full/Low Speed)D+/D-Bus 1 (USB 2.0 总线)

因此 1-12-1 会同时出现在 sysfs 中——它们是同一个物理端口的 USB 2.0 影子和 USB 3.0 影子,设备实际只出现在其中一条总线上,取决于插入设备的速度层级。

职责划分

职责
物理层D+/D- 和 SSTX/SSRX 是两组独立差分信号线
xHCI 控制器硬件内部两个根集线器,根据检测到的信号自动路由到对应总线
xhci_hcd 驱动为两个根集线器分别创建 usb1/usb2,管理端口映射和设备枚举
USB 核心 (usbcore)抽象层,统一设备模型与 sysfs 接口
设备驱动 (usbhid 等)不感知上述细节,只看到绑定到自己接口上的设备

为什么根集线器是 1-0 而非 1-0.1

根集线器的端口路径命名中,-0 是占位符——根集线器没有上游端口可以编号,所以用 0 表示它作为设备本身。

但引用根集线器上的端口时,路径直接从该端口号开始,根集线器本身不在路径中出现:

期望的写法(错误)实际写法原因
1-0.11-1- 之后直接是端口链,根集线器默认为起点
1-0.1.21-1.2同上,起点不编号
引用根集线器本身1-0这是唯一出现 0 的地方

下游集线器不需要 0,因为它们的设备路径已以自身端口号结束,不存在 N-M.n.0 这种写法。

速查表

sysfs 路径含义
usbN虚拟根设备,Bus N
N-0:1.0Bus N 根集线器,配置 1,接口 0
N-MBus N 根集线器 Port M 上的设备
N-M:1.0同上,配置 1,接口 0
N-M.NBus N → Port M 下游集线器 → Port N
N-M.N:1.0同上,配置 1,接口 0
N-M.N.O三层集线器嵌套
N-M:1.1同一配置下的第 2 个接口(复合设备)
N-M:2.0第 2 个配置的第 1 个接口(罕见)