首页/目录/全部文章

全部文章

八个专题的源码、算法与协议笔记都在这里。

笔记列表

VHCI 虚拟主机控制器与导入路径

VHCI 虚拟主机控制器与导入路径

1. HCD注册

vhci_hcd.c为每个配置的controller创建一个vhci_hcd.N平台设备。Probe依次:

  1. usb_create_hcd()创建primary USB2 HCD;
  2. usb_add_hcd()注册USB2 Root Hub;
  3. usb_create_shared_hcd()创建USB3 shared HCD;
  4. usb_add_hcd()注册USB3 Root Hub;
  5. 第一个primary HCD创建sysfs控制属性。

它没有MMIO、IRQ和DMA硬件,所有端口状态及传输完成由软件维护。

2. HCD接口

vhci_hc_driver实现:

  • start/stop
  • urb_enqueue/urb_dequeue
  • hub_status_data/hub_control
  • bus_suspend/bus_resume
  • alloc_streams/free_streams
  • get_frame_number

get_frame_number()仍报告未实现;PM代码也有明确FIXME。依赖精确frame或真实Host调度的驱动需谨慎。

3. 虚拟Root Hub

每个USB2和USB3 Root Hub分别有VHCI_HC_PORTS个端口。port_status[]模拟:

  • connection/change;
  • enable/suspend/reset;
  • power;
  • low/full/high/super speed。

Attach成功调用rh_port_connect()设置连接与变化位,再调用usb_hcd_poll_rh_status()。USB Core的Hub线程随后走标准枚举,读取远端设备descriptor并创建本地usb_device

4. Attach sysfs

用户态向attach写四个整数:

1
port sockfd devid speed

内核:

  • 将全局port映射到controller、USB2/USB3 HCD和rhport;
  • 验证speed与Hub类型匹配;
  • sockfd_lookup()取得stream socket;
  • 创建vhci_rxvhci_tx线程;
  • 设置远端devid和连接状态;
  • 模拟Root Hub connect。

status/status.N输出hub类型、port、状态、speed、devid、sockfd和本地busid。源码特意不输出socket内核指针,避免地址泄漏。

5. URB enqueue

本地class driver提交URB后:

1
2
3
4
5
6
7
8
vhci_urb_enqueue
-> validate vdev/status/buffer
-> usb_hcd_link_urb_to_ep
-> allocate vhci_priv
-> seqnum = atomic_inc
-> urb->hcpriv = priv
-> add priv_tx
-> wake vhci_tx

TX线程从priv_tx取请求,组装CMD_SUBMIT并发送;成功后转入priv_rx等待完成。OUT数据随请求发送,IN只发送期望长度。

枚举阶段对地址0设备的SET_ADDRESS由VHCI本地完成,并将状态从VDEV_ST_NOTASSIGNED转为VDEV_ST_USED,该请求不上网;其它descriptor/control URB仍通过远端取得。

6. Completion

vhci_rx_loop()收到RET_SUBMIT后:

  1. 按seqnum从priv_rx找到vhci_priv
  2. 解包status、actual length和ISO结果;
  3. 接收IN payload;
  4. 清除urb->hcpriv并从endpoint unlink;
  5. usb_hcd_giveback_urb()

从class driver角度,这与真实HCD异步完成没有API区别,但完成时延由网络和服务端HCD共同决定。

7. URB dequeue

若请求尚在priv_tx、还未发出,可本地移除并立即giveback。若已在priv_rx,创建vhci_unlink

1
2
3
4
new unlink seqnum + target submit seqnum
-> unlink_tx
-> CMD_UNLINK
-> RET_UNLINK / RET_SUBMIT race resolution

必须同时处理“取消回复先到”和“原URB已完成”两种顺序,确保每个URB只giveback一次。

8. Detach和网络错误

detach写port号,触发VDEV_EVENT_DOWN。EH关闭socket、停止线程、将所有未完成URB以-ENODEV完成,清空unlink链表并模拟Root Hub disconnect。

网络错误不会透明重连。重新attach会产生一次新的USB枚举,原class device实例已经销毁。

9. 容量

默认1个controller、USB2和USB3 Root Hub各8口,nports共16个全局端口位置;设备按speed分配到对应Hub。Kconfig可扩大controller和每类Hub的端口数量,但会增加静态对象、sysfs输出和并发连接资源。

VUDC 虚拟设备控制器与 Gadget

VUDC 虚拟设备控制器与 Gadget

1. 用途

usbip-vudc不是把物理USB device反向导出,而是实现软件UDC,使Linux Gadget通过网络出现在远端VHCI上:

1
2
3
4
5
6
ConfigFS Function
-> Composite Gadget
-> UDC Core
-> usbip-vudc
-> TCP
-> remote vhci-hcd

