rk3588/kernel-6.1 USB 总线枚举、驱动匹配与数据传输分析 1. USB 子系统整体结构 rk3588/kernel-6.1 中 USB 主机侧代码主要位于:
drivers/usb/core/:USB core,总线、枚举、匹配、URB、hub、配置描述符解析;
drivers/usb/host/:Host Controller Driver,例如 xHCI/EHCI/OHCI;
drivers/usb/dwc3/:Synopsys DWC3 双角色控制器,RK3588 常见 USB3/DRD 控制器基础;
drivers/usb/gadget/:USB device/gadget 侧框架;
drivers/usb/class/:USB class 驱动;
drivers/usb/storage/:U 盘/USB mass storage;
drivers/usb/serial/:USB 串口;
drivers/usb/typec/、drivers/usb/roles/:Type-C 与 dual-role 切换;
drivers/phy/rockchip/:Rockchip USB2/USB3/Type-C PHY。
USB core 的 Makefile:
1 2 3 4 usbcore-y := usb.o hub.o hcd.o urb.o message.o driver.o usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o usbcore-y += devio.o notify.o generic.o quirks.o devices.o usbcore-y += phy.o port.o
核心职责:
hub.c:端口变化检测、设备复位、地址分配、读取描述符、枚举;
config.c:解析配置、接口、端点描述符;
driver.c:USB bus 注册、驱动注册、id_table 匹配、probe/disconnect;
urb.c:URB 提交前检查和通用入口;
hcd.c:URB 到 Host Controller Driver 的桥接;
message.c:同步 control/bulk 消息封装;
usb.c:USB core 初始化、设备模型、接口辅助函数;
endpoint.c / sysfs.c:端点和设备信息导出。
2. USB 主机侧分层模型 USB 主机侧从上到下可分为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 USB 设备功能驱动 例如 usb-storage、cdc-acm、usbhid、usb-skeleton | USB Core 枚举、匹配、配置、URB 管理、设备模型 | HCD 通用层 struct usb_hcd / struct hc_driver | Host Controller Driver xHCI/EHCI/OHCI/UHCI/DWC3 host glue | USB 控制器硬件 + PHY | USB 外设
对 RK3588 来说,常见主机路径是:
1 2 3 4 5 Rockchip USB PHY / Type-C / role switch -> DWC3 控制器 -> xHCI platform HCD -> USB core -> 具体 USB 设备驱动
drivers/usb/dwc3/core.c 中 dwc3_get_dr_mode()、dwc3_set_prtcap()、__dwc3_set_mode() 负责 DWC3 host/device/OTG 角色处理。drivers/usb/host/xhci-plat.c 负责 platform xHCI HCD 创建、DMA mask 设置、寄存器映射和 usb_add_hcd()。
3. USB 设备枚举流程 USB 枚举由 hub 负责。无论是 root hub 还是外接 hub,端口状态变化最终都会进入 drivers/usb/core/hub.c。
整体流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 端口 connect/change 中断 -> hub_irq() -> hub_event() -> hub_port_connect_change() -> hub_port_connect() -> usb_alloc_dev() -> choose_devnum() -> hub_port_init() -> usb_new_device() -> usb_enumerate_device() -> usb_get_configuration() -> device_add() -> driver core 匹配 USB 设备/接口驱动
3.1 端口连接处理:hub_port_connect() hub_port_connect() 是新设备枚举的主入口之一。
它做的事情:
如果端口已有旧设备,先 usb_disconnect();
对端口信号做 debounce;
分配 struct usb_device;
设置初始状态为 USB_STATE_POWERED;
调用 choose_devnum() 分配 USB 地址编号;
调用 hub_port_init() 复位设备并读取设备描述符;
把设备挂到父 hub 的 port_dev->child;
调用 usb_new_device() 继续枚举和注册。
关键路径:
1 2 3 4 5 6 hub_port_connect() -> usb_alloc_dev() -> usb_set_device_state(USB_STATE_POWERED) -> choose_devnum() -> hub_port_init() -> usb_new_device()
3.2 地址分配:choose_devnum() USB 总线上设备地址范围是 1-127。choose_devnum() 在 usb_bus.devmap 中找空闲地址:
1 2 3 find_next_zero_bit(bus->devmap.devicemap, 128, bus->devnum_next) -> set_bit(devnum, bus->devmap.devicemap) -> udev->devnum = devnum
地址释放在 release_devnum() 中完成。
3.3 端口复位和设备描述符读取:hub_port_init() hub_port_init() 是 USB 枚举中最复杂的阶段。
主要动作:
hub_port_reset() 复位端口,识别设备速度;
根据速度设置 ep0 max packet 初始值;
读取设备描述符前 8/64 字节,确认 bMaxPacketSize0;
hub_set_address() 发送标准 SET_ADDRESS 请求;
再读取完整 usb_device_descriptor;
处理 USB2/USB3 速度、quirk、延迟、链路状态等。
这个阶段设备从默认地址 0 切换到内核分配的地址,并进入 USB_STATE_ADDRESS。
3.4 读取配置与字符串:usb_enumerate_device() usb_new_device() 会调用:
1 2 3 4 5 6 7 usb_enumerate_device() -> usb_get_configuration() -> usb_cache_string(iProduct) -> usb_cache_string(iManufacturer) -> usb_cache_string(iSerialNumber) -> usb_enumerate_device_otg() -> usb_detect_interface_quirks()
usb_get_configuration() 会读取并解析:
configuration descriptor;
interface descriptor;
endpoint descriptor;
class/vendor-specific descriptor;
SuperSpeed companion descriptor。
解析完成后形成内核对象:
1 2 3 4 5 struct usb_device -> struct usb_host_config[] -> struct usb_interface[] -> struct usb_host_interface[] -> struct usb_host_endpoint[]
3.5 注册到设备模型:device_add() usb_new_device() 最后调用:
此时 USB 设备对 sysfs、udev、driver core 可见。 后续 generic USB 设备驱动通常会选择配置,创建 interface device,再触发接口驱动匹配。
4. 配置、接口和端点 USB 设备结构不是“一个设备只对应一个驱动”,而是:
1 2 3 4 5 6 7 8 9 Device Configuration Interface 0 Altsetting 0 Endpoint 1 IN Endpoint 2 OUT Interface 1 Altsetting 0 Endpoint 3 Interrupt IN
Linux USB 驱动多数绑定在 struct usb_interface 上,而不是整个 struct usb_device 上。
例如复合设备:
一个 USB 声卡可能有 audio control interface 和 audio streaming interface;
一个 4G 模块可能同时暴露 serial、network、diagnostic interface;
一个 USB 摄像头可能有 video control 和 video streaming interface。
每个 interface 可以匹配不同驱动。
关键结构在 include/linux/usb.h:
struct usb_device
struct usb_interface
struct usb_host_interface
struct usb_host_endpoint
struct usb_driver
struct urb
5. USB 驱动注册 USB 接口驱动通过 struct usb_driver 描述:
1 2 3 4 5 6 7 8 static struct usb_driver my_usb_driver = { .name = "my_usb" , .probe = my_probe, .disconnect = my_disconnect, .id_table = my_table, }; module_usb_driver(my_usb_driver);
module_usb_driver() 最终调用:
1 2 usb_register_driver() -> driver_register(&new_driver->drvwrap.driver)
usb_register_driver() 会设置:
1 2 3 new_driver->drvwrap.driver.bus = &usb_bus_type new_driver->drvwrap.driver.probe = usb_probe_interface new_driver->drvwrap.driver.remove = usb_unbind_interface
也就是说,驱动真正注册到 Linux driver core,挂在 usb_bus_type 总线上。
6. USB 驱动匹配机制 6.1 id_table USB 驱动通过 struct usb_device_id 表声明支持的设备或接口。
常见宏:
1 2 3 USB_DEVICE(vendor, product) USB_DEVICE_INFO(dev_class, dev_subclass, dev_protocol) USB_INTERFACE_INFO(intf_class, intf_subclass, intf_protocol)
示例:drivers/usb/usb-skeleton.c
1 2 3 4 5 static const struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } }; MODULE_DEVICE_TABLE(usb, skel_table);
MODULE_DEVICE_TABLE(usb, ...) 会导出模块别名,支持热插拔自动加载模块。
6.2 匹配入口:usb_device_match() drivers/usb/core/driver.c 中 usb_device_match() 是 USB bus 的 .match 回调。
它区分两类对象:
struct usb_device:匹配 usb_device_driver;
struct usb_interface:匹配普通 usb_driver。
接口驱动匹配路径:
1 2 3 4 usb_device_match() -> is_usb_interface(dev) -> usb_match_id(intf, usb_drv->id_table) -> usb_match_dynamic_id()
usb_match_id() 会遍历驱动的 id_table,调用 usb_match_one_id() 对比:
idVendor/idProduct;
bDeviceClass/bDeviceSubClass/bDeviceProtocol;
bInterfaceClass/bInterfaceSubClass/bInterfaceProtocol;
product revision 范围;
match_flags。
6.3 probe 调用:usb_probe_interface() 匹配成功后,driver core 调用 usb_probe_interface()。
它做的事情:
检查设备和 interface 是否 authorized;
再次用动态 ID 或 id_table 找匹配项;
runtime PM 唤醒设备;
设置 interface 状态为 USB_INTERFACE_BINDING;
处理 autosuspend、LPM、altsetting;
调用具体驱动的 driver->probe(intf, id);
成功后设置为 USB_INTERFACE_BOUND。
核心流程:
1 2 3 4 5 6 usb_probe_interface() -> usb_match_dynamic_id() -> usb_match_id() -> usb_autoresume_device() -> driver->probe(intf, id) -> USB_INTERFACE_BOUND
驱动自己的 probe() 通常会:
分配私有结构;
获取 usb_device 和 usb_interface;
解析 endpoint;
分配 URB 和 buffer;
注册字符设备、input 设备、netdev、scsi host 等上层对象;
usb_set_intfdata() 保存私有数据。
7. usb-skeleton.c 驱动示例 drivers/usb/usb-skeleton.c 是 USB 字符设备驱动模板。
它展示了完整接口驱动写法:
声明 usb_device_id:
1 2 3 4 static const struct usb_device_id skel_table [] = { { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } };
在 probe() 中查找 bulk endpoint;
分配 bulk in URB 和 buffer;
注册 USB 字符设备;
在 read() 中提交 bulk in URB;
在 write() 中提交 bulk out URB;
在 disconnect() 中 kill URB、注销设备、处理并发关闭。
读路径:
1 2 3 4 5 6 7 8 userspace read() -> skel_read() -> skel_do_read_io() -> usb_fill_bulk_urb() -> usb_submit_urb() -> skel_read_bulk_callback() -> wake_up_interruptible() -> copy_to_user()
写路径:
1 2 3 4 5 6 7 8 9 userspace write() -> skel_write() -> usb_alloc_urb() -> usb_alloc_coherent() -> usb_fill_bulk_urb() -> usb_anchor_urb() -> usb_submit_urb() -> skel_write_bulk_callback() -> usb_free_coherent()
8. USB 数据传输类型 USB 有四种传输类型:
类型
用途
Linux 常用接口
Control
枚举、标准请求、配置、少量控制命令
usb_control_msg() / control URB
Bulk
大量可靠数据,吞吐优先
usb_bulk_msg() / bulk URB
Interrupt
小数据、周期轮询、低延迟
interrupt URB
Isochronous
音视频实时流,带宽保证,不重传
isoc URB
端点 0 永远是 control endpoint。 其他 endpoint 来自配置描述符和接口 altsetting。
9. URB 数据传输模型 Linux USB 数据传输核心对象是 struct urb(USB Request Block)。
异步传输基本流程:
1 2 3 4 5 6 7 8 usb_alloc_urb() -> usb_fill_bulk_urb() / usb_fill_control_urb() / usb_fill_int_urb() -> usb_submit_urb() -> HCD 排队到硬件 -> 设备传输完成 -> HCD 中断处理 -> usb_hcd_giveback_urb() -> urb->complete()
URB 关键字段:
dev:目标 usb_device;
pipe:端点号、方向、传输类型;
transfer_buffer:数据 buffer;
transfer_buffer_length:请求长度;
actual_length:实际传输长度;
setup_packet:control 传输 setup 包;
complete:完成回调;
context:驱动私有上下文;
status:完成状态;
interval:interrupt/isoc 周期;
iso_frame_desc[]:isoc 分包描述。
10. usb_submit_urb() 到 HCD 的路径 drivers/usb/core/urb.c 中 usb_submit_urb() 做通用检查:
URB 和 complete 回调不能为空;
设备状态必须有效;
通过 pipe 找 endpoint;
检查 control setup packet;
检查 endpoint max packet;
检查 isoc packet 长度;
检查 SG 约束;
修正 interrupt/isoc interval;
调用 usb_hcd_submit_urb()。
路径:
1 2 3 4 usb_submit_urb() -> usb_pipe_endpoint() -> endpoint/type/length/interval 检查 -> usb_hcd_submit_urb()
drivers/usb/core/hcd.c 中 usb_hcd_submit_urb():
1 2 3 4 5 usb_hcd_submit_urb() -> usb_get_urb() -> usbmon_urb_submit() -> map_urb_for_dma() -> hcd->driver->urb_enqueue()
如果是 root hub,则走 rh_urb_enqueue() 模拟 root hub 响应。 普通设备则映射 DMA,并调用具体 HCD 的 urb_enqueue()。
11. xHCI 如何传输 URB RK3588 主机侧常见是 xHCI。include/linux/usb/hcd.h 中 struct hc_driver 定义 HCD 操作:
1 2 3 4 5 int (*urb_enqueue)(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);int (*urb_dequeue)(struct usb_hcd *hcd, struct urb *urb, int status);void (*endpoint_disable)(struct usb_hcd *hcd, struct usb_host_endpoint *ep);int (*address_device)(struct usb_hcd *hcd, struct usb_device *udev, unsigned int timeout_ms);int (*add_endpoint)(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep);
drivers/usb/host/xhci.c 的 xhci_hc_driver:
1 2 3 4 5 6 7 .map_urb_for_dma = xhci_map_urb_for_dma .unmap_urb_for_dma = xhci_unmap_urb_for_dma .urb_enqueue = xhci_urb_enqueue .urb_dequeue = xhci_urb_dequeue .address_device = xhci_address_device .add_endpoint = xhci_add_endpoint .check_bandwidth = xhci_check_bandwidth
xhci_urb_enqueue() 会根据 endpoint 类型进入不同队列函数:
1 2 3 4 control -> xhci_queue_ctrl_tx() bulk -> xhci_queue_bulk_tx() / SG interrupt -> xhci_queue_intr_tx() isochronous -> xhci_queue_isoc_tx_prepare()
xHCI 硬件用 TRB(Transfer Request Block)描述传输:
1 2 3 4 5 6 7 8 9 10 URB -> xHCI TD -> one or more TRB -> endpoint transfer ring -> doorbell -> hardware DMA -> event ring completion -> xhci_giveback_urb_in_irq() -> usb_hcd_giveback_urb() -> urb->complete()
xhci-ring.c 文件开头描述了 xHCI ring 的生产者/消费者模型:
software 往 command/endpoint ring 里放 TRB;
hardware 消费 endpoint ring;
hardware 往 event ring 里写 completion event;
software 消费 event ring;
doorbell 用来通知 hardware 有新 TRB。
12. 同步传输封装 USB core 提供同步接口,底层仍然使用 URB。
12.1 Control 传输 usb_control_msg() 位于 drivers/usb/core/message.c。
流程:
1 2 3 4 5 6 7 8 9 usb_control_msg() -> 分配 usb_ctrlrequest -> usb_internal_control_msg() -> usb_alloc_urb() -> usb_fill_control_urb() -> usb_start_wait_urb() -> usb_submit_urb() -> wait_for_completion_timeout() -> usb_kill_urb() on timeout
control 传输常用于:
GET_DESCRIPTOR
SET_ADDRESS
SET_CONFIGURATION
GET_STATUS
CLEAR_FEATURE
class/vendor command。
USB 枚举本身大量使用 control transfer。
12.2 Bulk 同步传输 usb_bulk_msg() 同样位于 message.c。
流程:
1 2 3 4 usb_bulk_msg() -> usb_alloc_urb() -> usb_fill_bulk_urb() 或 usb_fill_int_urb() -> usb_start_wait_urb()
适合简单、低并发场景。 高性能驱动更常使用异步 URB 队列保持吞吐。
13. USB DMA 与 buffer USB 主机控制器一般通过 DMA 访问 URB buffer。 驱动可以用普通 buffer,让 USB core/HCD 自动 DMA map:
1 2 3 buf = kmalloc(size, GFP_KERNEL); usb_fill_bulk_urb(urb, udev, pipe, buf, size, complete, ctx); usb_submit_urb(urb, GFP_KERNEL);
也可以使用 coherent buffer 并设置 URB_NO_TRANSFER_DMA_MAP:
1 2 3 4 buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &dma); urb->transfer_buffer = buf; urb->transfer_dma = dma; urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
usb-skeleton.c 的写路径使用了 usb_alloc_coherent(),减少反复 map/unmap 成本。
注意:
completion 回调可能在软中断上下文执行;
callback 中不能做长时间睡眠操作;
disconnect 中必须 usb_kill_urb() 或 usb_kill_anchored_urbs();
已提交 URB 的 buffer 在 complete 前不能释放;
usb_anchor 常用于批量管理多个未完成 URB。
14. 断开、解绑与错误处理 设备拔出时路径:
1 2 3 4 5 6 7 8 9 hub 发现 disconnect -> usb_disconnect() -> usb_set_device_state(USB_STATE_NOTATTACHED) -> hub_disconnect_children() -> usb_disable_device() -> device_del() -> driver core remove -> usb_unbind_interface() -> driver->disconnect()
usb_unbind_interface() 会:
设置 interface 为 USB_INTERFACE_UNBINDING;
终止接口上的 URB;
调用驱动 disconnect();
禁用 endpoint;
清理 runtime PM 状态。
驱动 disconnect() 要特别注意并发:
用户进程可能还持有 file descriptor;
read/write 可能正在等待 completion;
URB callback 可能正在或即将执行;
需要用 mutex、kref、anchor、waitqueue 保护生命周期。
usb-skeleton.c 使用:
kref 管理对象生命周期;
io_mutex 防止 disconnect 与 read/write 并发;
usb_anchor 管理提交中的 URB;
usb_kill_anchored_urbs() 停止所有写请求。
15. RK3588 平台相关点 RK3588 USB 主机侧通常涉及:
DWC3 控制器
drivers/usb/dwc3/core.c
支持 host/device/OTG/DRD;
通过 dr_mode、role switch、Type-C 状态决定角色;
host 模式下创建 xHCI 子设备。
xHCI platform HCD
drivers/usb/host/xhci-plat.c
设置 DMA mask;
创建 usb_hcd;
映射寄存器;
注册 USB2 roothub 和 USB3 roothub;
调用 usb_add_hcd() 后 USB core 开始管理 root hub。
Rockchip PHY
drivers/phy/rockchip/phy-rockchip-inno-usb2.c
drivers/phy/rockchip/phy-rockchip-inno-usb3.c
drivers/phy/rockchip/phy-rockchip-typec.c
drivers/phy/rockchip/phy-rockchip-usbdp.c
负责 USB2/USB3 物理层、Type-C/DP combo PHY 等。
Type-C / role switch
drivers/usb/typec/
drivers/usb/roles/
决定 USB-C 口是 host 还是 device。
因此,RK3588 上“插入 U 盘并枚举”的大致链路是:
1 2 3 4 5 6 7 8 9 10 11 Type-C/USB PHY 检测连接 -> DWC3 进入 host role -> xHCI HCD 管理 root hub -> hub.c 发现 port connect -> hub_port_connect() -> hub_port_init() -> usb_new_device() -> 读取 U 盘描述符 -> interface 匹配 usb-storage -> usb-storage 提交 bulk URB -> xHCI 转成 TRB,通过 DMA 传输数据
16. USB 数据传输示例:Bulk 读写 16.1 Bulk IN 设备到主机:
1 2 3 4 5 6 usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, bulk_in_ep), buf, len, complete, ctx); ret = usb_submit_urb(urb, GFP_KERNEL);
完成后:
1 2 3 4 complete callback -> urb->status -> urb->actual_length -> 处理 buf 中的数据
16.2 Bulk OUT 主机到设备:
1 2 3 4 5 6 usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, bulk_out_ep), buf, len, complete, ctx); ret = usb_submit_urb(urb, GFP_KERNEL);
高吞吐场景通常同时提交多个 URB,避免 endpoint 队列空闲。
17. USB 调试方法 常用日志:
1 2 3 4 dmesg | grep -i usb dmesg | grep -i xhci dmesg | grep -i dwc3 dmesg | grep -i phy
常用 sysfs:
1 2 3 4 /sys/bus/usb/devices/ /sys/bus/usb/drivers/ /sys/kernel/debug/usb/devices /sys/kernel/debug/usb/usbmon/
常用工具:
lsusb
lsusb -t
lsusb -v
usbmon
trace-cmd / ftrace
重要 trace/debug 方向:
枚举失败:看 hub.c 日志、device descriptor read、SET_ADDRESS、供电、PHY;
匹配失败:看 idVendor/idProduct、interface class、MODULE_DEVICE_TABLE;
数据失败:看 URB status、endpoint halt、短包、timeout;
高速不稳定:看 PHY、线缆、Type-C、供电、LPM、autosuspend;
吞吐不足:看 URB 队列深度、bulk max packet、xHCI event、DMA 映射。
18. 常见问题
设备能枚举但驱动不 probe
id_table 不匹配;
驱动匹配的是 interface class,但设备是 vendor-specific;
模块没有导出 MODULE_DEVICE_TABLE(usb, ...);
interface 已被其他驱动绑定。
读取 device descriptor 失败
PHY/供电/线缆问题;
端口 reset 失败;
设备 firmware 异常;
low/full/high/super speed 协商异常。
bulk 传输超时
endpoint 地址或方向错误;
设备没有准备好;
没有处理 stall,需要 usb_clear_halt();
disconnect 与 I/O 并发。
callback 后数据长度不对
检查 urb->actual_length;
IN 方向可能短包结束;
OUT 方向可能需要 URB_ZERO_PACKET;
isoc 要逐个检查 iso_frame_desc[]。
拔出设备后崩溃
URB buffer 提前释放;
callback 访问已释放私有结构;
disconnect 没有 kill URB;
file_operations 没有用 kref/mutex 保护。
19. 总结 USB 枚举、匹配和传输可以概括为三条主线:
枚举主线
1 2 3 4 5 6 hub 端口变化 -> reset -> SET_ADDRESS -> GET_DESCRIPTOR -> GET_CONFIGURATION -> device_add
驱动匹配主线
1 2 3 4 5 6 usb_register_driver() -> usb_bus_type -> usb_device_match() -> usb_match_id() -> usb_probe_interface() -> driver->probe()
数据传输主线
1 2 3 4 5 6 7 8 9 driver 构造 URB -> usb_submit_urb() -> usb_hcd_submit_urb() -> hcd->urb_enqueue() -> xHCI TRB/ring/doorbell -> hardware DMA -> event ring -> usb_hcd_giveback_urb() -> urb->complete()
一句话概括:USB core 负责发现设备、解析描述符并按 usb_device_id 绑定 interface 驱动;真正的数据收发由驱动提交 URB,USB core/HCD/xHCI 把 URB 转成硬件传输描述符并在完成后回调驱动。