SDB:USBIP

Jump to: navigation, search

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

USBip 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
  1. define USBIP_CMD_SUBMIT 0x0001
  2. define USBIP_RET_SUBMIT 0x0002
  3. define USBIP_CMD_UNLINK 0x0003
  4. 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
    1. socket connect
    2. send op common (VERSION, code, status)
    3. send op import request (OP_REQ_IMPORT)
    4. receive op common
    5. receive op import reply
    6. check reply (busid)
    7. import device
    8. libusbip: open /sys/devices/platform/vhci_hcd
    9. libusbip: open driver -> get device
    10. libusbip: get available port
    11. libusbip: parse port status
    12. 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
    1. tcp connection
    2. send op common(OP_REQ_DEVLIST)
    3. receive op common
    4. receive number of device
    5. 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)

Userspace library

Reference