2. 实例化

vudc_main.c以模块参数num决定实例数,默认1。模块创建usbip-vudc.N平台设备并由同名platform driver probe。

vudc_probe()初始化虚拟硬件后调用usb_add_gadget_udc(),因此实例出现在:

1
/sys/class/udc/usbip-vudc.0

它不依赖RK3588 DWC3 UDC或USB PHY,也不会从物理Type-C端口输出信号。

3. Gadget对象

vudc内嵌usb_gadget,最大速度为High Speed;它提供EP0、15个IN和15个OUT endpoint,共31个虚拟endpoint。对象还维护:

  • 当前usb_gadget_driver
  • EP0及多个vep
  • address、device status和设备descriptor缓存;
  • pullup/connected状态;
  • 收到的Host URB队列;
  • completion TX队列;
  • 传输timer。

vep实现usb_ep,保存endpoint descriptor、type、halt/wedge及vrequest队列。

4. UDC Ops

VUDC实现标准Gadget操作:

  • endpoint enable/disable;
  • request alloc/free;
  • queue/dequeue;
  • set_halt/set_wedge;
  • pullup;
  • udc_start/udc_stop;
  • get_frame及设备状态相关操作。

因此configfs/composite function无需感知后端是网络而非硬件UDC。

5. ConfigFS绑定

典型流程:

1
2
3
4
5
6
7
modprobe usbip-vudc num=1
mount -t configfs none /sys/kernel/config
cd /sys/kernel/config/usb_gadget
mkdir g1
# 配置descriptor、configuration和function
echo usbip-vudc.0 > g1/UDC
usbipd --device

本树tools/usb/usbip/vudc/vudc_server_example.sh给出ACM Gadget示例。

6. Host请求接收

v_rx_loop()接收远端VHCI的CMD_SUBMIT/CMD_UNLINK。SUBMIT被转换为urbp

  • 根据ep地址找到vep
  • 分配URB及buffer;
  • 验证transfer length和ISO packet数;
  • 接收OUT数据;
  • 放入urb_queue

EP0 standard request部分由VUDC处理,其余交给gadget driver的setup()

7. 请求撮合

Gadget function调用usb_ep_queue()提交vrequest;远端Host提交的是urbpvudc_transfer.c在相同endpoint上撮合二者:

1
Host URB buffer <-> Gadget request buffer

按照方向复制数据,更新两侧actual/status,并处理short packet、zero packet、halt、setup/data/status stage。

传输timer模拟frame推进。源码对周期带宽主要采用软件limit,并不等价于真实UDC的微帧调度。

8. 返回路径

完成的urbp包装为tx_item(TX_SUBMIT);取消结果为TX_UNLINKv_tx_loop()发送RET_SUBMIT/RET_UNLINK及IN payload、ISO descriptor。

Gadget request completion仍通过Gadget API回调function driver。

9. 描述符和用户态发现

VUDC sysfs提供:

  • usbip_sockfd
  • usbip_status
  • 二进制dev_desc

usbipd --device读取虚拟设备descriptor并响应远端DEVLIST/IMPORT。导入时busid是usbip-vudc.N,不是物理USB busid。

10. 限制

  • 仅模拟UDC语义,不模拟真实PHY/link power state;
  • VUDC的timer传输路径当前对isochronous URB直接返回-EXDEV,不能用作ISO Gadget后端;
  • 远端Host完全控制USB请求,Gadget function必须按不可信输入设计;
  • 不能同时把一个VUDC绑定给多个Gadget或多个网络连接;
  • 删除模块前必须解除ConfigFS Gadget和socket连接。

用户态工具、握手、Sysfs 与部署流程

用户态工具、握手、Sysfs 与部署流程

1. 工具组成

tools/usb/usbip使用Autotools构建,依赖libudev,可选TCP wrappers和hwdata。主要命令:

命令 作用
usbip list -l 列出本机USB及driver状态
usbip bind/unbind -b BUSID 绑定/解除usbip-host
usbip list -r HOST 查询服务端DEVLIST
usbip attach -r HOST -b BUSID 导入设备
usbip port 查看VHCI端口
usbip detach -p PORT 断开虚拟端口
usbipd 监听并响应导入请求
usbipd --device 导出VUDC Gadget

2. 管理协议

用户态协议公共头包含version、opcode和status。当前主要使用:

  • OP_REQ/REP_DEVLIST
  • OP_REQ/REP_IMPORT

源码仍定义DEVINFO、EXPORT、UNEXPORT、CRYPKEY等opcode,但usbipd主请求分发没有实现CRYPKEY;不能把定义存在视为已支持IPsec密钥协商。

3. 物理设备服务端

