Stub 物理设备导出与 URB 代理

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
2
add 1-2.3
del 1-2.3

固定表最多16项,busid buffer为32字节。每项状态区分OTHER、REMOV、ADDED、ALLOC,并保存interface计数、设备引用和shutdown标志。

usbip bind --busid的本质是:

  1. 将busid加入match_busid
  2. 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_statususbip_sockfdusbip_debug

此时设备只是“可导出”,尚未有数据连接。

4. Socket移交

usbipd完成IMPORT协商后,把已连接TCP socket fd写入设备的usbip_sockfd

1
2
3
4
5
6
userspace fd
-> sockfd_lookup()
-> kernel socket引用
-> create stub_rx/stub_tx
-> status = SDEV_ST_USED
-> wake_up_process()

写入-1请求断开。sysfs路径由sysfs_lock串行化,并检查设备当前状态,防止重复连接。

5. SUBMIT接收

stub_rx_loop()读取固定头并只接受CMD_SUBMIT/CMD_UNLINK。SUBMIT处理大致为:

1
2
3
4
5
6
7
8
recv header
-> validate endpoint/type/length/ISO count
-> alloc stub_priv + urb(s) + buffer/SG
-> unpack URB fields
-> receive OUT payload/ISO descriptors
-> tweak control requests
-> add priv_init
-> usb_submit_urb()

支持SG时,大buffer可能拆成多个本地URB;stub_priv记录num_urbscompleted_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_txunlink_free保存RET_UNLINK,所有请求链表由priv_lock保护。

9. Disconnect

物理拔出、TCP错误或模块卸载触发EH:

  1. shutdown socket使RX/TX退出;
  2. stop并put线程;
  3. kill/cleanup未完成URB;
    4.释放socket引用;
  4. reset到AVAILABLE或标记ERROR;
  5. device remove时设置BYE并释放对象。

对客户端表现为远端USB设备断开,class driver必须能处理热拔插。

文章互动

阅读 --

留言

0 条留言

正在加载留言…