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)