1
2
3
4
5
modprobe usbip-core
modprobe usbip-host
usbip list -l
usbip bind --busid 1-2
usbipd -D

绑定后原本的usb-storageusbhid等本地driver会被解除,该设备在服务端本地不可继续正常使用。

导出状态可检查:

1
2
3
readlink /sys/bus/usb/devices/1-2/driver
cat /sys/bus/usb/drivers/usbip-host/match_busid
cat /sys/bus/usb/devices/1-2/usbip_status

4. 客户端

1
2
3
4
5
6
modprobe usbip-core
modprobe vhci-hcd
usbip list --remote SERVER
usbip attach --remote SERVER --busid 1-2
usbip port
lsusb -t

Detach:

1
usbip detach --port 0

5. Import握手与FD移交

1
2
3
4
5
6
7
8
9
client usbip                 server usbipd
| OP_REQ_IMPORT(busid) |
|--------------------------->|
| OP_REP_IMPORT |
|<---------------------------|
| write "port fd devid speed"
| to vhci attach sysfs |
| write fd
| to usbip_sockfd

usbipd把accepted socket写给stub/VUDC;client工具把connected socket写给VHCI。内核通过sockfd_lookup()增加socket引用,用户态随后可关闭自己的fd。

这种设计把地址解析、监听和访问策略留在用户态,将URB数据转发留在内核。

6. 关键Sysfs

Stub driver:

1
2
/sys/bus/usb/drivers/usbip-host/match_busid
/sys/bus/usb/drivers/usbip-host/rebind

Stub device:

1
2
3
usbip_status
usbip_sockfd
usbip_debug

VHCI:

1
2
3
4
/sys/devices/platform/vhci_hcd.0/nports
/sys/devices/platform/vhci_hcd.0/attach
/sys/devices/platform/vhci_hcd.0/detach
/sys/devices/platform/vhci_hcd.0/status

VUDC:

1
2
3
/sys/devices/platform/usbip-vudc.0/usbip_status
/sys/devices/platform/usbip-vudc.0/usbip_sockfd
/sys/devices/platform/usbip-vudc.0/dev_desc

这些store属性要求管理权限。不要向它们手工写入来源不明的fd或未验证参数。

7. 服务管理

生产环境至少应:

  • 让usbipd使用受限service account和capability;
  • 只监听管理网络地址;
  • firewall限制TCP 3240源地址;
  • 禁止自动导出新插入设备;
  • 明确设备allowlist;
  • 网络中断时自动清理VHCI端口;
  • 记录bind、attach和detach审计日志。

README中的“关闭防火墙、禁用SELinux”仅是历史调试建议,不应作为产品部署方案。

8. VUDC流程

VUDC服务端不是usbip bind

  1. 创建ConfigFS Gadget;
  2. 绑定到usbip-vudc.N
  3. 启动usbipd --device
  4. 客户端按busid usbip-vudc.N导入。

未绑定Gadget时,VUDC没有可供远端枚举的有效设备descriptor。

并发、队列、生命周期与错误恢复

并发、队列、生命周期与错误恢复

1. 执行上下文

每条活动USB/IP连接通常有:

  • 一个RX kthread;
  • 一个TX kthread;
  • 共享的全局单线程event workqueue;
  • Stub真实URB completion或VHCI giveback上下文;
  • sysfs管理进程上下文;
  • VUDC额外transfer timer。

Socket收发可睡眠,只在kthread中执行。真实URB completion仅移动链表并唤醒TX。

2. 公共同步

usbip_device

  • lock:status、event等短临界区;
  • sysfs_lock:连接建立/拆除与EH串行;
  • eh_waitq:等待event处理完成;
  • socket shutdown:唤醒阻塞中的RX/TX。

Event list由全局spinlock保护,workqueue对同一设备的多个事件合并。

3. Stub队列状态

一次SUBMIT对应的stub_priv始终位于:

1
priv_init -> priv_tx -> priv_free
  • priv_init:真实URB尚未全部完成;
  • priv_tx:可发送RET_SUBMIT;
  • priv_free:网络结果已发送,可释放。

UNLINK另有unlink_txunlink_free。所有链表由priv_lock保护,TX waitqueue由completion和unlink处理唤醒。

Scatter-gather拆分时,多个真实URB共享一个stub_priv。只有completed_urbs == num_urbs才能进入TX,聚合status必须保留首个有效错误。

4. VHCI队列状态

1
2
3
4
5
6
URB enqueue
-> priv_tx
-> TCP发送成功
-> priv_rx
-> RET_SUBMIT
-> giveback

urb->hcpriv指向vhci_priv,seqnum是网络请求主键。priv_lock保护submit和unlink链表,vhci->lock同时保护HCD endpoint链接、port status和设备连接状态。

