USB/IP 协议

架构

USB/IP 协议采用服务器/客户端(server/client)架构。服务器导出(export)USB 设备,客户端导入(import)它们。导出 USB 设备的设备驱动程序运行在客户端机器上。

客户端可能会请求导出的 USB 设备列表。为了获取该列表,客户端会打开到服务器的 TCP/IP 连接,并通过该 TCP/IP 连接发送一个 OP_REQ_DEVLIST 数据包(因此实际的 OP_REQ_DEVLIST 在底层传输层可能会被分成一个或多个片段发送)。服务器发回列出导出 USB 设备的 OP_REP_DEVLIST 数据包。最后,关闭 TCP/IP 连接。

virtual host controller                                 usb host
     "client"                                           "server"
 (imports USB devices)                             (exports USB devices)
         |                                                 |
         |                  OP_REQ_DEVLIST                 |
         | ----------------------------------------------> |
         |                                                 |
         |                  OP_REP_DEVLIST                 |
         | <---------------------------------------------- |
         |                                                 |

一旦客户端知道了导出的 USB 设备列表,它就可以决定使用其中一个。首先,客户端打开到服务器的 TCP/IP 连接并发送一个 OP_REQ_IMPORT 数据包。服务器回复 OP_REP_IMPORT。如果导入成功,TCP/IP 连接将保持打开状态,并用于在客户端和服务器之间传输 URB 流量。客户端可以发送两种类型的数据包:用于提交 URB 的 USBIP_CMD_SUBMIT,以及用于取消链接先前提交的 URB 的 USBIP_CMD_UNLINK。服务器的响应分别可能是 USBIP_RET_SUBMIT 和 USBIP_RET_UNLINK。

virtual host controller                                 usb host
     "client"                                           "server"
 (imports USB devices)                             (exports USB devices)
         |                                                 |
         |                  OP_REQ_IMPORT                  |
         | ----------------------------------------------> |
         |                                                 |
         |                  OP_REP_IMPORT                  |
         | <---------------------------------------------- |
         |                                                 |
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = n)         |
         | ----------------------------------------------> |
         |                                                 |
         |            USBIP_RET_SUBMIT(seqnum = n)         |
         | <---------------------------------------------- |
         |                        .                        |
         |                        :                        |
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = m)         |
         | ----------------------------------------------> |
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = m+1)       |
         | ----------------------------------------------> |
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = m+2)       |
         | ----------------------------------------------> |
         |                                                 |
         |            USBIP_RET_SUBMIT(seqnum = m)         |
         | <---------------------------------------------- |
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = m+3)       |
         | ----------------------------------------------> |
         |                                                 |
         |            USBIP_RET_SUBMIT(seqnum = m+1)       |
         | <---------------------------------------------- |
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = m+4)       |
         | ----------------------------------------------> |
         |                                                 |
         |            USBIP_RET_SUBMIT(seqnum = m+2)       |
         | <---------------------------------------------- |
         |                        .                        |
         |                        :                        |

对于 UNLINK,请注意,在成功的 USBIP_RET_UNLINK 之后,被取消链接的 URB 提交将不会具有对应的 USBIP_RET_SUBMIT(这在 drivers/usb/usbip/stub_rx.c 的 stub_recv_cmd_unlink 函数中有解释)。

virtual host controller                                 usb host
     "client"                                           "server"
 (imports USB devices)                             (exports USB devices)
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = p)         |
         | ----------------------------------------------> |
         |                                                 |
         |               USBIP_CMD_UNLINK                  |
         |         (seqnum = p+1, unlink_seqnum = p)       |
         | ----------------------------------------------> |
         |                                                 |
         |               USBIP_RET_UNLINK                  |
         |        (seqnum = p+1, status = -ECONNRESET)     |
         | <---------------------------------------------- |
         |                                                 |
         |         Note: No USBIP_RET_SUBMIT(seqnum = p)   |
         | <--X---X---X---X---X---X---X---X---X---X---X--- |
         |                        .                        |
         |                        :                        |
         |                                                 |
         |            USBIP_CMD_SUBMIT(seqnum = q)         |
         | ----------------------------------------------> |
         |                                                 |
         |            USBIP_RET_SUBMIT(seqnum = q)         |
         | <---------------------------------------------- |
         |                                                 |
         |               USBIP_CMD_UNLINK                  |
         |         (seqnum = q+1, unlink_seqnum = q)       |
         | ----------------------------------------------> |
         |                                                 |
         |               USBIP_RET_UNLINK                  |
         |           (seqnum = q+1, status = 0)            |
         | <---------------------------------------------- |
         |                                                 |

