SDB:USBIP
Introduction
The USB/IP Project aims to develop a general USB device sharing system over IP network. To share USB devices between computers with their full functionality, USB/IP encapsulates "USB I/O messages" into TCP/IP payloads and transmits them between computers
Architecture
Data structure
usbip-core layer
USB/IP request headers
Each request is transferred across the network to its counterpart, which facilitates the normal USB communication. The values contained in the headers are basically the same as in a URB. Currently, four request types are defined:
- client to server
- USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb()
- USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT,corresponds to usb_unlink_urb()
- server to client
- USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT
- USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK
- usbip_common.h
- define USBIP_CMD_SUBMIT 0x0001
- define USBIP_RET_SUBMIT 0x0002
- define USBIP_CMD_UNLINK 0x0003
- define USBIP_RET_UNLINK 0x0004
usbip header basic
- struct usbip_header_basic - data pertinent to every request
command | seqnum | devid | direction | ep |
---|---|---|---|---|
4 | 4 | 4 | 4 | 4 |
- command: the usbip request type
- seqnum: sequential number that identifies requests; incremented per connection
- devid: specifies a remote USB device uniquely instead of busnum and devnum; in the stub driver, this value is ((busnum << 16) | devnum)
- direction: direction of the transfer
- ep: endpoint number
USBIP_CMD_SUBMIT packet header
- struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header
transfer_flags | transfer_buffer_length | start_frame | number_of_packets | interval | setup |
---|---|---|---|---|---|
4 | 4 | 4 | 4 | 4 | 8 |
- transfer_flags: URB flags
- transfer_buffer_length: the data size for (in) or (out) transfer
- start_frame: initial frame for isochronous or interrupt transfers
- number_of_packets: number of isochronous packets
- interval: maximum time for the request on the server-side host controller
- setup: setup data for a control request
USBIP_RET_SUBMIT packet header
- struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header
status | actual_length | start_frame | number_of_packets | error_count |
---|---|---|---|---|
4 | 4 | 4 | 4 | 4 |
- status: return status of a non-iso request
- actual_length: number of bytes transferred
- start_frame: initial frame for isochronous or interrupt transfers
- number_of_packets: number of isochronous packets
- error_count: number of errors for isochronous transfers
USBIP_CMD_UNLINK packet header
- struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header
seqnum |
---|
4 |
- seqnum: the URB seqnum to unlink
USBIP_RET_UNLINK packet header
- struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header
status |
---|
4 |
- status: return status of the request
pdu - packet data unit
- struct usbip_header - common header for all usbip packets
usbip_header_basic | (depend on which packet type) |
---|---|
20 | 28 |
- usbip_header_basic: the basic header(20)
- (union): packet type dependent header
- usbip_header_cmd_submit(28)
- usbip_header_ret_submit(20)
- usbip_header_cmd_unlink(1)
- usbip_header_ret_unlink(1)
userspace layer
op command list
op command | value | comment |
---|---|---|
OP_REQUEST | (0x80 << 8) | |
OP_REPLY | (0x00 << 8) | |
OP_UNSPEC | 0x00 | Dummy Code |
OP_REQ_UNSPEC | OP_UNSPEC | |
OP_REP_UNSPEC | OP_UNSPEC | |
OP_DEVINFO | 0x02 | Retrieve USB device information |
OP_REQ_DEVINFO | (OP_REQUEST | OP_DEVINFO) | |
OP_REP_DEVINFO | (OP_REPLY | OP_DEVINFO) | |
OP_IMPORT | 0x03 | Import a remote USB device |
OP_REQ_IMPORT | (OP_REQUEST | OP_IMPORT) | |
OP_REP_IMPORT | (OP_REPLY | OP_IMPORT) | |
OP_EXPORT | 0x06 | Export a USB device to a remote host |
OP_REQ_EXPORT | (OP_REQUEST | OP_EXPORT) | |
OP_REP_EXPORT | (OP_REPLY | OP_EXPORT) | |
OP_UNEXPORT | 0x07 | un-Export a USB device from a remote host |
OP_REQ_UNEXPORT | (OP_REQUEST | OP_UNEXPORT) | |
OP_REP_UNEXPORT | (OP_REPLY | OP_UNEXPORT) | |
OP_CRYPKEY | 0x04 | Negotiate IPSec encryption key |
OP_REQ_CRYPKEY | (OP_REQUEST | OP_CRYPKEY) | |
OP_REP_CRYPKEY | (OP_REPLY | OP_CRYPKEY) | |
OP_DEVLIST | 0x05 | Retrieve the list of exported USB devices |
OP_REQ_DEVLIST | (OP_REQUEST | OP_DEVLIST) | |
OP_REP_DEVLIST | (OP_REPLY | OP_DEVLIST) |
- userspace/src/usbip_network.h
op common header
- Common header for all the kinds of PDUs
version | code | status |
---|---|---|
2 | 2 | 4 |
- version: usbip version (1.0.0)
- code: op command type
- #define OP_REQUEST (0x80 << 8)
- #define OP_REPLY (0x00 << 8)
- status: op_code status (for reply)
- #define ST_OK 0x00
- #define ST_NA 0x01
op import request header
busid |
---|
32 |
op import reply header
struct usbip_usb_device |
---|
312 |
Kernel modules
usbip-core.ko
usbip-host.ko
vhci-hcd.ko
vhci_hcd_init() |-platform_driver_register() /* register as a platform mode driver */ |-/* initial platform_driver structure */ |-vhci_hcd_probe() |-usb_create_hcd() /* Allocate and initialize hcd */ |-/* initial hc_driver structure */ |-vhci_start() |-vhci_device_init() /* initialize private data of usb_hcd */ /* #define VHCI_NPORTS 8 */ |-vhci_shutdown_connection() |-vhci_device_reset() |-vhci_device_unusable() |-usbip_start_eh() |-kthread_run(event_handler_loop, ud, "usbip_eh") |-atomic_set() |-sysfs_create_group() /* vhci_hcd is now ready to be controlled through sysfs */ :vhci_sysfs.c |-DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach) |-sockfd_to_socket() |-kthread_run(vhci_rx_loop,) :vhci_rx.c |-vhci_rx_pdu() |-usbip_xmit() /* receive a pdu */ |-usbip_header_correct_endian() |-vhci_recv_ret_submit() |-usbip_pack_pdu() /* unpack the pdu to a urb */ |-usbip_recv_xbuff() /* recv transfer buffer */ |-usbip_recv_iso() /* recv iso_packet_descriptor */ |-usbip_pad_iso() /* restore the padding in iso packets */ |-usb_hcd_unlink_urb_from_ep() |-usb_hcd_giveback_urb() or|-vhci_recv_ret_unlink() |-dequeue_pending_unlink() |-pickup_urb_and_free_priv() |-usb_hcd_unlink_urb_from_ep() |-usb_hcd_giveback_urb() |-kthread_run(vhci_tx_loop,) :vhci_tx.c |-vhci_send_cmd_submit() |-setup_cmd_submit_pdu() /* setup usbip_header */ |-usbip_pack_pdu() |-usbip_alloc_iso_desc_pdu() /* setup iso_packet_descriptor */ |-kernel_sendmsg() |-usbip_header_correct_endian() |-vhci_send_cmd_unlink() |-wait_event_interruptible() |-rh_port_connect() |-DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach) |-DEVICE_ATTR(status, S_IRUGO, show_status, NULL) |-vhci_stop() |-sysfs_remove_group() |-vhci_urb_enqueue() |-vhci_urb_dequeue() |-vhci_get_frame_number() |-vhci_hub_status() |-vhci_hub_control() |-vhci_bus_suspend() |-vhci_bus_resume() |-usb_add_hcd() |-vhci_hcd_remove() |-vhci_hcd_suspend() |-vhci_hcd_resume() |-platform_device_register()
todo
- remove status table from /sys/bus/platform/devices/vhci_hcd/
# cat /sys/devices/platform/vhci_hcd/status prt sta spd bus dev socket local_busid 000 004 000 000 000 0000000000000000 0-0 001 004 000 000 000 0000000000000000 0-0 002 004 000 000 000 0000000000000000 0-0 003 004 000 000 000 0000000000000000 0-0 004 004 000 000 000 0000000000000000 0-0 005 004 000 000 000 0000000000000000 0-0 006 004 000 000 000 0000000000000000 0-0 007 004 000 000 000 0000000000000000 0-0
It's still being used by usbip_vhci_driver_open(), but can try to get status information from /sys/bus/usb/devices/usb/*
- add each port to /sys/bus/platform/devices/vhci_hcd/
#ls /sys/bus/platform/devices/vhci_hcd/ports 00 01 02 03 04 05 06 07 08
- add vhci status to each port
#cat /sys/bus/platform/devices/vhci_hcd/ports/00/status 4 (VDEV_ST_NULL) /* userspace/libsrc/usbip_common.h */ enum usbip_device_status{ /* sdev is available. */ SDEV_ST_AVAILABLE = 0x01, /* sdev is now used. */ SDEV_ST_USED, /* sdev is unusable because of a fatal error. */ SDEV_ST_ERROR, /* vdev does not connect a remote device. */ VDEV_ST_NULL, /* vdev is used, but the USB address is not assigned yet */ VDEV_ST_NOTASSIGNED, VDEV_ST_USED, VDEV_ST_ERROR };
Userspace commands description
Client
attach
attach: usbip_attach() |-attach_device() :userspace/src/usbip_attach.c |-usbip_net_tcp_connect() /* socket connect */ |-query_import_device() |-usbip_send_op_common() /* send a request OP_REQ_IMPORT */ |-usbip_send() |-usbip_send() |-usbip_recv_op_common() /* recieve a reply OP_REP_IMPORT */ |-usbip_recv() |-usbip_recv() |-strncmp() /* check the reply */ |-import_device() /* import a device */ :userspace/libsrc/vhci_driver.c |-usbip_vhci_driver_open() |-sysfs_get_mnt_path() |-get_hc_busid() |-sysfs_open_device() |-get_nports() |-dlist_new() |-refresh_class_device_list() |-refresh_imported_device_list() |-parse_status() |-imported_device_init() |-sysfs_open_device() |-read_usb_device() |-sysfs_close_device() |-usbip_vhci_get_free_port() |-usbip_vhci_attach_device() |-usbip_vhci_attach_device2() /* write to sysfs attach */ |-sysfs_get_device_attr() |-sysfs_write_attribute() |-usbip_vhci_driver_close() |-record_connection()
- usbip attach -h 147.2.211.4 -b 1-3
- socket connect
- send op common (VERSION, code, status)
- send op import request (OP_REQ_IMPORT)
- receive op common
- receive op import reply
- check reply (busid)
- import device
- libusbip: open /sys/devices/platform/vhci_hcd
- libusbip: open driver -> get device
- libusbip: get available port
- libusbip: parse port status
- libusbip: write to attach
detach
detach:usbip_detach() :userspace/src/usbip_detach.c |-detach_port() :userspace/libsrc/vhci_driver.c |-usbip_vhci_driver_open() |-sysfs_get_mnt_path() |-get_hc_busid() |-sysfs_open_device() |-get_nports() |-dlist_new() |-usbip_vhci_detach_device() /* write to sysfs detach */ |-sysfs_get_device_attr() |-sysfs_write_attribute() |-usbip_vhci_driver_close()
list
list:usbip_list() :userspace/src/usbip_list.c |-usbip_names_init(USBIDS_FILE) /* #define USBIDS_FILE "/usr/share/hwdata/usb.ids" */ |-(remote)list_exported_devices() :userspace/src/usbip_list.c |-list_exported_devices() |-usbip_net_tcp_connect() |-get_exported_devices() /* Retrieve the list of exported USB devices. */ |-usbip_send_op_common() /* OP_REQ_DEVLIST */ |-usbip_recv_op_common() /* OP_REP_DEVLIST */ |-usbip_recv() /* receive op_devlist_reply */ |-usbip_recv() |-pack_usb_device() |-usbip_names_get_product() |-usbip_names_get_class() |-usbip_recv() |-pack_usb_interface() |-usbip_names_get_class() |-(local)list_devices()
- remote
- tcp connection
- send op common(OP_REQ_DEVLIST)
- receive op common
- receive number of device
- receive devlist reply
- local
Server
1. usbipd -D
main() |- do_standalone_mode() |- usbip_names_init():read usb.ids file |- usbip_stub_driver_open() |- struct usbip_stub_driver { int ndevs; struct sysfs_driver *sysfs_driver; struct dlist *edev_list;/* list of exported device */ } |- allocate memory to usbip_stub_driver->edev_list. |- set deletion function "usbip_exported_device_delete" to usbip_stub_driver->edev_list. |- stub_driver->sysfs_driver = open_sysfs_stub_driver() |- stub_driver = sysfs_open_driver_path(stub_driver_path) |- refresh_exported_devices() |- |- set_signal() |- g_io_add_watch(gio, (G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL), process_comming_request, NULL);
2. usbip attach -h [IP] -b [busid]
Server (usbipd) : process_comming_request() |-recv_pdu() |-usbip_network.c:usbip_recv_op_common()-> |-usbip_network.c:usbip_xmit()-> |-usbip_stub_refresh_device_list() |- refresh_exported_devices() |- usbip_exported_device_new() |-read_usb_device() |- read_attribute() |- .... |- read_speed(),例如︰/sys/devices/pci0000:00/0000:00:1a.7/usb1/1-1/speed |- for loop for --> read_usb_interface() |- recv_request_import() |- usbip_recv() |- recv_request_import() |-usbip_set_nodelay() |- |- usbip_stub_export_device() |- usbip_send_op_common() |- pack_usb_device() : 作host byte順序與network byte順序之間的轉換 |- usbip_send() : 將資訊送給Client
3. usbip bind -b [busid]
usbip.c:run_command() -> |- usbip_bind.c:usbip_bind() |- usbip_bind.c:use_device_by_usbip() |- unbind():先移除掉此device的driver |- 從busid上讀取bConfigurationValue與bNumInterfaces |- 根據該busid上記載的interface數量,作bind_interface |- 尋找此device的driver, getdriver(), 把driver名稱設到driver[] |- 找出類似(/sys/bus/usb/devices/%s:%d.%d/driver", busid, conf, infnum) 然後再把driver名稱設定到driver變數 |- unbind_interface():將此driver內的一個busid的link給移除 |- unbind_interface_busid() |- modify_match_busid |- 加人busid到/sys/bus/usb/drivers/usbip-host/match_busid |- 這裡會去對/sys/bus/usb/drivers/usbip-host/match_busid檔案寫入"add [busid]" 則會引發(usbip)stub_main.c:store_match_busid(). |- add_match_busid() |- get_busid_idx():查看是否已經register過了 |- for loop to add |- if ((busid_table[i].status != STUB_BUSID_ALLOC) && (busid_table[i].status != STUB_BUSID_REMOV)) busid_table[i].status = STUB_BUSID_ADDED; |- store_match_busid : add busid 1-1 |- bind_to_usbip() |- bind_interface() |- bind_interface_busid() |- [Kernel_driver] : usbip_host driver |- stub_probe |- get_busid_priv() |- get_busid_idx():從busid_table[i]去一個一個相比較,直到某個busid_table[i].name跟busid不相符 則此i即為回傳值 |- stub_device_alloc() |- Initialize everything to stub_driver |- usbip_start_eh() |- ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh") |- stub_add_files(&interface->dev) |- device_create_file(dev, &dev_attr_usbip_status) |- device_create_file(dev, &dev_attr_usbip_sockfd) |- device_create_file(dev, &dev_attr_usbip_debug)