锁内不能执行可能睡眠的socket操作;giveback也常需要临时释放全局锁,防止completion回调重入HCD。

5. Unlink状态机

URB取消与正常完成存在天然竞态:

1
2
3
CMD_UNLINK ----\
+-- 哪个先到均可能
RET_SUBMIT -----/

实现需要满足:

  • 一个URB只完成一次;
  • 目标seqnum找不到时不释放未知对象;
  • RET_UNLINK不等价于原URB一定没有RET_SUBMIT;
  • disconnect清理覆盖所有submit/unlink列表。

VHCI分别维护unlink_tx/unlink_rx;Stub在收到取消后调用usb_unlink_urb()并排队RET_UNLINK。

6. Event优先级

EH顺序固定:

  1. SHUTDOWN
  2. RESET
  3. UNUSABLE

原因是必须先停止socket线程和新I/O,才能安全清理URB及改变状态。BYE用于对象remove,阻止再加入新event。

7. 常见错误入口

  • usbip_recv()得到0:对端关闭;
  • send/recv返回负值:TCP错误;
  • header command非法:协议错误;
  • buffer/URB分配失败:malloc event;
  • usb_submit_urb()失败:submit event;
  • USB物理设备remove:removed event;
  • sysfs写入无效fd、非stream socket或已占用端口:立即返回错误。

这些错误通常会拆除整条设备连接,而不是只丢弃单个PDU,避免字节流失去消息边界后继续解析。

8. 生命周期规则

正确销毁顺序:

1
2
3
4
5
6
7
8
9
block new sysfs operations
-> shutdown socket
-> stop RX/TX
-> kill/giveback pending URBs
-> clear queues
-> drop socket/device references
-> reset/disconnect port
-> stop EH
-> free object

线程由kthread_create后额外get_task_struct(),退出时需kthread_stop_put()成对释放。

9. 内存与输入验证

网络头控制URB数量、transfer buffer和ISO descriptor分配,是高风险路径。已有保护包括:

  • ISO packet上限;
  • 负数检查;
  • endpoint和direction检查;
  • actual length不得超过transfer buffer;
  • VUDC按max packet估算packet上限;
  • 无效command触发连接错误。

审计时仍应重点检查整数乘法溢出、SG拆分、short read、异常actual length和disconnect并发。

10. PM语义

VHCI的bus suspend/resume仅改变软件HCD状态,不能保证远端物理设备、电源域或网络连接同步休眠。系统休眠前最好显式detach;否则恢复后TCP可能超时,最终表现为热拔插。

安全、性能、调试与 RK3588 适用性

安全、性能、调试与 RK3588 适用性

1. 安全模型

USB/IP等价于允许远端向本机热插USB设备。攻击面包括:

  • USB Core descriptor和Hub枚举解析;
  • HID、Storage、Audio、Video、网络等class driver;
  • 文件系统和SCSI协议;
  • Gadget function setup/data处理;
  • USB/IP PDU长度、ISO和unlink状态机;
  • usbipd管理协议与sysfs fd移交。

网络来源可信并不代表USB设备内容可信。

2. 协议不提供的能力

本树USB/IP没有:

  • 对端身份认证;
  • PDU加密;
  • 消息完整性MAC;
  • 细粒度设备访问授权;
  • 有效的CRYPKEY协商实现。

TCP 3240不应暴露到互联网或普通办公网。可通过WireGuard/IPsec/VPN提供加密和双方身份认证,但隧道不能修复USB class parser漏洞。

另外,数据面transfer_buffer_length没有统一的协议级上限。Stub会据远端长度尝试分配buffer或SG;异常的大值通常以分配失败和连接拆除结束,但仍可造成内存压力/拒绝服务,因此网络访问控制不能由内核字段校验替代。

3. 权限控制

建议:

  • 默认不构建USBIP_HOST/VHCI/VUDC
  • 仅特定管理镜像按模块提供;
  • usbipd只监听专用管理接口;
  • firewall采用源地址allowlist;
  • 只允许固定VID/PID/serial/busid导出;
  • 禁止导出键盘、网卡、可写存储等高风险class;
  • 限制谁能写usbip sysfs和加载模块;
  • SELinux/AppArmor限制usbipd、configfs和设备节点;
  • 客户端结合USB authorization或USBGuard再做本地策略。

busid本身会随拓扑变化,不能作为强设备身份。

4. 性能路径

一次传输包含:

1
2
3
4
5
6
class buffer
-> VHCI PDU/网络发送
-> TCP/IP
-> Stub接收/URB buffer
-> 真实HCD DMA
-> 反向完成

