Stub 物理设备导出与 URB 代理
1. 驱动注册
stub_main.c通过usb_register_device_driver()注册stub_driver。这是整设备usb_device_driver,不是普通usb_driver接口驱动,因此导出复合设备时会接管全部interface。
Hub被排除,不能把USB拓扑中的Hub作为普通设备导出。
2. busid允许表
match_busid是usbip-host driver属性,接受:
1 | add 1-2.3 |
固定表最多16项,busid buffer为32字节。每项状态区分OTHER、REMOV、ADDED、ALLOC,并保存interface计数、设备引用和shutdown标志。
usbip bind --busid的本质是:
- 将busid加入
match_busid; - unbind原interface drivers;
3.触发设备重新匹配到usbip-host。
解除导出后rebind属性促使原class driver重新探测。
3. Probe
stub_probe()检查busid表,未列入允许表的设备返回不匹配。成功后:
- 分配
stub_device; - 引用真实
usb_device; - 设置devid;
- 初始化
usbip_device状态、锁和EH; - 初始化
priv_init/priv_tx/priv_free及unlink链表; - 创建设备属性
usbip_status、usbip_sockfd和usbip_debug。
此时设备只是“可导出”,尚未有数据连接。
4. Socket移交
usbipd完成IMPORT协商后,把已连接TCP socket fd写入设备的usbip_sockfd:
1 | userspace fd |
写入-1请求断开。sysfs路径由sysfs_lock串行化,并检查设备当前状态,防止重复连接。
5. SUBMIT接收
stub_rx_loop()读取固定头并只接受CMD_SUBMIT/CMD_UNLINK。SUBMIT处理大致为:
1 | recv header |
支持SG时,大buffer可能拆成多个本地URB;stub_priv记录num_urbs和completed_urbs,所有分片完成后才形成一个远端RET_SUBMIT。
6. 控制传输修正
远端Host看到的地址、configuration和interface状态不能机械复制。stub_rx.c识别并修正部分standard control request:
CLEAR_FEATURE(ENDPOINT_HALT);SET_INTERFACE;SET_CONFIGURATION;- device reset相关请求。
这些操作需要调用服务端USB Core API更新真实设备状态,而不仅是向endpoint 0提交原始URB。
7. 完成发送
真实URB completion stub_complete()在不可睡眠上下文中:
- 聚合分片状态;
- 将请求从
priv_init移到priv_tx; - 唤醒
stub_tx_loop()。
TX线程组装RET_SUBMIT:
- header和status;
- IN方向actual data;
- ISO descriptor;
- SG或线性buffer。
发送结束后对象进入priv_free并统一释放,避免completion直接执行socket I/O和复杂释放。
8. UNLINK竞态
CMD_UNLINK按目标seqnum搜索仍在priv_init的请求并调用usb_unlink_urb()。目标可能:
- 仍在执行:异步取消,completion随后返回;
- 已完成但尚未发送:需要避免重复giveback;
- 已不在列表:返回对应错误状态。
unlink_tx和unlink_free保存RET_UNLINK,所有请求链表由priv_lock保护。
9. Disconnect
物理拔出、TCP错误或模块卸载触发EH:
- shutdown socket使RX/TX退出;
- stop并put线程;
- kill/cleanup未完成URB;
4.释放socket引用; - reset到AVAILABLE或标记ERROR;
- device remove时设置BYE并释放对象。
对客户端表现为远端USB设备断开,class driver必须能处理热拔插。
正在加载留言…