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
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:
command | seqnum | devid | direction | ep |
---|---|---|---|---|
4 | 4 | 4 | 4 | 4 |
transfer_flags | transfer_buffer_length | start_frame | number_of_packets | interval | setup |
---|---|---|---|---|---|
4 | 4 | 4 | 4 | 4 | 8 |
status | actual_length | start_frame | number_of_packets | error_count |
---|---|---|---|---|
4 | 4 | 4 | 4 | 4 |
seqnum |
---|
4 |
status |
---|
4 |
usbip_header_basic | (depend on which packet type) |
---|---|
20 | 28 |
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) |
version | code | status |
---|---|---|
2 | 2 | 4 |
busid |
---|
32 |
struct usbip_usb_device |
---|
312 |
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()
# 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/*
#ls /sys/bus/platform/devices/vhci_hcd/ports 00 01 02 03 04 05 06 07 08
#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 };
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()
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: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()
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)