主要开销:

  • TCP往返时延;
  • 两端线程调度;
  • payload拷贝和SG转换;
  • 网络拥塞和重传;
  • 单连接TCP队头阻塞;
  • 服务端真实USB总线带宽。

TCP_NODELAY降低小控制包等待,但可能增加包率。Jumbo frame、offload和更快CPU可改善吞吐,不能消除RTT。

5. 传输类型

  • Control:枚举大量串行往返,长RTT明显拖慢probe;
  • Bulk:适合存储/打印,但吞吐受TCP窗口和拷贝限制;
  • Interrupt:功能可用,轮询完成延迟受网络抖动影响;
  • Isochronous:Stub路径支持descriptor,但TCP重传和队头阻塞不适合低抖动实时音视频;VUDC当前直接以-EXDEV拒绝ISO传输;
  • USB3 streams:VHCI暴露相关ops,但不应假设与真实xHCI完全等价。

6. RK3588

本树Rockchip defconfig没有CONFIG_USBIP_*。若需要:

1
2
3
4
5
6
7
CONFIG_NET=y
CONFIG_USB=y
CONFIG_USBIP_CORE=m
CONFIG_USBIP_HOST=m # 服务端物理设备
CONFIG_USBIP_VHCI_HCD=m # 客户端
CONFIG_USB_GADGET=y
CONFIG_USBIP_VUDC=m # 仅软件Gadget导出

RK3588有足够CPU和高速网络能力,但实际结果取决于GMAC/PCIe网卡、IRQ亲和、CPU频率、内存压力及USB端口拓扑。USB/IP客户端使用VHCI,不经过本机DWC3 PHY;物理设备服务端才受DWC3/EHCI/OHCI及VBUS影响。

7. 基础排障

1
2
3
4
5
6
7
8
zcat /proc/config.gz | grep USBIP
lsmod | grep -E 'usbip|vhci'
ss -tnlp | grep 3240
usbip list -l
usbip list -r SERVER
usbip port
lsusb -t
dmesg -w

服务端确认目标设备driver为usbip-host,客户端确认设备位于vhci_hcd总线。

8. Sysfs与调试位

查看VHCI:

1
2
cat /sys/devices/platform/vhci_hcd.0/status
cat /sys/devices/platform/vhci_hcd.0/nports

usbip_debug_flag位定义在usbip_common.h,覆盖xmit、sysfs、URB、EH以及Stub/VHCI RX/TX。可写模块参数或设备usbip_debug属性;详细日志可能包含设备行为和大量数据元信息,量产不要持续开启。

编译CONFIG_USBIP_DEBUG=y会启用更多pr_debug,仍可能需要dynamic debug:

1
echo 'file drivers/usb/usbip/* +p' > /sys/kernel/debug/dynamic_debug/control

9. 抓包

可用tcpdump/Wireshark观察TCP 3240:

1
tcpdump -i eth0 -s 0 -w usbip.pcap tcp port 3240

由于协议明文,抓包能看到descriptor、control request和payload,这本身说明网络与pcap文件必须按敏感数据保护。

配合usbmon可以分别观察服务端真实USB URB和客户端VHCI侧行为,但时间戳来自不同主机,需先同步时钟。

10. 故障映射

现象 优先检查
remote list为空 usbipd、firewall、bind、match_busid
attach失败 VHCI空闲port、版本、socket、speed
枚举到一半断开 RTT、TCP reset、descriptor/control错误
服务端设备消失 正常:已被usbip-host接管
存储很慢 RTT、TCP窗口、网络丢包、BOT串行语义
音视频卡顿 ISO实时性与TCP队头阻塞
detach后URB挂起 RX/TX线程、EH、pending unlink列表

11. 产品结论

USB/IP适合受控网络中的实验室、远程维护、测试农场和低实时性外设共享。它不适合默认开放在量产RK3588设备上,也不应作为硬实时USB、可信启动输入设备或高安全域跨网桥接方案。

Linux 6.1 USB/IP 文档索引

Linux 6.1 USB/IP 文档索引

源码:

  • rk3588/kernel-6.1/drivers/usb/usbip/
  • rk3588/kernel-6.1/tools/usb/usbip/
文档 内容
USBIP架构与实现总览.md Stub、VHCI、VUDC与用户态总体分层
01-源码目录构建配置与对象模型.md 22个驱动文件、Kconfig、模块及核心对象
02-USBIP协议PDU与公共核心.md SUBMIT/UNLINK、字节序、ISO与事件处理
03-Stub物理设备导出与URB代理.md usbip-host绑定、收发和控制请求修正
04-VHCI虚拟主机控制器与导入路径.md 虚拟Root Hub、端口、URB发送和giveback
05-VUDC虚拟设备控制器与Gadget.md 虚拟UDC、endpoint/request与ConfigFS
06-用户态工具握手Sysfs与部署流程.md usbip/usbipd、TCP 3240及socket移交
07-并发队列生命周期与错误恢复.md RX/TX线程、链表、锁、unlink和EH
08-安全性能调试与RK3588适用性.md 信任边界、性能瓶颈、排障和构建状态