各字段采用网络字节序(大端序),这意味着最高有效字节(MSB)存储在最低地址处。

协议版本

文档记录的 USBIP 版本是 v1.1.1。该版本在消息头中的二进制表示形式为 0x0111。

这在 tools/usb/usbip/configure.ac 中定义

消息格式

OP_REQ_DEVLIST

检索导出的 USB 设备列表。

偏移量

长度

描述

0

2

USBIP 版本

2

2

0x8005

命令代码:检索导出的 USB 设备列表。

4

4

0x00000000

状态:未使用,应设置为 0

OP_REP_DEVLIST

回复导出的 USB 设备列表。

偏移量

长度

描述

0

2

USBIP 版本

2

2

0x0005

回复代码:导出的 USB 设备列表。

4

4

0x00000000

状态:0 表示成功

8

4

n

导出设备的数量:0 表示没有导出的设备。

0x0C

从现在开始描述导出的 n 个设备(如果有的话)。如果没有导出任何设备,则消息以先前的“导出设备数量”字段结束。

256

path:导出 USB 设备的宿主机上的设备路径,以零字节结尾的字符串,例如“/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2”。未使用的字节应填充零字节。

0x10C

32

busid:导出设备的总线 ID,以零字节结尾的字符串,例如“3-2”。未使用的字节应填充零字节。

0x12C

4

busnum

0x130

4

devnum

0x134

4

speed

0x138

2

idVendor

0x13A

2

idProduct

0x13C

2

bcdDevice

0x13E

1

bDeviceClass

0x13F

1

bDeviceSubClass

0x140

1

bDeviceProtocol

0x141

1

bConfigurationValue

0x142

1

bNumConfigurations

0x143

1

bNumInterfaces

0x144

m_0

从现在开始描述每个接口,总共描述 bNumInterfaces 次,包含以下 4 个字段

1

bInterfaceClass

0x145

1

bInterfaceSubClass

0x146

1

bInterfaceProtocol

0x147

1

用于对齐的填充字节,应设置为零

0xC + i*0x138 + m_(i-1)*4

第二个导出的 USB 设备从 i=1 的 path 字段开始。

OP_REQ_IMPORT

请求导入(附加)远程 USB 设备。

偏移量

长度

描述

0

2

USBIP 版本

2

2

0x8003

命令代码:导入远程 USB 设备。

4

4

0x00000000

状态:未使用,应设置为 0

8

32

busid:远程主机上导出设备的 busid。可能的值取自消息字段 OP_REP_DEVLIST.busid。一个以零结尾的字符串,未使用的字节应填充零。

OP_REP_IMPORT

回复导入(附加)远程 USB 设备。

偏移量

长度

描述

0

2

USBIP 版本

2

2

0x0003

回复代码:导入回复。

4

4

0x00000000

状态

  • 0 表示成功

  • 1 表示错误

8

接下来是导入设备的详细信息(如果先前的状态字段为成功 (0)),否则回复以状态字段结束。

256

path:导出 USB 设备的宿主机上的设备路径,以零字节结尾的字符串,例如“/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2”。未使用的字节应填充零字节。

0x108

32

busid:导出设备的总线 ID,以零字节结尾的字符串,例如“3-2”。未使用的字节应填充零字节。

0x128

4

busnum

0x12C

4

devnum

0x130

4

speed

0x134

2

idVendor

0x136

2

idProduct

0x138

2

bcdDevice

0x13A

1

bDeviceClass

0x13B

1

bDeviceSubClass

0x13C

1

bDeviceProtocol

0x13D

1

bConfigurationValue

0x13E

1

bNumConfigurations

0x13F

1

bNumInterfaces

以下四个命令具有一个名为 ‘usbip_header_basic’ 的公共基本头部,并且它们的头部(在 transfer_buffer 负载之前)称为 ‘usbip_header’,具有相同的长度,因此需要填充。

usbip_header_basic

偏移量

长度

描述

0

4

command