USB/IP把URB语义封装到TCP。物理设备导出路径是usbip-host ↔ vhci-hcd;软件Gadget导出路径是usbip-vudc ↔ vhci-hcd。本树Rockchip defconfig未启用任何CONFIG_USBIP_*,使用前必须重新配置并构建内核与用户态工具。

USB/IP 架构与实现总览

USB/IP 架构与实现总览

1. 目标

USB/IP不转发设备文件操作,而是在内核USB层转发URB。客户端看到正常USB设备并加载原有class driver;服务端把URB提交给真实HCD或虚拟UDC。

1
2
3
4
5
6
7
客户端                                             服务端
class driver 真实USB设备
│ URB │
usbcore 本地HCD
│ │
vhci-hcd ── CMD_SUBMIT/UNLINK ── TCP ── usbip-host(stub)
◄─ RET_SUBMIT/UNLINK ─────────

VUDC路径把服务端右侧替换为Gadget:

1
远端vhci-hcd ← TCP → usbip-vudc ← UDC Core ← composite/configfs function

2. 四个内核模块

模块 配置 角色
usbip-core USBIP_CORE PDU、socket辅助、事件处理、调试
usbip-host USBIP_HOST 绑定整台物理USB device并导出
vhci-hcd USBIP_VHCI_HCD 客户端虚拟USB2/USB3 HCD
usbip-vudc USBIP_VUDC 服务端虚拟UDC,导出软件Gadget

usbip-hostvhci-hcd依赖USB;VUDC依赖USB_GADGET。三者都依赖USBIP_CORE和网络栈。

3. 控制面与数据面

控制面位于用户态:

  1. usbip bind把busid加入stub匹配表并绑定设备;
  2. usbipd监听TCP 3240;
  3. client执行usbip attach,完成DEVLIST/IMPORT握手;
  4. 用户态把已连接socket fd写入内核sysfs;
  5. 内核接管socket,启动RX/TX线程。

数据面随后完全位于内核:

1
URB -> USB/IP PDU -> kernel socket -> PDU -> URB

用户态守护进程不逐包转发URB payload。

4. 设备模型位置

  • Stub注册usb_device_driver,以整设备粒度接管全部interface;
  • VHCI注册hc_driver,向USB Core提供软件Root Hub;
  • VUDC注册usb_gadget,向UDC Core提供虚拟endpoint;
  • 用户态通过sysfs传递socket和端口参数,不直接操作内核链表。

5. 协议阶段

连接先使用用户态操作协议完成设备发现和导入;成功后同一TCP连接切换为内核URB协议:

  • USBIP_CMD_SUBMIT / USBIP_RET_SUBMIT
  • USBIP_CMD_UNLINK / USBIP_RET_UNLINK

所有32-bit协议字段按网络大端传输,bulk/control/interrupt/isochronous都复用同一基本头。

6. 与RK3588硬件关系

USB/IP不是RK3588 DWC3/xHCI/EHCI的专用驱动。作为服务端导出物理设备时,它位于任意本地USB Host controller之上;作为客户端时,远端设备挂在VHCI软件总线上,不经过RK3588物理USB PHY。

因此RK3588差异主要是:

  • 真实设备插在哪个板级Host端口;
  • 网络链路吞吐、CPU和内存带宽;
  • 产品内核是否启用USB/IP;
  • 安全策略是否允许远端注入USB设备。

7. 关键限制

  • TCP带来队头阻塞,无法提供USB帧级实时性;
  • 网络断开等价于USB设备突然拔出;
  • 协议本身不提供可靠的身份认证或机密性;
  • 远端设备描述符和数据进入本地USB/class parser,信任边界显著扩大;
  • Hub不能按普通终端设备方式导出;
  • VHCI suspend/resume和frame number实现有限,不应按真实HCD能力推断。

USB 子系统架构与源码总览

USB 子系统架构与源码总览

1. Host侧

1
2
3
4
5
6
7
8
9
用户态 / block / tty / net / input / media

USB class/function drivers
│ usb_driver + usb_interface
drivers/usb/core
│ URB + usb_hcd/hc_driver
xHCI / EHCI / OHCI / DWC2

PHY / connector / device

USB device可有多个configuration,每个configuration有多个interface;Linux通常把driver绑定到interface,而不是整台device。

2. Gadget侧

1
2
3
4
5
ConfigFS / legacy gadget
-> composite + function
-> usb_gadget / usb_ep / usb_request
-> UDC(RK3588为DWC3 device)
-> Host

Host的URB与Gadget的usb_request是两套对向模型。

3. Type-C侧

Type-C/TCPM/PD决定:

  • attach/orientation;
  • source/sink;
  • power role;
  • data role;
    -PD contract;
    -alternate mode/mux。

USB role switch再通知DWC3进入host或device。Type-C不是USB数据协议栈本身。

4. 核心对象

  • usb_device:总线设备和地址;
  • usb_interface:驱动绑定单元;
  • usb_host_endpoint
  • urb
  • usb_driver / usb_device_driver
  • usb_hcd / hc_driver
  • usb_gadget / usb_ep / usb_request
  • typec_port / tcpm_port
  • usb_role_switch

5. RK3588

  • DWC3 DRD0:USB2 OTG + USB3/DP combo PHY;
  • RK3588非S还增加DRD1;
  • DWC3 host0:SuperSpeed host;
    -两组独立EHCI/OHCI USB2 host;
    -USB2 PHY0/1/2/3(依SoC变体);
    -USBDP PHY把高速lanes在USB3和DP间复用;
    -板级Type-C controller、VBUS regulator和extcon/role决定OTG行为。

6. 构建

rockchip_linux_defconfig列出了xHCI/EHCI/OHCI、DWC3/DWC2、Gadget/ConfigFS、Storage/UAS、Serial、usbmon以及TCPM/TCPCI/FUSB302/HUSB311;但该文件未显式列出顶层 CONFIG_USB=y。产品是否经过基础defconfig/fragment合并,必须以最终 .config为准。

7. 边界

Rockchip高速PHY实现主要位于 drivers/phy/rockchip/,不在 drivers/usb/phy/;后者是旧USB PHY框架及通用transceiver驱动。

rk3588/kernel-6.1 USB 总线枚举、驱动匹配与数据传输分析

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.cdwc3_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() 是新设备枚举的主入口之一。

它做的事情:

  1. 如果端口已有旧设备,先 usb_disconnect()
  2. 对端口信号做 debounce;
  3. 分配 struct usb_device
  4. 设置初始状态为 USB_STATE_POWERED
  5. 调用 choose_devnum() 分配 USB 地址编号;
  6. 调用 hub_port_init() 复位设备并读取设备描述符;
  7. 把设备挂到父 hub 的 port_dev->child
  8. 调用 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 枚举中最复杂的阶段。

主要动作:

  1. hub_port_reset() 复位端口,识别设备速度;
  2. 根据速度设置 ep0 max packet 初始值;
  3. 读取设备描述符前 8/64 字节,确认 bMaxPacketSize0
  4. hub_set_address() 发送标准 SET_ADDRESS 请求;
  5. 再读取完整 usb_device_descriptor
  6. 处理 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() 最后调用:

1
device_add(&udev->dev)

此时 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.cusb_device_match() 是 USB bus 的 .match 回调。

它区分两类对象:

  1. struct usb_device:匹配 usb_device_driver
  2. 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()

它做的事情:

  1. 检查设备和 interface 是否 authorized;
  2. 再次用动态 ID 或 id_table 找匹配项;
  3. runtime PM 唤醒设备;
  4. 设置 interface 状态为 USB_INTERFACE_BINDING
  5. 处理 autosuspend、LPM、altsetting;
  6. 调用具体驱动的 driver->probe(intf, id)
  7. 成功后设置为 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_deviceusb_interface
  • 解析 endpoint;
  • 分配 URB 和 buffer;
  • 注册字符设备、input 设备、netdev、scsi host 等上层对象;
  • usb_set_intfdata() 保存私有数据。

7. usb-skeleton.c 驱动示例

drivers/usb/usb-skeleton.c 是 USB 字符设备驱动模板。