4

4

seqnum:标识请求和相应响应的序列号;每连接递增

8

4

devid:唯一指定远程 USB 设备,代替 busnum 和 devnum;对于客户端(请求),该值为 ((busnum << 16) | devnum);对于服务器(响应),该值应设置为 0

0xC

4

direction (方向)

  • 0: USBIP_DIR_OUT

  • 1: USBIP_DIR_IN

仅由客户端使用,对于服务器,此值应为 0

0x10

4

ep:端点号,仅由客户端使用,对于服务器,此值应为 0;对于 UNLINK,此值应为 0

USBIP_CMD_SUBMIT

提交一个 URB

偏移量

长度

描述

0

20

usbip_header_basic,‘command’ 应为 0x00000001

0x14

4

transfer_flags:可能的值取决于 USBIP_URB 的 transfer_flags。请参考 include/uapi/linux/usbip.h 和 USB 请求块 (URB)。请参考 drivers/usb/usbip/usbip_common.c 中的 usbip_pack_cmd_submit()tweak_transfer_flags()

0x18

4

transfer_buffer_length:使用 URB transfer_buffer_length

0x1C

4

start_frame:使用 URB start_frame;等时传输的初始帧;如果不是等时传输,则应设置为 0

0x20

4

number_of_packets:等时数据包的数量;如果不是等时传输,则应设置为 0xffffffff

0x24

4

interval:服务端主机控制器上请求的最大时间

0x28

8

setup:USB 设置的数据字节,如果未使用则用零填充。

0x30

n

transfer_buffer。如果方向是 USBIP_DIR_OUT,则 n 等于 transfer_buffer_length;否则 n 等于 0。对于等时传输,各个等时数据包之间的填充不会被传输。

0x30+n

m

iso_packet_descriptor

USBIP_RET_SUBMIT

提交 URB 的回复

偏移量

长度

描述

0

20

usbip_header_basic,‘command’ 应为 0x00000003

0x14

4

status:URB 事务成功时为零,否则发生了某种错误。

0x18

4

actual_length:URB 数据字节数;使用 URB actual_length

0x1C

4

start_frame:使用 URB start_frame;等时传输的初始帧;如果不是等时传输,则应设置为 0

0x20

4

number_of_packets:等时数据包的数量;如果不是等时传输,则应设置为 0xffffffff

0x24

4

error_count

0x28

8

填充,应设置为 0

0x30

n

transfer_buffer。如果方向是 USBIP_DIR_IN,则 n 等于 actual_length;否则 n 等于 0。对于等时传输,各个等时数据包之间的填充不会被传输。

0x30+n

m

iso_packet_descriptor

USBIP_CMD_UNLINK

取消链接一个 URB

偏移量

长度

描述

0

20

usbip_header_basic,‘command’ 应为 0x00000002

0x14

4

unlink_seqnum,要取消链接的 SUBMIT 请求的序列号

0x18

24

填充,应设置为 0

USBIP_RET_UNLINK

URB 取消链接的回复

偏移量

长度

描述

0

20

usbip_header_basic,‘command’ 应为 0x00000004

0x14

4

status:这类似于 USBIP_RET_SUBMIT 的状态(共享相同的内存偏移量)。当 UNLINK 成功时,状态为 -ECONNRESET;当 USBIP_CMD_UNLINK 在 USBIP_RET_SUBMIT 之后时,状态为 0

0x18

24

填充,应设置为 0

示例

以下数据是从带有 HID 负载的线缆中捕获的

CmdIntrIN:  00000001 00000d05 0001000f 00000001 00000001 00000200 00000040 ffffffff 00000000 00000004 00000000 00000000
CmdIntrOUT: 00000001 00000d06 0001000f 00000000 00000001 00000000 00000040 ffffffff 00000000 00000004 00000000 00000000
            ffffffff860008a784ce5ae212376300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
RetIntrOut: 00000003 00000d06 00000000 00000000 00000000 00000000 00000040 ffffffff 00000000 00000000 00000000 00000000
RetIntrIn:  00000003 00000d05 00000000 00000000 00000000 00000000 00000040 ffffffff 00000000 00000000 00000000 00000000
            ffffffff860011a784ce5ae2123763612891b1020100000400000000000000000000000000000000000000000000000000000000000000000000000000000000