它展示了完整接口驱动写法:

  1. 声明 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) },
{ }
};
  1. probe() 中查找 bulk endpoint;
  2. 分配 bulk in URB 和 buffer;
  3. 注册 USB 字符设备;
  4. read() 中提交 bulk in URB;
  5. write() 中提交 bulk out URB;
  6. 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.cusb_submit_urb() 做通用检查:

  1. URB 和 complete 回调不能为空;
  2. 设备状态必须有效;
  3. 通过 pipe 找 endpoint;
  4. 检查 control setup packet;
  5. 检查 endpoint max packet;
  6. 检查 isoc packet 长度;
  7. 检查 SG 约束;
  8. 修正 interrupt/isoc interval;
  9. 调用 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.cusb_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.hstruct 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.cxhci_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 主机侧通常涉及:

  1. DWC3 控制器

    • drivers/usb/dwc3/core.c
    • 支持 host/device/OTG/DRD;
    • 通过 dr_mode、role switch、Type-C 状态决定角色;
    • host 模式下创建 xHCI 子设备。
  2. xHCI platform HCD

    • drivers/usb/host/xhci-plat.c
    • 设置 DMA mask;
    • 创建 usb_hcd
    • 映射寄存器;
    • 注册 USB2 roothub 和 USB3 roothub;
    • 调用 usb_add_hcd() 后 USB core 开始管理 root hub。
  3. 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 等。
  4. 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 readSET_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. 常见问题

  1. 设备能枚举但驱动不 probe

    • id_table 不匹配;
    • 驱动匹配的是 interface class,但设备是 vendor-specific;
    • 模块没有导出 MODULE_DEVICE_TABLE(usb, ...)
    • interface 已被其他驱动绑定。
  2. 读取 device descriptor 失败

    • PHY/供电/线缆问题;
    • 端口 reset 失败;
    • 设备 firmware 异常;
    • low/full/high/super speed 协商异常。
  3. bulk 传输超时

    • endpoint 地址或方向错误;
    • 设备没有准备好;
    • 没有处理 stall,需要 usb_clear_halt()
    • disconnect 与 I/O 并发。
  4. callback 后数据长度不对

    • 检查 urb->actual_length
    • IN 方向可能短包结束;
    • OUT 方向可能需要 URB_ZERO_PACKET
    • isoc 要逐个检查 iso_frame_desc[]
  5. 拔出设备后崩溃

    • URB buffer 提前释放;
    • callback 访问已释放私有结构;
    • disconnect 没有 kill URB;
    • file_operations 没有用 kref/mutex 保护。

19. 总结

USB 枚举、匹配和传输可以概括为三条主线:

  1. 枚举主线
1
2
3
4
5
6
hub 端口变化
-> reset
-> SET_ADDRESS
-> GET_DESCRIPTOR
-> GET_CONFIGURATION
-> device_add
  1. 驱动匹配主线
1
2
3
4
5
6
usb_register_driver()
-> usb_bus_type
-> usb_device_match()
-> usb_match_id()
-> usb_probe_interface()
-> driver->probe()
  1. 数据传输主线
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 转成硬件传输描述符并在完成后回调驱动。

drivers/usb 源码目录与模块索引

drivers/usb 源码目录与模块索引

核心目录

目录 作用 RK3588相关性
core/ usbcore、Hub、枚举、URB、HCD、usbfs 核心
host/ xHCI/EHCI/OHCI/UHCI等 核心
dwc3/ USB3 DRD Host glue/Gadget 核心
dwc2/ USB2 OTG controller 配置启用,板级未必使用
gadget/ UDC、composite、functions OTG device模式
typec/ Type-C、PD、TCPM、UCSI、mux Type-C板型
roles/ USB role switch class DWC3 dual-role
common/ Host/Gadget共享工具 核心
phy/ legacy USB PHY framework 辅助

设备驱动

  • storage/:BOT、UAS、SCSI glue;
  • serial/:usbserial、FTDI、CP210x、PL2303、Option等;
  • class/:ACM、printer、WDM、TMC;
  • misc/:usbtest、hub、firmware loader等;
  • mon/:usbmon;
  • image/atm/:legacy设备。

其它控制器

cdns3/chipidea/musb/mtu3/renesas_usbhs/isp1760/c67x00/为其它IP,不是RK3588主路径。

虚拟与远程

  • usbip/:stub、VHCI、VUDC;
  • early/:early printk;
  • usb-skeleton.c:示例。

顶层Makefile

CONFIG_USB构建core、storage和misc;各HCD/IP/Gadget/Type-C按独立Kconfig裁剪。源码存在不能证明进入产品内核。

Defconfig重点

1
2
3
4
5
6
7
8
USB_OTG, USB_MON
USB_XHCI_HCD, USB_EHCI_HCD, USB_OHCI_HCD
USB_DWC3, USB_DWC2
USB_STORAGE, USB_UAS
USB_SERIAL及常见芯片
USB_GADGET, USB_CONFIGFS
CONFIGFS_ACM/MASS_STORAGE/F_FS/UVC
TYPEC_TCPM, TYPEC_TCPCI, HUSB311, FUSB302, DP_ALTMODE

该文件没有显式 CONFIG_USB=y;上述依赖Host USB的请求是否生效取决于构建时是否与基础配置/fragment合并,必须核对最终 .config。在USB和Gadget均有效时,DWC3 mode choice默认dual-role;OF simple glue默认跟随DWC3。

规模

本目录约839个文件。阅读应先core→host/DWC3→gadget/Type-C→具体class,避免按文件名线性阅读。