All of lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND][PATCH V9 0/7] xen pvusb toolstack work
@ 2015-11-25  9:46 Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 1/7] libxl: export some functions for pvusb use Chunyan Liu
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig

RESEND to update usb controller and device naming according to current
discussion:
* libxl: Introduce a template for devices with a controller
https://www.mail-archive.com/xen-devel@lists.xen.org/msg46720.html

This patch series is to add pvusb toolstack work, supporting hot add|remove
USB device to|from guest and specify USB device in domain configuration file.

Changes to V8:
* lots of changes in libxl pvusb API (patch 3/7)
* update 2/7 to write separate read_sysfs_file function
* address all other comments

V8:
http://lists.xen.org/archives/html/xen-devel/2015-10/msg02178.html

V7:
http://lists.xen.org/archives/html/xen-devel/2015-09/msg03115.html

V6:
http://lists.xen.org/archives/html/xen-devel/2015-08/msg00750.html

V5:
http://lists.xen.org/archives/html/xen-devel/2015-06/msg04052.html

V4:
http://lists.xenproject.org/archives/html/xen-devel/2015-06/msg01327.html

Related Discussion Threads:
http://www.redhat.com/archives/libvir-list/2014-June/msg00038.html
http://lists.xen.org/archives/html/xen-devel/2014-06/msg00086.html

              <<< pvusb work introduction >>>

1. Overview

There are two general methods for passing through individual host
devices to a guest. The first is via an emulated USB device
controller; the second is PVUSB.

Additionally, there are two ways to add USB devices to a guest: via
the config file at domain creation time, and via hot-plug while the VM
is running.

* Emulated USB

In emulated USB, the device model (qemu) presents an emulated USB
controller to the guest. The device model process then grabs control
of the device from domain 0 and and passes the USB commands between
the guest OS and the host USB device.

This method is only available to HVM domains, and is not available for
domains running with device model stubdomains.

* PVUSB

PVUSB uses a paravirtialized front-end/back-end interface, similar to
the traditional Xen PV network and disk protocols. In order to use
PVUSB, you need usbfront in your guest OS, and usbback in dom0 (or
your USB driver domain).

2. Specifying a host USB device

QEMU qmp commands allows USB devices to be specified either by their
bus address (in the form bus.device) or their device tag (in the form
vendorid:deviceid).

Each way of specifying has its advantages:

    Specifying by device tag will always get the same device,
regardless of where the device ends up in the USB bus topology.
However, if there are two identical devices, it will not allow you to
specify which one.

    Specifying by bus address will always allow you to choose a
specific device, even if you have duplicates. However, the bus address
may change depending on which port you plugged the device into, and
possibly also after a reboot.

To avoid duplication of vendorid:deviceid, we'll use bus address to
specify host USB device in xl toolstack.

You can use lsusb to list the USB devices on the system:

Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0
Hub
Bus 003 Device 002: ID f617:0905
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 004: ID 0424:2640 Standard Microsystems Corp. USB 2.0
Hub
Bus 001 Device 005: ID 0424:4060 Standard Microsystems Corp. Ultra
Fast Media Reader
Bus 001 Device 006: ID 046d:c016 Logitech, Inc. Optical Wheel Mouse

To pass through the Logitec mouse, for instance, you could specify
1.6 (remove leading zeroes).

Note: USB hubs can not be assigned to guest.

3. PVUSB toolstack

* Specify USB device in xl config file

You can just specify usb devices, like:
usbdev=['1.6']

Then it will create a USB controller automatically and attach the USB
device to the first available USB controller:port.

or, you can explicitly specify usb controllers and usb devices, like:
usbctrl=['verison=1, ports=4', 'version=2, ports=8', ]
usbdev=['1.6, controller=0, port=1']

Then it will create two USB controllers as you specified.
And if controller and port are specified in usb config, then it will
attach the USB device to that controller:port. About the controller
and port value:
Each USB controller has a index (or called devid) based on 0. The 1st
controller has index 0, the 2nd controller has index 1, ...
Under controller, each port has a port number based on 1. In above
configuration, the 1st controller will have port 1,2,3,4.

* Hot-Plug USB device

To attach a USB device, you should first create a USB controller.
e.g.
xl usb-ctrl-attach domain [version=1|2] [ports=value]
By default, it will create a USB2.0 controller with 8 ports.

Then you could attach a USB device.
e.g.
xl usb-attach domain 1.6 [controller=index port=number]
By default, it will find the 1st available controller:port to attach
the USB device.

You could view USB device status of the domain by usb-list.
e.g.
xl usb-list domain
It will list USB controllers and USB devices under each controller.

You could detach a USB device with usb-detach command.
e.g.
xl usb-detach domain 1.6

You can also remove the whole USB controller by usb-ctrl-detach
command.
e.g.
xl usb-ctrl-detach domain 0
It will remove the USB controller with index 0 and all USB devices
under it.

4. PVUSB Libxl implementation

* usb-ctrl-attach
To create a usb controller, we need:
1) generate usb controler related information
2) write usb controller frontend/backend info to xenstore
PVUSB frontend and backend driver will probe xenstore paths and build
connection between frontend and backend.

* usb-ctrl-detach
To remove a usb controller, we need:
1) check if the usb controller exists or not
2) remove all usb devices under controller
3) remove usb controller info from xenstore

* usb-attach
To attach a usb device, we need:
1) check if the usb device type is assignable
2) check if the usb device is already assigned to a domain
3) add 'busid' of the usb device to xenstore contoller/port/.
   PVUSB driver watches the xenstore changes and detects that,
   and needs to use 'busid' to do following work.
4) unbind usb device from original driver and bind to usbback.
   If usb device has many interfaces, then:
   - unbind each interface from its original driver and bind to usbback.
   - store the original driver to xenstore for later rebinding when
     detaching the device.

* usb-detach
To detach a usb device, we need:
1) check if the usb device is assigned to the domain
2) remove the usb device from xenstore controller/port.
3) unbind usb device from usbback and rebind to its original driver.
   If usb device has many interfaces, do it to each interface.

* usb-list
List all USB controllers and USB devices under each controller.

5. PVUSB xenstore information

PVUSB xenstore information includes three parts: frontend, backend
and /libxl part.

A USB controller is corresponding to a "vusb" device in xenstore.
Adding a USB controller will add a new "vusb" device, removing a
USB controller will delete the related "vusb" device.

Following is an example xenstore values of a USB controller.
Backend:
   backend = ""
    vusb = ""
     1 = ""
      0 = ""
       frontend = "/local/domain/1/device/vusb/0"
       frontend-id = "1"
       online = "1"
       state = "4"
       type = "pv"
       usb-ver = "1"
       num-ports = "4"
       port = ""
        1 = ""
        2 = ""
        3 = ""
        4 = ""

Frontend:
   device = ""
    vusb = ""
     0 = ""
      backend = "/local/domain/0/backend/vusb/1/0"
      backend-id = "0"
      state = "4"
      urb-ring-ref = "348"
      conn-ring-ref = "346"
      event-channel = "20"

Adding a USB device won't create a new "vusb" device, but only write
the USB device busid to one port of USB controller.
For example, attaching a USB device (busid is 2-1.6) to above USB
controller port 1, it only need write 2-1.6 to port 1 of this USB
controller:
Backend:
   backend = ""
    vusb = ""
     1 = ""
      0 = ""
       frontend = "/local/domain/1/device/vusb/0"
       frontend-id = "1"
       online = "1"
       state = "4"
       type = "pv"
       usb-ver = "1"
       num-ports = "4"
       port = ""
        1 = "2-1.6"
        2 = ""
        3 = ""
        4 = ""
Frontend doesn't change.

Since assign a host USB device to guest, we'll unbind USB interfaces
from their original drivers and bind them to usbback. After detaching
this USB device from guest, one would hope the USB interfaces could
be rebind to their original drivers, so there should some place to
get the original driver info. To support that, when attaching a USB
device to guest, we'll save the original driver info in xenstore too,
the place is /libxl/usbback, for example:
libxl = ""
 1 = ""
  dm-version = "qemu_xen"
 usbback = ""
  3-11 = ""
   3-11@1_0 = ""
    driver_path = "/sys/bus/usb/drivers/btusb"

In this example, USB device (busid is 3-11, /sys/bus/usb/devices/3-11).
It has interface 3-11:1.0, whose original dirver is btusb.
Since xenstore doesn't allow ':' and '.' in a key, so we encode the
interface by changing ':' to '@' and changing '.' to '_'.

When detaching the USB device from guest, we can rebind 3-11:1.0 to
btusb driver.

Chunyan Liu (7):
  libxl: export some functions for pvusb use
  libxl_utils: add internal function to read sysfs file contents
  libxl: add pvusb API
  libxl: add libxl_device_usb_assignable_list API
  xl: add pvusb commands
  xl: add usb-assignable-list command
  domcreate: support pvusb in configuration file

 docs/man/xl.cfg.pod.5                |   84 ++
 docs/man/xl.pod.1                    |   41 +
 tools/libxl/Makefile                 |    2 +-
 tools/libxl/libxl.c                  |   55 +-
 tools/libxl/libxl.h                  |   77 ++
 tools/libxl/libxl_create.c           |   73 +-
 tools/libxl/libxl_device.c           |    9 +-
 tools/libxl/libxl_internal.h         |   34 +
 tools/libxl/libxl_osdeps.h           |   13 +
 tools/libxl/libxl_pvusb.c            | 1584 ++++++++++++++++++++++++++++++++++
 tools/libxl/libxl_types.idl          |   46 +
 tools/libxl/libxl_types_internal.idl |    1 +
 tools/libxl/libxl_utils.c            |   95 ++
 tools/libxl/libxl_utils.h            |    5 +
 tools/libxl/xl.h                     |    6 +
 tools/libxl/xl_cmdimpl.c             |  322 ++++++-
 tools/libxl/xl_cmdtable.c            |   29 +
 17 files changed, 2466 insertions(+), 10 deletions(-)
 create mode 100644 tools/libxl/libxl_pvusb.c

-- 
2.1.4

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RESEND][PATCH V9 1/7] libxl: export some functions for pvusb use
  2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
@ 2015-11-25  9:46 ` Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 2/7] libxl_utils: add internal function to read sysfs file contents Chunyan Liu
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig, Simon Cao

Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Simon Cao <caobosimon@gmail.com>
Reviewed-by: Wei Liu <wei.liu2@citrix.com>

---
Changes:
  - fix indentation

 tools/libxl/libxl.c          | 5 ++---
 tools/libxl/libxl_internal.h | 3 +++
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index 854e957..eaa7d75 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -2035,7 +2035,7 @@ out:
 }
 
 /* common function to get next device id */
-static int libxl__device_nextid(libxl__gc *gc, uint32_t domid, char *device)
+int libxl__device_nextid(libxl__gc *gc, uint32_t domid, char *device)
 {
     char *dompath, **l;
     unsigned int nb;
@@ -2054,8 +2054,7 @@ static int libxl__device_nextid(libxl__gc *gc, uint32_t domid, char *device)
     return nextid;
 }
 
-static int libxl__resolve_domid(libxl__gc *gc, const char *name,
-                                uint32_t *domid)
+int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
 {
     if (!name)
         return 0;
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index b00add0..ab981d2 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1165,6 +1165,9 @@ _hidden int libxl__init_console_from_channel(libxl__gc *gc,
                                              libxl__device_console *console,
                                              int dev_num,
                                              libxl_device_channel *channel);
+_hidden int libxl__device_nextid(libxl__gc *gc, uint32_t domid, char *device);
+_hidden int libxl__resolve_domid(libxl__gc *gc, const char *name,
+                                 uint32_t *domid);
 
 /*
  * For each aggregate type which can be used as an input we provide:
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RESEND][PATCH V9 2/7] libxl_utils: add internal function to read sysfs file contents
  2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 1/7] libxl: export some functions for pvusb use Chunyan Liu
@ 2015-11-25  9:46 ` Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 3/7] libxl: add pvusb API Chunyan Liu
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig

Add a new function libxl_read_sysfs_file_contents to handle sysfs file
specially. It would be used in later pvusb work.

Signed-off-by: Chunyan Liu <cyliu@suse.com>
---
Changes:
  - write a separate function libxl__read_sysfs_file_contents, no
    longer mix with libxl_read_file_contents

 tools/libxl/libxl_internal.h |  5 +++
 tools/libxl/libxl_utils.c    | 77 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index ab981d2..7aff237 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -4021,6 +4021,11 @@ void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
                                     const libxl_bitmap *sptr);
 
 int libxl__count_physical_sockets(libxl__gc *gc, int *sockets);
+
+_hidden int libxl__read_sysfs_file_contents(libxl__gc *gc,
+                                            const char *filename,
+                                            void **data_r,
+                                            int *datalen_r);
 #endif
 
 /*
diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c
index e42422a..7f612a6 100644
--- a/tools/libxl/libxl_utils.c
+++ b/tools/libxl/libxl_utils.c
@@ -396,6 +396,83 @@ int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
     return e;
 }
 
+int libxl__read_sysfs_file_contents(libxl__gc *gc, const char *filename,
+                                    void **data_r, int *datalen_r)
+{
+    FILE *f = 0;
+    uint8_t *data = 0;
+    int datalen = 0;
+    int e;
+    struct stat stab;
+    ssize_t rs;
+
+    f = fopen(filename, "r");
+    if (!f) {
+        if (errno == ENOENT) return ENOENT;
+        LOGE(ERROR, "failed to open %s", filename);
+        goto xe;
+    }
+
+    if (fstat(fileno(f), &stab)) {
+        LOGE(ERROR, "failed to fstat %s", filename);
+        goto xe;
+    }
+
+    if (!S_ISREG(stab.st_mode)) {
+        LOGE(ERROR, "%s is not a plain file", filename);
+        errno = ENOTTY;
+        goto xe;
+    }
+
+    if (stab.st_size > INT_MAX) {
+        LOG(ERROR, "file %s is far too large", filename);
+        errno = EFBIG;
+        goto xe;
+    }
+
+    datalen = stab.st_size;
+
+    if (stab.st_size && data_r) {
+        data = libxl__malloc(gc, datalen);
+        if (!data) goto xe;
+
+        /* For sysfs file, datalen is always PAGE_SIZE. 'read'
+         * will return the number of bytes of the actual content,
+         * rs <= datalen is expected.
+         */
+        rs = fread(data, 1, datalen, f);
+        if (rs < datalen) {
+            if (ferror(f)) {
+                LOGE(ERROR, "failed to read %s", filename);
+                goto xe;
+            }
+
+            datalen = rs;
+            data = libxl__realloc(gc, data, datalen);
+            if (!data)
+                goto xe;
+        }
+    }
+
+    if (fclose(f)) {
+        f = 0;
+        LOGE(ERROR, "failed to close %s", filename);
+        goto xe;
+    }
+
+    if (data_r) *data_r = data;
+    if (datalen_r) *datalen_r = datalen;
+
+    return 0;
+
+ xe:
+    e = errno;
+    assert(e != ENOENT);
+    if (f) fclose(f);
+    return e;
+}
+
+
 #define READ_WRITE_EXACTLY(rw, zero_is_eof, constdata)                    \
                                                                           \
   int libxl_##rw##_exactly(libxl_ctx *ctx, int fd,                 \
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RESEND][PATCH V9 3/7] libxl: add pvusb API
  2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 1/7] libxl: export some functions for pvusb use Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 2/7] libxl_utils: add internal function to read sysfs file contents Chunyan Liu
@ 2015-11-25  9:46 ` Chunyan Liu
  2015-12-08  5:50   ` Chun Yan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 4/7] libxl: add libxl_device_usbdev_assignable_list API Chunyan Liu
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig, Simon Cao

Add pvusb APIs, including:
 - attach/detach (create/destroy) virtual usb controller.
 - attach/detach usb device
 - list usb controller and usb devices
 - some other helper functions

Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Simon Cao <caobosimon@gmail.com>

---
changes:
  - update naming, all places indicating usb controller named
    as usbctrl, all places indicating usb device named as usbdev
  - update DEFINE_DEVICE_REMOVE instead of creating a new
    DEFINE_DEVICE_REMOVE_EXT
  - use libxl__xs_read_checked instead of libxl__xs_read
  - update local READ_SUBPATH(_INT) macros to include more common codes
  - save drvpath before unbind
  - get_assigned_devices: call libxl__device_usbdev_list_for_ctrl
    instead of doing all things from scratch
  - usb_interface_xenstore_encode: use special char to avoid confusion
  - usb readdir_r instead of readdir
  - check syscall errno
  - remove usbinfo definition
  - address other comments except:
    libxl__device_usbdev_add/remove and do_usbdev_add/remove, in previous
    discussion, we'd like to get usbctrlinfo once and pass usbctrlinfo to
    do_usbdev_add/remove. However, during update, adding usbdev process
    still needs to try twice to get usbctrlinfo. (Before set_default,
    if usbctrl doesn't exist it doesn't doing getting usbctrlinfo actually;
    after set_default, needs to get usbctrlinfo then). So, finally, just
    change codes to make adding/removing process symmetrical.

 tools/libxl/Makefile                 |    2 +-
 tools/libxl/libxl.c                  |   50 +-
 tools/libxl/libxl.h                  |   77 ++
 tools/libxl/libxl_device.c           |    5 +-
 tools/libxl/libxl_internal.h         |   18 +
 tools/libxl/libxl_osdeps.h           |   13 +
 tools/libxl/libxl_pvusb.c            | 1534 ++++++++++++++++++++++++++++++++++
 tools/libxl/libxl_types.idl          |   46 +
 tools/libxl/libxl_types_internal.idl |    1 +
 tools/libxl/libxl_utils.c            |   18 +
 tools/libxl/libxl_utils.h            |    5 +
 11 files changed, 1766 insertions(+), 3 deletions(-)
 create mode 100644 tools/libxl/libxl_pvusb.c

diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index 6ff5bee..a36145a 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -103,7 +103,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \
 			libxl_stream_read.o libxl_stream_write.o \
 			libxl_save_callout.o _libxl_save_msgs_callout.o \
 			libxl_qmp.o libxl_event.o libxl_fork.o \
-			libxl_dom_suspend.o $(LIBXL_OBJS-y)
+			libxl_dom_suspend.o libxl_pvusb.o $(LIBXL_OBJS-y)
 LIBXL_OBJS += libxl_genid.o
 LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o
 
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index eaa7d75..a479465 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -4144,6 +4144,36 @@ out:
     return rc;
 }
 
+static void libxl__initiate_device_disk_remove(libxl__egc *egc,
+                                               libxl__ao_device *aodev)
+{
+    return libxl__initiate_device_remove(egc, aodev);
+}
+
+static void libxl__initiate_device_nic_remove(libxl__egc *egc,
+                                              libxl__ao_device *aodev)
+{
+    return libxl__initiate_device_remove(egc, aodev);
+}
+
+static void libxl__initiate_device_vtpm_remove(libxl__egc *egc,
+                                               libxl__ao_device *aodev)
+{
+    return libxl__initiate_device_remove(egc, aodev);
+}
+
+static void libxl__initiate_device_vkb_remove(libxl__egc *egc,
+                                              libxl__ao_device *aodev)
+{
+    return libxl__initiate_device_remove(egc, aodev);
+}
+
+static void libxl__initiate_device_vfb_remove(libxl__egc *egc,
+                                              libxl__ao_device *aodev)
+{
+    return libxl__initiate_device_remove(egc, aodev);
+}
+
 /******************************************************************************/
 
 /* Macro for defining device remove/destroy functions in a compact way */
@@ -4158,6 +4188,8 @@ out:
  * libxl_device_vkb_destroy
  * libxl_device_vfb_remove
  * libxl_device_vfb_destroy
+ * libxl_device_usbctrl_remove
+ * libxl_device_usbctrl_destroy
  */
 #define DEFINE_DEVICE_REMOVE(type, removedestroy, f)                    \
     int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \
@@ -4179,7 +4211,7 @@ out:
         aodev->dev = device;                                            \
         aodev->callback = device_addrm_aocomplete;                      \
         aodev->force = f;                                               \
-        libxl__initiate_device_remove(egc, aodev);                      \
+        libxl__initiate_device_##type##_remove(egc, aodev);             \
                                                                         \
     out:                                                                \
         if (rc) return AO_CREATE_FAIL(rc);                                    \
@@ -4209,6 +4241,10 @@ DEFINE_DEVICE_REMOVE(vfb, destroy, 1)
 DEFINE_DEVICE_REMOVE(vtpm, remove, 0)
 DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
 
+/* usbctrl */
+DEFINE_DEVICE_REMOVE(usbctrl, remove, 0)
+DEFINE_DEVICE_REMOVE(usbctrl, destroy, 1)
+
 /* channel/console hotunplug is not implemented. There are 2 possibilities:
  * 1. add support for secondary consoles to xenconsoled
  * 2. dynamically add/remove qemu chardevs via qmp messages. */
@@ -4222,6 +4258,8 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
  * libxl_device_disk_add
  * libxl_device_nic_add
  * libxl_device_vtpm_add
+ * libxl_device_usbctrl_add
+ * libxl_device_usbdev_add
  */
 
 #define DEFINE_DEVICE_ADD(type)                                         \
@@ -4253,6 +4291,12 @@ DEFINE_DEVICE_ADD(nic)
 /* vtpm */
 DEFINE_DEVICE_ADD(vtpm)
 
+/* usbctrl */
+DEFINE_DEVICE_ADD(usbctrl)
+
+/* usb */
+DEFINE_DEVICE_ADD(usbdev)
+
 #undef DEFINE_DEVICE_ADD
 
 /******************************************************************************/
@@ -6794,6 +6838,10 @@ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
 
     MERGE(pci, pcidevs, COMPARE_PCI, {});
 
+    MERGE(usbctrl, usbctrls, COMPARE_USBCTRL, {});
+
+    MERGE(usbdev, usbdevs, COMPARE_USB, {});
+
     /* Take care of removable device. We maintain invariant in the
      * insert / remove operation so that:
      * 1. if xenstore is "empty" while JSON is not, the result
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 168fedd..609d068 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -123,6 +123,12 @@
 #define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1
 
 /*
+ * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices
+ * through pvusb -- both hotplug and at domain creation time..
+ */
+#define LIBXL_HAVE_PVUSB 1
+
+/*
  * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the
  * libxl_vendor_device field is present in the hvm sections of
  * libxl_domain_build_info. This field tells libxl which
@@ -1418,6 +1424,77 @@ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
                        const libxl_asyncop_how *ao_how)
                        LIBXL_EXTERNAL_CALLERS_ONLY;
 
+/*
+ * USB
+ *
+ * For each device removed or added, one of these protocols is available:
+ * - PV (i.e., PVUSB)
+ * - DEVICEMODEL (i.e, qemu)
+ *
+ * PV is available for either PV or HVM domains.  DEVICEMODEL is only
+ * available for HVM domains.  The caller can additionally specify
+ * "AUTO", in which case the library will try to determine the best
+ * protocol automatically.
+ *
+ * At the moment, the only protocol implemented is PV.
+ *
+ * One can add/remove USB controllers to/from guest, and attach/detach USB
+ * devices to/from USB controllers.
+ *
+ * To add USB controllers and USB devices, one can adding USB controllers
+ * first and then attaching USB devices to some USB controller, or adding
+ * USB devices to guest directly, it will automatically create a USB
+ * controller for USB devices to attach.
+ *
+ * To remove USB controllers or USB devices, one can remove USB devices
+ * under USB controller one by one and then remove USB controller, or
+ * remove USB controller directly, it will remove all USB devices under
+ * it automatically.
+ *
+ */
+/* USB Controllers*/
+int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_usbctrl *usbctrl,
+                             const libxl_asyncop_how *ao_how)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid,
+                                libxl_device_usbctrl *usbctrl,
+                                const libxl_asyncop_how *ao_how)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_usbctrl *usbctrl,
+                                 const libxl_asyncop_how *ao_how)
+                                 LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx,
+                                                uint32_t domid, int *num);
+
+void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr);
+
+
+int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_usbctrl *usbctrl,
+                                 libxl_usbctrlinfo *usbctrlinfo);
+
+/* USB Devices */
+
+int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_usbdev *usbdev,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
+                               libxl_device_usbdev *usbdev,
+                               const libxl_asyncop_how *ao_how)
+                               LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_usbdev *
+libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num);
+
+void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr);
+
 /* Network Interfaces */
 int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic,
                          const libxl_asyncop_how *ao_how)
diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c
index 8bb5e93..6715c16 100644
--- a/tools/libxl/libxl_device.c
+++ b/tools/libxl/libxl_device.c
@@ -676,7 +676,10 @@ void libxl__devices_destroy(libxl__egc *egc, libxl__devices_remove_state *drs)
                 aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
                 aodev->dev = dev;
                 aodev->force = drs->force;
-                libxl__initiate_device_remove(egc, aodev);
+                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB)
+                    libxl__initiate_device_usbctrl_remove(egc, aodev);
+                else
+                    libxl__initiate_device_remove(egc, aodev);
             }
         }
     }
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 7aff237..5b70c6e 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -2569,6 +2569,14 @@ _hidden void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid,
                                    libxl_device_vtpm *vtpm,
                                    libxl__ao_device *aodev);
 
+_hidden void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
+                                       libxl_device_usbctrl *usbctrl,
+                                       libxl__ao_device *aodev);
+
+_hidden void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid,
+                                      libxl_device_usbdev *usbdev,
+                                      libxl__ao_device *aodev);
+
 /* Internal function to connect a vkb device */
 _hidden int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid,
                                   libxl_device_vkb *vkb);
@@ -2601,6 +2609,13 @@ _hidden void libxl__wait_device_connection(libxl__egc*,
 _hidden void libxl__initiate_device_remove(libxl__egc *egc,
                                            libxl__ao_device *aodev);
 
+_hidden int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid,
+                               libxl_device_usbctrl *usbctrl,
+                               libxl__device *device);
+
+_hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
+                                                   libxl__ao_device *aodev);
+
 /*
  * libxl__get_hotplug_script_info returns the args and env that should
  * be passed to the hotplug script for the requested device.
@@ -3958,6 +3973,9 @@ static inline void libxl__update_config_vtpm(libxl__gc *gc,
 #define COMPARE_PCI(a, b) ((a)->func == (b)->func &&    \
                            (a)->bus == (b)->bus &&      \
                            (a)->dev == (b)->dev)
+#define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \
+                           (a)->port == (b)->port)
+#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid)
 
 /* DEVICE_ADD
  *
diff --git a/tools/libxl/libxl_osdeps.h b/tools/libxl/libxl_osdeps.h
index d9661c9..802c762 100644
--- a/tools/libxl/libxl_osdeps.h
+++ b/tools/libxl/libxl_osdeps.h
@@ -24,6 +24,8 @@
 #define _GNU_SOURCE
 
 #if defined(__NetBSD__)
+#define SYSFS_USB_DEV          "/sys/bus/usb/devices"
+#define SYSFS_USBBACK_DRIVER   "/kern/xen/usb"
 #define SYSFS_PCI_DEV          "/sys/bus/pci/devices"
 #define SYSFS_PCIBACK_DRIVER   "/kern/xen/pci"
 #define NETBACK_NIC_NAME       "xvif%ui%d"
@@ -31,6 +33,8 @@
 #elif defined(__OpenBSD__)
 #include <util.h>
 #elif defined(__linux__)
+#define SYSFS_USB_DEV          "/sys/bus/usb/devices"
+#define SYSFS_USBBACK_DRIVER   "/sys/bus/usb/drivers/usbback"
 #define SYSFS_PCI_DEV          "/sys/bus/pci/devices"
 #define SYSFS_PCIBACK_DRIVER   "/sys/bus/pci/drivers/pciback"
 #define NETBACK_NIC_NAME       "vif%u.%d"
@@ -38,6 +42,8 @@
 #elif defined(__sun__)
 #include <stropts.h>
 #elif defined(__FreeBSD__)
+#define SYSFS_USB_DEV          "/dev/null"
+#define SYSFS_USBBACK_DRIVER   "/dev/null"
 #define SYSFS_PCI_DEV          "/dev/null"
 #define SYSFS_PCIBACK_DRIVER   "/dev/null"
 #define NETBACK_NIC_NAME       "xnb%u.%d"
@@ -45,6 +51,13 @@
 #include <sys/endian.h>
 #endif
 
+#ifndef SYSFS_USBBACK_DRIVER
+#error define SYSFS_USBBACK_DRIVER for your platform
+#endif
+#ifndef SYSFS_USB_DEV
+#error define SYSFS_USB_DEV for your platform
+#endif
+
 #ifndef SYSFS_PCIBACK_DRIVER
 #error define SYSFS_PCIBACK_DRIVER for your platform
 #endif
diff --git a/tools/libxl/libxl_pvusb.c b/tools/libxl/libxl_pvusb.c
new file mode 100644
index 0000000..e35c6b5
--- /dev/null
+++ b/tools/libxl/libxl_pvusb.c
@@ -0,0 +1,1534 @@
+/*
+ * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
+ * Author Chunyan Liu <cyliu@suse.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+#include <inttypes.h>
+
+#define USBBACK_INFO_PATH "/libxl/usbback"
+
+#define USBHUB_CLASS_CODE 9
+
+static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid,
+                                            libxl_device_usbctrl *usbctrl)
+{
+    int rc;
+    libxl_domain_type domtype = libxl__domain_type(gc, domid);
+
+    if (!usbctrl->version)
+        usbctrl->version = 2;
+
+    if (!usbctrl->ports)
+        usbctrl->ports = 8;
+
+    if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) {
+        if (domtype == LIBXL_DOMAIN_TYPE_PV) {
+            usbctrl->type = LIBXL_USBCTRL_TYPE_PV;
+        } else if (domtype == LIBXL_DOMAIN_TYPE_HVM) {
+            /* FIXME: See if we can detect PV frontend */
+            usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL;
+        }
+    }
+
+    rc = libxl__resolve_domid(gc, usbctrl->backend_domname,
+                              &usbctrl->backend_domid);
+    return rc;
+}
+
+int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid,
+                               libxl_device_usbctrl *usbctrl,
+                               libxl__device *device)
+{
+    device->backend_devid   = usbctrl->devid;
+    device->backend_domid   = usbctrl->backend_domid;
+    device->backend_kind    = LIBXL__DEVICE_KIND_VUSB;
+    device->devid           = usbctrl->devid;
+    device->domid           = domid;
+    device->kind            = LIBXL__DEVICE_KIND_VUSB;
+
+    return 0;
+}
+
+/* Add usbctrl information to xenstore.
+ *
+ * Adding a usb controller will add a new 'vusb' device in xenstore, and
+ * add corresponding frontend, backend information to it. According to
+ * "update_json", decide wether to update json config file.
+ */
+static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t domid,
+                                              libxl_device_usbctrl *usbctrl,
+                                              bool update_json)
+{
+    libxl__device *device;
+    flexarray_t *front;
+    flexarray_t *back;
+    xs_transaction_t t = XBT_NULL;
+    int i, rc;
+    libxl_domain_config d_config;
+    libxl_device_usbctrl usbctrl_saved;
+    libxl__domain_userdata_lock *lock = NULL;
+
+    libxl_domain_config_init(&d_config);
+    libxl_device_usbctrl_init(&usbctrl_saved);
+    libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl);
+
+    GCNEW(device);
+    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
+    if (rc) goto out;
+
+    front = flexarray_make(gc, 4, 1);
+    back = flexarray_make(gc, 12, 1);
+
+    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid));
+    flexarray_append_pair(back, "online", "1");
+    flexarray_append_pair(back, "state",
+                          GCSPRINTF("%d", XenbusStateInitialising));
+    flexarray_append_pair(back, "type",
+                          (char *)libxl_usbctrl_type_to_string(usbctrl->type));
+    flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version));
+    flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports));
+    flexarray_append_pair(back, "port", "");
+    for (i = 0; i < usbctrl->ports; i++)
+        flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), "");
+
+    flexarray_append_pair(front, "backend-id",
+                          GCSPRINTF("%d", usbctrl->backend_domid));
+    flexarray_append_pair(front, "state",
+                          GCSPRINTF("%d", XenbusStateInitialising));
+
+    if (update_json) {
+        lock = libxl__lock_domain_userdata(gc, domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+            goto out;
+        }
+
+        rc = libxl__get_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        DEVICE_ADD(usbctrl, usbctrls, domid, &usbctrl_saved,
+                   COMPARE_USBCTRL, &d_config);
+    }
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        rc = libxl__device_exists(gc, t, device);
+        if (rc < 0) goto out;
+        if (rc == 1) {
+            /* already exists in xenstore */
+            LOG(ERROR, "device already exists in xenstore");
+            rc = ERROR_DEVICE_EXISTS;
+            goto out;
+        }
+
+        if (update_json) {
+            rc = libxl__set_domain_configuration(gc, domid, &d_config);
+            if (rc) goto out;
+        }
+
+        libxl__device_generic_add(gc, t, device,
+                          libxl__xs_kvs_of_flexarray(gc, back, back->count),
+                          libxl__xs_kvs_of_flexarray(gc, front, front->count),
+                          NULL);
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    if (lock) libxl__unlock_domain_userdata(lock);
+    libxl_device_usbctrl_dispose(&usbctrl_saved);
+    libxl_domain_config_dispose(&d_config);
+    return rc;
+}
+
+/* AO operation to add a usb controller.
+ *
+ * Generally, it does:
+ * 1) fill in necessary usb controler information with default value
+ * 2) write usb controller frontend/backend info to xenstore, update json
+ *    config file if necessary.
+ * 3) wait for device connection. PVUSB frontend and backend driver will
+ *    probe xenstore paths and build connection between frontend and backend.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, aodev->update_json, ...
+ */
+void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
+                               libxl_device_usbctrl *usbctrl,
+                               libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl__device *device;
+    int rc;
+
+    rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl);
+    if (rc < 0) goto out;
+
+    if (usbctrl->devid == -1) {
+        usbctrl->devid = libxl__device_nextid(gc, domid, "vusb");
+        if (usbctrl->devid < 0) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    if (usbctrl->type != LIBXL_USBCTRL_TYPE_PV) {
+        LOG(ERROR, "Unsupported USB controller type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
+                                            aodev->update_json);
+    if (rc) goto out;
+
+    GCNEW(device);
+    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device);
+    if (rc) goto out;
+
+    aodev->dev = device;
+    aodev->action = LIBXL__DEVICE_ACTION_ADD;
+    libxl__wait_device_connection(egc, aodev);
+    return;
+
+out:
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+    return;
+}
+
+static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t domid,
+                                                 libxl_devid usbctrl,
+                                                 libxl_device_usbdev **usbdevs,
+                                                 int *num);
+
+static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid,
+                                       libxl_device_usbdev *usbdev);
+
+/* AO function to remove a usb controller.
+ *
+ * Generally, it does:
+ * 1) check if the usb controller exists or not
+ * 2) remove all usb devices under controller
+ * 3) remove usb controller information from xenstore
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->dev, aodev->callback, ...
+ */
+void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
+                                           libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl_ctx *ctx = CTX;
+    libxl_device_usbdev *usbdevs = NULL;
+    int num_usbdev = 0;
+    int i, rc;
+    uint32_t domid = ao->domid;
+    int usbctrl_devid = aodev->dev->devid;
+    libxl_device_usbctrl usbctrl;
+    libxl_usbctrlinfo usbctrlinfo;
+
+    libxl_device_usbctrl_init(&usbctrl);
+    libxl_usbctrlinfo_init(&usbctrlinfo);
+    usbctrl.devid = usbctrl_devid;
+
+    rc = libxl_device_usbctrl_getinfo(ctx, domid, &usbctrl, &usbctrlinfo);
+    if (rc) goto out;
+
+    if (usbctrlinfo.type != LIBXL_USBCTRL_TYPE_PV) {
+        LOG(ERROR, "Unsupported USB controller type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* Remove usb devices first */
+    rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid,
+                                               &usbdevs, &num_usbdev);
+    if (rc) goto out;
+
+    for (i = 0; i < num_usbdev; i++) {
+        if (libxl__device_usbdev_remove(gc, domid, &usbdevs[i])) {
+            LOG(ERROR, "libxl__device_usbdev_remove failed: controller %d, "
+                "port %d", usbdevs[i].ctrl, usbdevs[i].port);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    libxl_device_usbctrl_dispose(&usbctrl);
+    libxl_usbctrlinfo_dispose(&usbctrlinfo);
+
+    /* Remove usbctrl */
+    return libxl__initiate_device_remove(egc, aodev);
+
+out:
+    libxl_device_usbctrl_dispose(&usbctrl);
+    libxl_usbctrlinfo_dispose(&usbctrlinfo);
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+    return;
+}
+
+libxl_device_usbctrl *
+libxl_device_usbctrl_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usbctrl *usbctrls = NULL;
+    char *path = NULL;
+    char **entry = NULL;
+    unsigned int nentries = 0;
+
+    *num = 0;
+
+    path = GCSPRINTF("%s/device/vusb",
+                     libxl__xs_get_dompath(gc, domid));
+    entry = libxl__xs_directory(gc, XBT_NULL, path, &nentries);
+
+    if (entry && nentries) {
+        usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * nentries);
+        libxl_device_usbctrl *usbctrl;
+        libxl_device_usbctrl *end = usbctrls + nentries;
+        for (usbctrl = usbctrls;
+             usbctrl < end;
+             usbctrl++, entry++, (*num)++) {
+            const char *tmp, *be_path, *fe_path;
+            int ret;
+
+            libxl_device_usbctrl_init(usbctrl);
+            usbctrl->devid = atoi(*entry);
+
+#define READ_SUBPATH(path, subpath) ({                                  \
+        ret = libxl__xs_read_checked(gc, XBT_NULL,                      \
+                                     GCSPRINTF("%s/" subpath, path),    \
+                                     &tmp);                             \
+        if (ret) goto out;                                              \
+        (char *)tmp;                                                    \
+    })
+
+#define READ_SUBPATH_INT(path, subpath) ({                              \
+        ret = libxl__xs_read_checked(gc, XBT_NULL,                      \
+                                     GCSPRINTF("%s/" subpath, path),    \
+                                     &tmp);                             \
+        if (ret) goto out;                                              \
+        tmp ? atoi(tmp) : -1;                                           \
+    })
+
+            fe_path = GCSPRINTF("%s/%s", path, *entry);
+            be_path = READ_SUBPATH(fe_path, "backend");
+            usbctrl->backend_domid = READ_SUBPATH_INT(fe_path, "backend-id");
+            usbctrl->version = READ_SUBPATH_INT(be_path, "usb-ver");
+            usbctrl->ports = READ_SUBPATH_INT(be_path, "num-ports");
+            libxl_usbctrl_type_from_string(READ_SUBPATH(be_path, "type"),
+                                           &usbctrl->type);
+
+#undef READ_SUBPATH
+#undef READ_SUBPATH_INT
+       }
+    }
+
+    GC_FREE;
+    return usbctrls;
+
+out:
+    LOG(ERROR, "Unable to list USB Controllers");
+    libxl_device_usbctrl_list_free(usbctrls, *num);
+    GC_FREE;
+    *num = 0;
+    return NULL;
+}
+
+int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_usbctrl *usbctrl,
+                                 libxl_usbctrlinfo *usbctrlinfo)
+{
+    GC_INIT(ctx);
+    const char *dompath, *fe_path, *be_path, *tmp;
+    int rc;
+
+    usbctrlinfo->devid = usbctrl->devid;
+
+#define READ_SUBPATH(path, subpath) ({                                  \
+        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \
+                                    GCSPRINTF("%s/" subpath, path),     \
+                                    &tmp);                              \
+        if (rc) goto out;                                               \
+        (char *)tmp;                                                    \
+    })
+
+#define READ_SUBPATH_INT(path, subpath) ({                              \
+        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \
+                                    GCSPRINTF("%s/" subpath, path),     \
+                                    &tmp);                              \
+        if (rc) goto out;                                               \
+        tmp ? atoi(tmp) : -1;                                           \
+    })
+
+    dompath = libxl__xs_get_dompath(gc, domid);
+    fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrl->devid);
+    be_path = READ_SUBPATH(fe_path, "backend");
+    usbctrlinfo->backend = libxl__strdup(NOGC, be_path);
+    usbctrlinfo->backend_id = READ_SUBPATH_INT(fe_path, "backend-id");
+    usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state");
+    usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel");
+    usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
+    usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref");
+    tmp = READ_SUBPATH(be_path, "frontend");
+    usbctrlinfo->frontend = libxl__strdup(NOGC, tmp);
+    usbctrlinfo->frontend_id = READ_SUBPATH_INT(be_path, "frontend-id");
+    usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports");
+    usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver");;
+    tmp = READ_SUBPATH(be_path, "type");
+    libxl_usbctrl_type_from_string(tmp, &usbctrlinfo->type);
+
+#undef READ_SUBPATH
+#undef READ_SUBPATH_INT
+
+    rc = 0;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+int libxl_devid_to_device_usbctrl(libxl_ctx *ctx,
+                                  uint32_t domid,
+                                  int devid,
+                                  libxl_device_usbctrl *usbctrl)
+{
+    libxl_device_usbctrl *usbctrls;
+    int nb = 0;
+    int i, rc;
+
+    usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb);
+    if (!usbctrls)
+        return ERROR_FAIL;
+
+    rc = ERROR_FAIL;
+    for (i = 0; i < nb; i++) {
+        if (devid == usbctrls[i].devid) {
+            libxl_device_usbctrl_copy(ctx, usbctrl, &usbctrls[i]);
+            rc = 0;
+            break;
+        }
+    }
+
+    libxl_device_usbctrl_list_free(usbctrls, nb);
+    return rc;
+}
+
+static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr)
+{
+    DIR *dir;
+    char *busid = NULL;
+
+    /* invalid hostbus or hostaddr */
+    if (bus < 1 || addr < 1)
+        return NULL;
+
+    dir = opendir(SYSFS_USB_DEV);
+    if (!dir) {
+        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
+        return NULL;
+    }
+
+    size_t need = offsetof(struct dirent, d_name) +
+        pathconf(SYSFS_USB_DEV, _PC_NAME_MAX) + 1;
+    struct dirent *de_buf = libxl__zalloc(gc, need);
+    struct dirent *de;
+
+    for (;;) {
+        char *filename;
+        void *buf;
+        int busnum = -1;
+        int devnum = -1;
+
+        int r = readdir_r(dir, de_buf, &de);
+        if (r) {
+            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
+            break;
+        }
+        if (!de)
+            break;
+
+        if (!strcmp(de->d_name, ".") ||
+            !strcmp(de->d_name, ".."))
+            continue;
+
+        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", de->d_name);
+        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+            devnum = atoi(buf);
+
+        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", de->d_name);
+        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+            busnum = atoi(buf);
+
+        if (bus == busnum && addr == devnum) {
+            busid = libxl__strdup(gc, de->d_name);
+            break;
+        }
+    }
+
+    closedir(dir);
+    return busid;
+}
+
+static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid,
+                                     uint8_t *bus, uint8_t *addr)
+{
+    char *filename;
+    void *buf;
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid);
+    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+        *bus = atoi((char *)buf);
+    else
+        return ERROR_FAIL;
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid);
+    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+        *addr = atoi((char *)buf);
+    else
+        return ERROR_FAIL;
+
+    return 0;
+}
+
+static int get_assigned_devices(libxl__gc *gc,
+                                libxl_device_usbdev **list, int *num)
+{
+    char **domlist;
+    unsigned int ndom = 0;
+    int i, j, k;
+    int rc;
+
+    *list = NULL;
+    *num = 0;
+
+    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom);
+    for (i = 0; i < ndom; i++) {
+        char *path;
+        char **usbctrls;
+        unsigned int nc = 0;
+        uint32_t domid = atoi(domlist[i]);
+
+        path = GCSPRINTF("%s/device/vusb", libxl__xs_get_dompath(gc, domid));
+        usbctrls = libxl__xs_directory(gc, XBT_NULL, path, &nc);
+
+        for (j = 0; j < nc; j++) {
+            libxl_device_usbdev *tmp = NULL;
+            int nd = 0;
+
+            rc = libxl__device_usbdev_list_for_usbctrl(gc, domid,
+                                                       atoi(usbctrls[j]),
+                                                       &tmp, &nd);
+            if (rc) goto out;
+
+            if (!nd) continue;
+
+            GCREALLOC_ARRAY(*list, *num + nd);
+            for (k = 0; k < nd; k++) {
+                libxl_device_usbdev_copy(CTX, *list + *num, tmp + k);
+                (*num)++;
+            }
+        }
+    }
+
+    return 0;
+
+out:
+    LOG(ERROR, "fail to get assigned devices");
+    return rc;
+}
+
+static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num,
+                               libxl_device_usbdev *usbdev)
+{
+    int i;
+
+    for (i = 0; i < num; i++) {
+        if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus &&
+            usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr)
+            return true;
+    }
+
+    return false;
+}
+
+/* check if USB device type is assignable */
+static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev *usbdev)
+{
+    int classcode;
+    char *filename;
+    void *buf = NULL;
+    char *busid = NULL;
+
+    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+                                    usbdev->u.hostdev.hostaddr);
+    if (!busid) return false;
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", busid);
+    if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL))
+        return false;
+
+    classcode = atoi(buf);
+    return classcode != USBHUB_CLASS_CODE;
+}
+
+/* get usb devices under certain usb controller */
+static int
+libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc,
+                                      uint32_t domid,
+                                      libxl_devid usbctrl,
+                                      libxl_device_usbdev **usbdevs,
+                                      int *num)
+{
+    const char *fe_path, *be_path, *num_devs;
+    int n, i, rc;
+
+    *usbdevs = NULL;
+    *num = 0;
+
+    fe_path = GCSPRINTF("%s/device/vusb/%d",
+                        libxl__xs_get_dompath(gc, domid), usbctrl);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/backend", fe_path),
+                                &be_path);
+    if (rc) goto out;
+
+    if (!be_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/num-ports", be_path),
+                                &num_devs);
+    if (rc) goto out;
+
+    n = num_devs ? atoi(num_devs) : 0;
+
+    for (i = 0; i < n; i++) {
+        const char *busid;
+        libxl_device_usbdev *usbdev;
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/port/%d", be_path, i + 1),
+                                    &busid);
+        if (rc) goto out;
+
+        if (busid && strcmp(busid, "")) {
+            GCREALLOC_ARRAY(*usbdevs, *num + 1);
+            usbdev = *usbdevs + *num;
+            (*num)++;
+            libxl_device_usbdev_init(usbdev);
+            usbdev->ctrl = usbctrl;
+            usbdev->port = i + 1;
+            usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+            rc = usbdev_busaddr_from_busid(gc, busid,
+                                           &usbdev->u.hostdev.hostbus,
+                                           &usbdev->u.hostdev.hostaddr);
+            if (rc) goto out;
+        }
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/* get all usb devices of the domain */
+libxl_device_usbdev *
+libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usbdev *usbdevs = NULL;
+    const char *path;
+    char **usbctrls;
+    unsigned int nc = 0;
+    int i, j;
+
+    *num = 0;
+
+    path = GCSPRINTF("%s/device/vusb",
+                        libxl__xs_get_dompath(gc, domid));
+    usbctrls = libxl__xs_directory(gc, XBT_NULL, path, &nc);
+
+    for (i = 0; i < nc; i++) {
+        int r, nd = 0;
+        libxl_device_usbdev *tmp = NULL;
+
+        r = libxl__device_usbdev_list_for_usbctrl(gc, domid,
+                                                  atoi(usbctrls[i]),
+                                                  &tmp, &nd);
+        if (!r || !nd) continue;
+
+        usbdevs = libxl__realloc(NOGC, usbdevs,
+                                 sizeof(*usbdevs) * (*num + nd));
+        for (j = 0; j < nd; j++) {
+            libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j);
+            (*num)++;
+        }
+    }
+
+    GC_FREE;
+    return usbdevs;
+}
+
+/* find first unused controller:port and give that to usb device */
+static int
+libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid,
+                                         libxl_device_usbdev *usbdev)
+{
+    libxl_device_usbctrl *usbctrls = NULL;
+    int numctrl = 0;
+    int i, j, rc;
+
+    usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl);
+    if (!numctrl || !usbctrls) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = ERROR_FAIL;
+    for (i = 0; i < numctrl; i++) {
+        for (j = 0; j < usbctrls[i].ports; j++) {
+            const char *path, *tmp;
+
+            path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d",
+                             libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                             domid, usbctrls[i].devid, j + 1);
+            tmp = libxl__xs_read(gc, XBT_NULL, path);
+            if (tmp && !strcmp(tmp, "")) {
+                usbdev->ctrl = usbctrls[i].devid;
+                usbdev->port = j + 1;
+                rc = 0;
+                goto out;
+            }
+        }
+    }
+
+out:
+    libxl_device_usbctrl_list_free(usbctrls, numctrl);
+    return rc;
+}
+
+/* Fill in usb information with default value.
+ *
+ * Generally, it does:
+ * 1) if "controller" is not specified:
+ *    - if "port" is not specified, try to find an available controller:port,
+ *      if found, use that; otherwise, create a new controller, use this
+ *      controller and its first port
+ *    - if "port" is specified, report error.
+ * 2) if "controller" is specified, but port is not specified:
+ *    try to find an available port under this controller, if found, use
+ *    that, otherwise, report error.
+ * 3) if both "controller" and "port" are specified:
+ *    check the controller:port is available, if not, report error.
+ */
+static int libxl__device_usbdev_setdefault(libxl__gc *gc,
+                                           uint32_t domid,
+                                           libxl_device_usbdev *usbdev,
+                                           bool update_json)
+{
+    int rc;
+
+    if (!usbdev->type)
+        usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+
+    if (usbdev->ctrl == -1) {
+        if (usbdev->port) {
+            LOG(ERROR, "USB controller must be specified if you specify port");
+            return ERROR_INVAL;
+        }
+
+        rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev);
+        /* If no existing controller to host this usb device, add a new one */
+        if (rc) {
+            libxl_device_usbctrl *usbctrl;
+
+            GCNEW(usbctrl);
+            libxl_device_usbctrl_init(usbctrl);
+            rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl);
+            if (rc < 0) goto out;
+
+            if (usbctrl->devid == -1) {
+                usbctrl->devid = libxl__device_nextid(gc, domid, "vusb");
+                if (usbctrl->devid < 0) {
+                    rc = ERROR_FAIL;
+                    goto out;
+                }
+            }
+
+            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
+                                                    update_json);
+            if (rc) goto out;
+
+            usbdev->ctrl = usbctrl->devid;
+            usbdev->port = 1;
+        }
+    } else if (!usbdev->port) {
+        /* Valid port starts from 1. Choose port for us. */
+        int i, ports;
+        const char *fe_path, *be_path, *tmp;
+
+        fe_path = GCSPRINTF("%s/device/vusb/%d",
+                         libxl__xs_get_dompath(gc, domid), usbdev->ctrl);
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/backend", fe_path), &be_path);
+        if (rc) goto out;
+
+        if (!be_path) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/num-ports", be_path), &tmp);
+        if (rc) goto out;
+
+        ports = tmp ? atoi(tmp) : 0;
+
+        for (i = 0; i < ports; i++) {
+            rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                        GCSPRINTF("%s/port/%d", be_path, i + 1),
+                                        &tmp);
+            if (rc) goto out;
+
+            if (tmp && !strcmp(tmp, "")) {
+                usbdev->port = i + 1;
+                break;
+            }
+        }
+
+        if (!usbdev->port) {
+            LOG(ERROR, "No available port under specified controller");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    } else {
+        const char *be_path, *tmp;
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/device/vusb/%d/backend",
+                                              libxl__xs_get_dompath(gc, domid),
+                                              usbdev->ctrl),
+                                    &be_path);
+        if (rc) goto out;
+
+        if (!be_path) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                    GCSPRINTF("%s/port/%d",
+                                              be_path, usbdev->port),
+                                    &tmp);
+        if (rc) goto out;
+
+        if (tmp && strcmp(tmp, "")) {
+            LOG(ERROR, "The controller port isn't available");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/* Add usb information to xenstore
+ *
+ * Adding a usb device won't create new 'vusb' device, but only write
+ * the device busid to the controller:port in xenstore.
+ */
+static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid,
+                                             libxl_device_usbdev *usbdev,
+                                             bool update_json)
+{
+    char *be_path, *busid;
+    int rc;
+    xs_transaction_t t = XBT_NULL;
+    libxl_domain_config d_config;
+    libxl_device_usbdev usbdev_saved;
+    libxl__domain_userdata_lock *lock = NULL;
+
+    libxl_domain_config_init(&d_config);
+    libxl_device_usbdev_init(&usbdev_saved);
+    libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev);
+
+    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+                                    usbdev->u.hostdev.hostaddr);
+    if (!busid) {
+        LOG(DEBUG, "Fail to get busid of usb device");
+        goto out;
+    }
+
+    if (update_json) {
+        lock = libxl__lock_domain_userdata(gc, domid);
+        if (!lock) {
+            rc = ERROR_LOCK_FAIL;
+            goto out;
+        }
+
+        rc = libxl__get_domain_configuration(gc, domid, &d_config);
+        if (rc) goto out;
+
+        DEVICE_ADD(usbdev, usbdevs, domid, &usbdev_saved,
+                   COMPARE_USB, &d_config);
+    }
+
+    for (;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        if (update_json) {
+            rc = libxl__set_domain_configuration(gc, domid, &d_config);
+            if (rc) goto out;
+        }
+
+        be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d",
+                            libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                            domid, usbdev->ctrl, usbdev->port);
+
+        LOG(DEBUG, "Adding usb device %s to xenstore: controller %d, port %d",
+            busid, usbdev->ctrl, usbdev->port);
+
+        if (libxl__xs_write_checked(gc, t, be_path, busid))
+            goto out;
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    rc = 0;
+
+out:
+    if (lock) libxl__unlock_domain_userdata(lock);
+    libxl_device_usbdev_dispose(&usbdev_saved);
+    libxl_domain_config_dispose(&d_config);
+    return rc;
+}
+
+static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t domid,
+                                                libxl_device_usbdev *usbdev)
+{
+    char *be_path;
+
+    be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d",
+                        libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                        domid, usbdev->ctrl, usbdev->port);
+
+    LOG(DEBUG, "Removing usb device from xenstore: controller %d, port %d",
+        usbdev->ctrl, usbdev->port);
+
+    return libxl__xs_write_checked(gc, XBT_NULL, be_path, "");
+}
+
+static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_usbdev *usbdev)
+{
+    return libxl__xs_read(gc, XBT_NULL,
+                          GCSPRINTF("%s/backend/vusb/%d/%d/port/%d",
+                              libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                          domid, usbdev->ctrl, usbdev->port));
+}
+
+/* get original driver path of usb interface, stored in @drvpath */
+static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char **drvpath)
+{
+    char *spath, *dp = NULL;
+    struct stat st;
+    int rc;
+
+    spath = GCSPRINTF(SYSFS_USB_DEV"/%s/driver", intf);
+
+    rc = lstat(spath, &st);
+    if (rc == 0) {
+        /* Find the canonical path to the driver. */
+        dp = libxl__zalloc(gc, PATH_MAX);
+        dp = realpath(spath, dp);
+    } else if (errno != ENOENT) {
+        LOGE(ERROR, "lstat failed: '%s'", spath);
+        return ERROR_FAIL;
+    }
+
+    *drvpath = dp;
+
+    return 0;
+}
+
+static int sysfs_write_intf(libxl__gc *gc, const char *intf, const char *path)
+{
+    int rc, fd = -1;
+
+    fd = open(path, O_WRONLY);
+    if (fd < 0) {
+        LOGE(ERROR, "open file failed: '%s'", path);
+        return ERROR_FAIL;
+    }
+
+    rc = write(fd, intf, strlen(intf));
+    close(fd);
+    if (rc < 0) {
+        LOGE(ERROR, "write '%s' to '%s' failed", intf, path);
+        return errno;
+    }
+    if (rc != strlen(intf)) {
+        LOG(ERROR, "write '%s' to '%s' failed: incorrect write count",
+            intf, path);
+        return ERROR_FAIL;
+    }
+
+    return 0;
+}
+
+static int unbind_usbintf(libxl__gc *gc, const char *intf)
+{
+    char *path;
+
+    path = GCSPRINTF(SYSFS_USB_DEV"/%s/driver/unbind", intf);
+    return sysfs_write_intf(gc, intf, path);
+}
+
+static int bind_usbintf(libxl__gc *gc, const char *intf, const char *drvpath)
+{
+    char *path;
+    struct stat st;
+
+    path = GCSPRINTF("%s/%s", drvpath, intf);
+    /* if already bound, return */
+    if (!lstat(path, &st))
+        return 0;
+
+    path = GCSPRINTF("%s/bind", drvpath);
+    return sysfs_write_intf(gc, intf, path);
+}
+
+/* Is usb interface bound to usbback? */
+static int usbintf_is_assigned(libxl__gc *gc, char *intf)
+{
+    char *spath;
+    int rc;
+    struct stat st;
+
+    spath = GCSPRINTF(SYSFS_USBBACK_DRIVER"/%s", intf);
+    rc = lstat(spath, &st);
+
+    if (rc == 0)
+        return 1;
+    if (rc < 0 && errno == ENOENT)
+        return 0;
+    LOGE(ERROR, "Accessing %s", spath);
+    return -1;
+}
+
+static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid,
+                                     char ***intfs, int *num)
+{
+    DIR *dir;
+    char *buf;
+    int rc;
+
+    *intfs = NULL;
+    *num = 0;
+
+    buf = GCSPRINTF("%s:", busid);
+
+    dir = opendir(SYSFS_USB_DEV);
+    if (!dir) {
+        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV);
+        return ERROR_FAIL;
+    }
+
+    size_t need = offsetof(struct dirent, d_name) +
+        pathconf(SYSFS_USB_DEV, _PC_NAME_MAX) + 1;
+    struct dirent *de_buf = libxl__zalloc(gc, need);
+    struct dirent *de;
+
+    for (;;) {
+        int r = readdir_r(dir, de_buf, &de);
+
+        if (r) {
+            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+        if (!de)
+            break;
+
+        if (!strcmp(de->d_name, ".") ||
+            !strcmp(de->d_name, ".."))
+            continue;
+
+        if (!strncmp(de->d_name, buf, strlen(buf))) {
+            GCREALLOC_ARRAY(*intfs, *num + 1);
+            (*intfs)[*num] = libxl__strdup(gc, de->d_name);
+            (*num)++;
+        }
+    }
+
+    rc = 0;
+
+out:
+    closedir(dir);
+    return rc;
+}
+
+/* Encode usb interface so that it could be written to xenstore as a key.
+ *
+ * Since xenstore key cannot include '.' or ':', we'll change '.' to '_',
+ * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1.
+ * This will be used to save original driver of USB device to xenstore.
+ */
+static char *usb_interface_xenstore_encode(libxl__gc *gc, const char *busid)
+{
+    char *str = libxl__strdup(gc, busid);
+    int i, len = strlen(str);
+
+    for (i = 0; i < len; i++) {
+        if (str[i] == '.')
+            str[i] = '_';
+        if (str[i] == ':')
+            str[i] = '@';
+    }
+    return str;
+}
+
+/* Unbind USB device from "usbback" driver.
+ *
+ * If there are many interfaces under USB device, check each interface,
+ * unbind from "usbback" driver and rebind to its original driver.
+ */
+static int usbback_dev_unassign(libxl__gc *gc, const char *busid)
+{
+    char **intfs = NULL;
+    char *usbdev_encode = NULL;
+    char *path = NULL;
+    int i, num = 0;
+    int rc;
+
+    if (usbdev_get_all_interfaces(gc, busid, &intfs, &num) < 0)
+        return ERROR_FAIL;
+
+    usbdev_encode = usb_interface_xenstore_encode(gc, busid);
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+        char *usbintf_encode = NULL;
+        const char *drvpath;
+
+        /* check if the USB interface is already bound to "usbback" */
+        if (usbintf_is_assigned(gc, intf) > 0) {
+            /* unbind interface from usbback driver */
+            if (unbind_usbintf(gc, intf) < 0) {
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+
+        /* try to rebind USB interface to its originial driver.
+         * If rebinding failed, export warning so that user can
+         * handle it later.
+         */
+        usbintf_encode = usb_interface_xenstore_encode(gc, intf);
+        path = GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path",
+                         usbdev_encode, usbintf_encode);
+        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath);
+        if (rc) continue;
+
+        if (drvpath && bind_usbintf(gc, intf, drvpath))
+            LOGE(WARN, "Couldn't rebind %s to %s", intf, drvpath);
+    }
+
+    /* finally, remove xenstore driver path */
+    path = GCSPRINTF(USBBACK_INFO_PATH"/%s", usbdev_encode);
+    libxl__xs_rm_checked(gc, XBT_NULL, path);
+    rc = 0;
+
+out:
+    return rc;
+}
+
+/* Bind USB device to "usbback" driver.
+ *
+ * If there are many interfaces under USB device, check each interface,
+ * unbind from original driver and bind to "usbback" driver.
+ */
+static int usbback_dev_assign(libxl__gc *gc, const char *busid)
+{
+    char **intfs = NULL;
+    int num = 0, i;
+    int rc;
+    char *usbdev_encode = NULL;
+
+    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num);
+    if (rc)
+        return rc;
+
+    usbdev_encode = usb_interface_xenstore_encode(gc, busid);
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+        char *drvpath = NULL;
+
+        /* already assigned to usbback */
+        if (usbintf_is_assigned(gc, intf) > 0)
+            continue;
+
+        rc = usbintf_get_drvpath(gc, intf, &drvpath);
+        if (rc < 0) goto out;
+
+        if (drvpath) {
+            /* write driver path to xenstore for later rebinding */
+            char *usbintf_encode = NULL;
+            char *path;
+
+            usbintf_encode = usb_interface_xenstore_encode(gc, intf);
+            path = GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path",
+                             usbdev_encode, usbintf_encode);
+            if (libxl__xs_write_checked(gc, XBT_NULL, path, drvpath) < 0)
+                goto out;
+
+            /* unbind interface from original driver */
+            if (unbind_usbintf(gc, intf) < 0) {
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+
+        /* bind interface to usbback */
+        if (bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER) < 0) {
+            LOG(ERROR, "Couldn't bind %s to %s", intf, SYSFS_USBBACK_DRIVER);
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    return 0;
+
+out:
+    /* some interfaces might be bound to usbback, unbind it and
+     * rebind it to its original driver
+     */
+    usbback_dev_unassign(gc, busid);
+    return rc;
+}
+
+static int do_usbdev_add(libxl__gc *gc, uint32_t domid,
+                         libxl_device_usbdev *usbdev,
+                         bool update_json)
+{
+    int rc;
+    char *busid;
+    libxl_device_usbctrl usbctrl;
+    libxl_usbctrlinfo usbctrlinfo;
+
+    libxl_device_usbctrl_init(&usbctrl);
+    libxl_usbctrlinfo_init(&usbctrlinfo);
+    usbctrl.devid = usbdev->ctrl;
+
+    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo);
+    if (rc) goto out;
+
+    switch (usbctrlinfo.type) {
+    case LIBXL_USBCTRL_TYPE_PV:
+        busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus,
+                                        usbdev->u.hostdev.hostaddr);
+        if (!busid) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev, update_json);
+        if (rc) goto out;
+
+        rc = usbback_dev_assign(gc, busid);
+        if (rc) {
+            libxl__device_usbdev_remove_xenstore(gc, domid, usbdev);
+            goto out;
+        }
+        break;
+    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+    default:
+        LOG(ERROR, "Unsupported usb controller type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    libxl_device_usbctrl_dispose(&usbctrl);
+    libxl_usbctrlinfo_dispose(&usbctrlinfo);
+    return rc;
+}
+
+/* AO operation to add a usb device.
+ *
+ * Generally, it does:
+ * 1) check if the usb device type is assignable
+ * 2) check if the usb device is already assigned to a domain
+ * 3) add 'busid' of the usb device to xenstore contoller/port/.
+ *    (PVUSB driver watches the xenstore changes and will detect that.)
+ * 4) unbind usb device from original driver and bind to usbback.
+ *    If usb device has many interfaces, then:
+ *    - unbind each interface from its original driver and bind to usbback.
+ *    - store the original driver to xenstore for later rebinding when
+ *      detaching the device.
+ *
+ * Before calling this function, aodev should be properly filled:
+ * aodev->ao, aodev->callback, aodev->update_json, ...
+ */
+void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid,
+                              libxl_device_usbdev *usbdev,
+                              libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    int rc;
+    libxl_device_usbdev *assigned;
+    int num_assigned;
+    libxl_device_usbctrl usbctrl;
+    libxl_usbctrlinfo usbctrlinfo;
+
+    libxl_device_usbctrl_init(&usbctrl);
+    libxl_usbctrlinfo_init(&usbctrlinfo);
+
+    /* Currently only support adding USB device from Dom0 backend.
+     * So, if USB controller is specified, check its backend domain,
+     * if it's not Dom0, report error.
+     */
+    if (usbdev->ctrl != -1) {
+        usbctrl.devid = usbdev->ctrl;
+        rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo);
+        if (rc) goto out;
+
+        if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) {
+            LOG(ERROR, "Don't support adding USB device from non-Dom0 backend");
+            rc = ERROR_INVAL;
+            goto out;
+        }
+    }
+
+    /* check usb device is assignable type */
+    if (!is_usbdev_assignable(gc, usbdev)) {
+        LOG(ERROR, "USB device is not assignable.");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* check usb device is already assigned */
+    rc = get_assigned_devices(gc, &assigned, &num_assigned);
+    if (rc) {
+        LOG(ERROR, "cannot determine if device is assigned,"
+                   " refusing to continue");
+        goto out;
+    }
+
+    if (is_usbdev_in_array(assigned, num_assigned, usbdev)) {
+        LOG(ERROR, "USB device already attached to a domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* fill default values, e.g, if usbdev->ctrl and usbdev->port
+     * not specified, choose available controller:port and fill in. */
+    rc = libxl__device_usbdev_setdefault(gc, domid, usbdev,
+                                         aodev->update_json);
+    if (rc) goto out;
+
+    /* do actual adding usb device operation */
+    rc = do_usbdev_add(gc, domid, usbdev, aodev->update_json);
+
+out:
+    libxl_device_usbctrl_dispose(&usbctrl);
+    libxl_usbctrlinfo_dispose(&usbctrlinfo);
+    aodev->rc = rc;
+    aodev->callback(egc, aodev);
+    return;
+}
+
+static int do_usbdev_remove(libxl__gc *gc, uint32_t domid,
+                            libxl_device_usbdev *usbdev)
+{
+    int rc;
+    char *busid;
+    libxl_device_usbctrl usbctrl;
+    libxl_usbctrlinfo usbctrlinfo;
+
+    libxl_device_usbctrl_init(&usbctrl);
+    libxl_usbctrlinfo_init(&usbctrlinfo);
+    usbctrl.devid = usbdev->ctrl;
+
+    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo);
+    if (rc) goto out;
+
+    switch (usbctrlinfo.type) {
+    case LIBXL_USBCTRL_TYPE_PV:
+        busid = usbdev_busid_from_ctrlport(gc, domid, usbdev);
+        if (!busid) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev);
+        if (rc) goto out;
+
+        rc = usbback_dev_unassign(gc, busid);
+        if (rc) {
+            /* Till here, usb device information is already removed
+             * from xenstore, usb list couldn't list it any more.
+             * If unassign usb device from usbback failed, export
+             * warning only so that user could handle driver status
+             * later.
+             */
+            LOG(WARN, "unbind usb device from usbback and rebind to its "
+                      "original driver failed");
+        }
+        break;
+    case LIBXL_USBCTRL_TYPE_DEVICEMODEL:
+    default:
+        LOG(ERROR, "Unsupported usb controller type");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    libxl_device_usbctrl_dispose(&usbctrl);
+    libxl_usbctrlinfo_dispose(&usbctrlinfo);
+    return rc;
+}
+
+/* Operation to remove usb device.
+ *
+ * Generally, it does:
+ * 1) check if the usb device is assigned to the domain
+ * 2) remove the usb device from xenstore controller/port.
+ * 3) unbind usb device from usbback and rebind to its original driver.
+ *    If usb device has many interfaces, do it to each interface.
+ */
+static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid,
+                                       libxl_device_usbdev *usbdev)
+{
+    libxl_usbctrlinfo usbctrlinfo;
+    libxl_device_usbctrl usbctrl;
+    int rc;
+
+    if (usbdev->ctrl < 0 || usbdev->port < 1) {
+        LOG(ERROR, "Invalid USB device");
+        return ERROR_FAIL;
+    }
+
+    libxl_device_usbctrl_init(&usbctrl);
+    libxl_usbctrlinfo_init(&usbctrlinfo);
+    usbctrl.devid = usbdev->ctrl;
+
+    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo);
+    if (rc) goto out;
+
+    if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) {
+        LOG(ERROR, "Don't support removing USB device from non-Dom0 backend");
+        rc = ERROR_INVAL;
+        goto out;
+    }
+
+    /* do actual removing usb device operation */
+    rc = do_usbdev_remove(gc, domid, usbdev);
+
+out:
+    libxl_device_usbctrl_dispose(&usbctrl);
+    libxl_usbctrlinfo_dispose(&usbctrlinfo);
+    return rc;
+}
+
+int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid,
+                               libxl_device_usbdev *usbdev,
+                               const libxl_asyncop_how *ao_how)
+
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    rc = libxl__device_usbdev_remove(gc, domid, usbdev);
+
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx,
+                                    uint32_t domid,
+                                    int ctrl,
+                                    int port,
+                                    libxl_device_usbdev *usbdev)
+{
+    GC_INIT(ctx);
+    const char *dompath, *be_path, *busid;
+    int rc;
+
+    dompath = libxl__xs_get_dompath(gc, domid);
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                  GCSPRINTF("%s/device/vusb/%d/backend", dompath, ctrl),
+                  &be_path);
+    if (rc) goto out;
+
+    if (!be_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                           GCSPRINTF("%s/port/%d", be_path, port),
+                           &busid);
+    if (rc) goto out;
+
+    if (!busid || !strcmp(busid, "")) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    usbdev->ctrl = ctrl;
+    usbdev->port = port;
+    usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV;
+    rc = usbdev_busaddr_from_busid(gc, busid,
+                                   &usbdev->u.hostdev.hostbus,
+                                   &usbdev->u.hostdev.hostaddr);
+
+out:
+    GC_FREE;
+    return rc;
+}
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index 4d78f86..ed7da81 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -603,6 +603,35 @@ libxl_device_rdm = Struct("device_rdm", [
     ("policy", libxl_rdm_reserve_policy),
     ])
 
+libxl_usbctrl_type = Enumeration("usbctrl_type", [
+    (0, "AUTO"),
+    (1, "PV"),
+    (2, "DEVICEMODEL"),
+    ])
+
+libxl_usbdev_type = Enumeration("usbdev_type", [
+    (1, "hostdev"),
+    ])
+
+libxl_device_usbctrl = Struct("device_usbctrl", [
+    ("type", libxl_usbctrl_type),
+    ("devid", libxl_devid),
+    ("version", integer),
+    ("ports", integer),
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+   ])
+
+libxl_device_usbdev = Struct("device_usbdev", [
+    ("ctrl", libxl_devid),
+    ("port", integer),
+    ("u", KeyedUnion(None, libxl_usbdev_type, "type",
+           [("hostdev", Struct(None, [
+                 ("hostbus",   uint8),
+                 ("hostaddr",  uint8)])),
+           ])),
+    ])
+
 libxl_device_dtdev = Struct("device_dtdev", [
     ("path", string),
     ])
@@ -641,6 +670,8 @@ libxl_domain_config = Struct("domain_config", [
     # a channel manifests as a console with a name,
     # see docs/misc/channels.txt
     ("channels", Array(libxl_device_channel, "num_channels")),
+    ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")),
+    ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")),
 
     ("on_poweroff", libxl_action_on_shutdown),
     ("on_reboot", libxl_action_on_shutdown),
@@ -684,6 +715,21 @@ libxl_vtpminfo = Struct("vtpminfo", [
     ("uuid", libxl_uuid),
     ], dir=DIR_OUT)
 
+libxl_usbctrlinfo = Struct("usbctrlinfo", [
+    ("type", libxl_usbctrl_type),
+    ("devid", libxl_devid),
+    ("version", integer),
+    ("ports", integer),
+    ("backend", string),
+    ("backend_id", uint32),
+    ("frontend", string),
+    ("frontend_id", uint32),
+    ("state", integer),
+    ("evtch", integer),
+    ("ref_urb", integer),
+    ("ref_conn", integer),
+    ], dir=DIR_OUT)
+
 libxl_vcpuinfo = Struct("vcpuinfo", [
     ("vcpuid", uint32),
     ("cpu", uint32),
diff --git a/tools/libxl/libxl_types_internal.idl b/tools/libxl/libxl_types_internal.idl
index 5e55685..696f5f8 100644
--- a/tools/libxl/libxl_types_internal.idl
+++ b/tools/libxl/libxl_types_internal.idl
@@ -22,6 +22,7 @@ libxl__device_kind = Enumeration("device_kind", [
     (6, "VKBD"),
     (7, "CONSOLE"),
     (8, "VTPM"),
+    (9, "VUSB"),
     ])
 
 libxl__console_backend = Enumeration("console_backend", [
diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c
index 7f612a6..f7ba880 100644
--- a/tools/libxl/libxl_utils.c
+++ b/tools/libxl/libxl_utils.c
@@ -1314,6 +1314,24 @@ int libxl__random_bytes(libxl__gc *gc, uint8_t *buf, size_t len)
     return ret;
 }
 
+void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr)
+{
+   int i;
+
+   for (i = 0; i < nr; i++)
+       libxl_device_usbctrl_dispose(&list[i]);
+   free(list);
+}
+
+void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr)
+{
+   int i;
+
+   for (i = 0; i < nr; i++)
+       libxl_device_usbdev_dispose(&list[i]);
+   free(list);
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h
index 339ebdf..4495417 100644
--- a/tools/libxl/libxl_utils.h
+++ b/tools/libxl/libxl_utils.h
@@ -76,6 +76,11 @@ int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
                                libxl_uuid *uuid, libxl_device_vtpm *vtpm);
 int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid,
                                int devid, libxl_device_vtpm *vtpm);
+int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid,
+                                  int devid, libxl_device_usbctrl *usbctrl);
+int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid,
+                                    int ctrl, int port,
+                                    libxl_device_usbdev *usbdev);
 
 int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits);
     /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RESEND][PATCH V9 4/7] libxl: add libxl_device_usbdev_assignable_list API
  2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (2 preceding siblings ...)
  2015-11-25  9:46 ` [RESEND][PATCH V9 3/7] libxl: add pvusb API Chunyan Liu
@ 2015-11-25  9:46 ` Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 5/7] xl: add pvusb commands Chunyan Liu
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig

Add API for listing assignable USB devices info.
Assignable USB device means the USB device type is assignable and
it's not assigned to any guest yet.

Signed-off-by: Chunyan Liu <cyliu@suse.com>

---
This could be squashed with previous patch. Split because there is
some dispute on this. If this is acceptable, could be squashed,
otherwise could be removed.

Changes:
  - update usb device naming

 tools/libxl/libxl.h       |  2 ++
 tools/libxl/libxl_pvusb.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 609d068..d659ec3 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -1479,6 +1479,8 @@ int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
                                  libxl_usbctrlinfo *usbctrlinfo);
 
 /* USB Devices */
+libxl_device_usbdev *
+libxl_device_usbdev_assignable_list(libxl_ctx *ctx, int *num);
 
 int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid,
                             libxl_device_usbdev *usbdev,
diff --git a/tools/libxl/libxl_pvusb.c b/tools/libxl/libxl_pvusb.c
index e35c6b5..b0f0808 100644
--- a/tools/libxl/libxl_pvusb.c
+++ b/tools/libxl/libxl_pvusb.c
@@ -592,6 +592,68 @@ static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev *usbdev)
     return classcode != USBHUB_CLASS_CODE;
 }
 
+libxl_device_usbdev *
+libxl_device_usbdev_assignable_list(libxl_ctx *ctx, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usbdev *usbdevs = NULL;
+    libxl_device_usbdev *assigned;
+    int num_assigned;
+    DIR *dir;
+    int r;
+
+    *num = 0;
+
+    r = get_assigned_devices(gc, &assigned, &num_assigned);
+    if (r) {
+        LOG(ERROR, "cannot determine if device is assigned");
+        goto out;
+    }
+
+    dir = opendir(SYSFS_USB_DEV);
+    if (!dir) goto out;
+
+    size_t need = offsetof(struct dirent, d_name) +
+        pathconf(SYSFS_USB_DEV, _PC_NAME_MAX) + 1;
+    struct dirent *de_buf = libxl__zalloc(gc, need);
+    struct dirent *de;
+
+    while (readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
+        libxl_device_usbdev *usbdev;
+        uint8_t bus, addr;
+
+        if (!strcmp(de->d_name, ".") ||
+            !strcmp(de->d_name, ".."))
+            continue;
+
+        if (usbdev_busaddr_from_busid(gc, de->d_name, &bus, &addr))
+            continue;
+
+        GCNEW(usbdev);
+        usbdev->u.hostdev.hostbus = bus;
+        usbdev->u.hostdev.hostaddr = addr;
+
+        if (!is_usbdev_assignable(gc, usbdev))
+            continue;
+
+        if (is_usbdev_in_array(assigned, num_assigned, usbdev))
+            continue;
+
+        usbdevs = libxl__realloc(NOGC, usbdevs,
+                                 sizeof(*usbdevs) * (*num + 1));
+        libxl_device_usbdev_init(usbdevs + *num);
+        usbdevs[*num].u.hostdev.hostbus = bus;
+        usbdevs[*num].u.hostdev.hostaddr = addr;
+        (*num)++;
+    }
+
+    closedir(dir);
+
+out:
+    GC_FREE;
+    return usbdevs;
+}
+
 /* get usb devices under certain usb controller */
 static int
 libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc,
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RESEND][PATCH V9 5/7] xl: add pvusb commands
  2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (3 preceding siblings ...)
  2015-11-25  9:46 ` [RESEND][PATCH V9 4/7] libxl: add libxl_device_usbdev_assignable_list API Chunyan Liu
@ 2015-11-25  9:46 ` Chunyan Liu
  2015-12-09 16:42   ` George Dunlap
  2015-11-25  9:46 ` [RESEND][PATCH V9 6/7] xl: add usbdev-assignable-list command Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 7/7] domcreate: support pvusb in configuration file Chunyan Liu
  6 siblings, 1 reply; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig, Simon Cao

Add pvusb commands: usbctrl-attach, usbctrl-detach, usb-list,
usbdev-attach and usbdev-detach.

To attach a usb device to guest through pvusb, one could follow
following example:

 #xl usbctrl-attach test_vm version=1 ports=8

 #xl usb-list test_vm
 will show the usb controllers and port usage under the domain.

 #xl usbdev-attach test_vm hostbus=1 hostaddr=2
 will find the first usable controller:port, and attach usb
 device whose busnum is 1 and devnum is 6.
 One could also specify which <controller> and which <port>.

 #xl usbdev-detach test_vm 0 1
 will detach USB device under controller 0 port 1.

 #xl usbctrl-detach test_vm dev_id
 will destroy the controller with specified dev_id. Dev_id
 can be traced in usb-list info.

Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Simon Cao <caobosimon@gmail.com>

---
Changes:
  - use libxl_usbdev/usbctrl_type_from_string instead of
    comparing oparg string mannually.
  - update docs as George suggested

 docs/man/xl.pod.1         |  41 ++++++++
 tools/libxl/xl.h          |   5 +
 tools/libxl/xl_cmdimpl.c  | 243 ++++++++++++++++++++++++++++++++++++++++++++++
 tools/libxl/xl_cmdtable.c |  25 +++++
 4 files changed, 314 insertions(+)

diff --git a/docs/man/xl.pod.1 b/docs/man/xl.pod.1
index 4279c7c..746f49f 100644
--- a/docs/man/xl.pod.1
+++ b/docs/man/xl.pod.1
@@ -1345,6 +1345,47 @@ List pass-through pci devices for a domain.
 
 =back
 
+=head1 USB PASS-THROUGH
+
+=over 4
+
+=item B<usbctrl-attach> I<domain-id> I[<type=val>] [I<version=val>] [I<ports=number>]
+
+Create a new USB controller for the specified domain.
+B<type=val> is the usb controller type.  Currently only 'pv' and 'auto'
+are supported.
+B<version=val> is the usb controller version.  Possible values include
+1 (USB1.1) and 2 (USB2.0).
+B<ports=number> is the total ports of the usb controller. The maximum
+number is 31.
+By default, it will create a USB2.0 controller with 8 ports.
+
+=item B<usbctrl-detach> I<domain-id> I<devid>
+
+Destroy a USB controller from the specified domain.
+B<devid> is devid of the USB controller.
+
+=item B<usb-attach> I<domain-id> I<hostbus=busnum> I<hostaddr=devnum> [I<controller=devid> [I<port=number>]]
+
+Hot-plug a new pass-through USB device to the specified domain.
+B<hostbus> and B<hostaddr> are the bus and device number of the physical
+USB device to pass through.
+B<controller=devid> B<port=number> is the USB controller:port to hotplug the
+USB device to. By default, it will find the first available controller:port
+and use it; if there is no controller, a new controller will be created.
+
+=item B<usb-detach> I<domain-id> I<controller=devid> I<port=number>
+
+Hot-unplug a previously assigned USB device from a domain.
+B<controller=devid> and B<port=number> is USB controller:port in guest where the
+USB device is attached to.
+
+=item B<usb-list> I<domain-id>
+
+List pass-through usb devices for a domain.
+
+=back
+
 =head1 TMEM
 
 =over 4
diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h
index bdab125..309627a 100644
--- a/tools/libxl/xl.h
+++ b/tools/libxl/xl.h
@@ -92,6 +92,11 @@ int main_blockdetach(int argc, char **argv);
 int main_vtpmattach(int argc, char **argv);
 int main_vtpmlist(int argc, char **argv);
 int main_vtpmdetach(int argc, char **argv);
+int main_usbctrl_attach(int argc, char **argv);
+int main_usbctrl_detach(int argc, char **argv);
+int main_usbdev_attach(int argc, char **argv);
+int main_usbdev_detach(int argc, char **argv);
+int main_usblist(int argc, char **argv);
 int main_uptime(int argc, char **argv);
 int main_claims(int argc, char **argv);
 int main_tmem_list(int argc, char **argv);
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index 03442e1..f98e367 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -1255,6 +1255,58 @@ static void parse_vnuma_config(const XLU_Config *config,
     free(vcpu_parsed);
 }
 
+/* Parses usbctrl data and adds info into usbctrl
+ * Returns 1 if the input token does not match one of the keys
+ * or parsed values are not correct. Successful parse returns 0 */
+static int parse_usbctrl_config(libxl_device_usbctrl *usbctrl, char *token)
+{
+    char *oparg;
+
+    if (MATCH_OPTION("type", token, oparg)) {
+        if (libxl_usbctrl_type_from_string(oparg, &usbctrl->type)) {
+            fprintf(stderr, "Invalid usb controller type '%s'\n", oparg);
+            return 1;
+        }
+    } else if (MATCH_OPTION("version", token, oparg)) {
+        usbctrl->version = atoi(oparg);
+    } else if (MATCH_OPTION("ports", token, oparg)) {
+        usbctrl->ports = atoi(oparg);
+    } else {
+        fprintf(stderr, "Unknown string `%s' in usbctrl spec\n", token);
+        return 1;
+    }
+
+    return 0;
+}
+
+/* Parses usbdev data and adds info into usbdev
+ * Returns 1 if the input token does not match one of the keys
+ * or parsed values are not correct. Successful parse returns 0 */
+static int parse_usbdev_config(libxl_device_usbdev *usbdev, char *token)
+{
+    char *oparg;
+
+    if (MATCH_OPTION("type", token, oparg)) {
+        if (libxl_usbdev_type_from_string(oparg, &usbdev->type)) {
+            fprintf(stderr, "Invalid usb device type: %s\n", optarg);
+            return 1;
+        }
+    } else if (MATCH_OPTION("hostbus", token, oparg)) {
+        usbdev->u.hostdev.hostbus = strtoul(oparg, NULL, 0);
+    } else if (MATCH_OPTION("hostaddr", token, oparg)) {
+        usbdev->u.hostdev.hostaddr = strtoul(oparg, NULL, 0);
+    } else if (MATCH_OPTION("controller", token, oparg)) {
+        usbdev->ctrl = atoi(oparg);
+    } else if (MATCH_OPTION("port", token, oparg)) {
+        usbdev->port = atoi(oparg);
+    } else {
+        fprintf(stderr, "Unknown string `%s' in usbdev spec\n", token);
+        return 1;
+    }
+
+    return 0;
+}
+
 static void parse_config_data(const char *config_source,
                               const char *config_data,
                               int config_len,
@@ -3397,6 +3449,197 @@ int main_cd_insert(int argc, char **argv)
     return 0;
 }
 
+int main_usbctrl_attach(int argc, char **argv)
+{
+    uint32_t domid;
+    int opt, rc = 0;
+    libxl_device_usbctrl usbctrl;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usbctrl-attach", 1) {
+        /* No options */
+    }
+
+    domid = find_domain(argv[optind++]);
+
+    libxl_device_usbctrl_init(&usbctrl);
+
+    for (argv += optind, argc -= optind; argc > 0; ++argv, --argc) {
+        if (parse_usbctrl_config(&usbctrl, *argv))
+            return 1;
+    }
+
+    rc = libxl_device_usbctrl_add(ctx, domid, &usbctrl, 0);
+    if (rc) {
+        fprintf(stderr, "libxl_device_usbctrl_add failed.\n");
+        rc = 1;
+    }
+
+    libxl_device_usbctrl_dispose(&usbctrl);
+    return rc;
+}
+
+int main_usbctrl_detach(int argc, char **argv)
+{
+    uint32_t domid;
+    int opt, devid, rc;
+    libxl_device_usbctrl usbctrl;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usbctrl-detach", 2) {
+        /* No options */
+    }
+
+    domid = find_domain(argv[optind]);
+    devid = atoi(argv[optind+1]);
+
+    libxl_device_usbctrl_init(&usbctrl);
+    if (libxl_devid_to_device_usbctrl(ctx, domid, devid, &usbctrl)) {
+        fprintf(stderr, "Unknown device %s.\n", argv[optind+1]);
+        return 1;
+    }
+
+    rc = libxl_device_usbctrl_remove(ctx, domid, &usbctrl, 0);
+    if (rc) {
+        fprintf(stderr, "libxl_device_usbctrl_remove failed.\n");
+        rc = 1;
+    }
+
+    libxl_device_usbctrl_dispose(&usbctrl);
+    return rc;
+
+}
+
+int main_usbdev_attach(int argc, char **argv)
+{
+    uint32_t domid;
+    int opt, rc;
+    libxl_device_usbdev usbdev;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usbdev-attach", 2) {
+        /* No options */
+    }
+
+    libxl_device_usbdev_init(&usbdev);
+
+    domid = find_domain(argv[optind++]);
+
+    for (argv += optind, argc -= optind; argc > 0; ++argv, --argc) {
+        if (parse_usbdev_config(&usbdev, *argv))
+            return 1;
+    }
+
+    rc = libxl_device_usbdev_add(ctx, domid, &usbdev, 0);
+    if (rc) {
+        fprintf(stderr, "libxl_device_usbdev_add failed.\n");
+        rc = 1;
+    }
+
+    libxl_device_usbdev_dispose(&usbdev);
+    return rc;
+}
+
+int main_usbdev_detach(int argc, char **argv)
+{
+    uint32_t domid;
+    int ctrl, port;
+    int opt, rc = 1;
+    libxl_device_usbdev usbdev;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usbdev-detach", 3) {
+        /* No options */
+    }
+
+    domid = find_domain(argv[optind]);
+    ctrl = atoi(argv[optind+1]);
+    port = atoi(argv[optind+2]);
+
+    if (argc - optind > 3) {
+        fprintf(stderr, "Invalid arguments.\n");
+        return 1;
+    }
+
+    libxl_device_usbdev_init(&usbdev);
+    if (libxl_ctrlport_to_device_usbdev(ctx, domid, ctrl, port, &usbdev)) {
+        fprintf(stderr, "Unknown device at controller %d port %d.\n",
+                ctrl, port);
+        return 1;
+    }
+
+    rc = libxl_device_usbdev_remove(ctx, domid, &usbdev, 0);
+    if (rc) {
+        fprintf(stderr, "libxl_device_usbdev_remove failed.\n");
+        rc = 1;
+    }
+
+    libxl_device_usbdev_dispose(&usbdev);
+    return rc;
+}
+
+int main_usblist(int argc, char **argv)
+{
+    uint32_t domid;
+    libxl_device_usbctrl *usbctrls;
+    libxl_usbctrlinfo usbctrlinfo;
+    int numctrl, i, j, opt;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usb-list", 1) {
+        /* No options */
+    }
+
+    domid = find_domain(argv[optind++]);
+
+    if (argc > optind) {
+        fprintf(stderr, "Invalid arguments.\n");
+        exit(-1);
+    }
+
+    usbctrls = libxl_device_usbctrl_list(ctx, domid, &numctrl);
+    if (!usbctrls) {
+        return 0;
+    }
+
+    for (i = 0; i < numctrl; ++i) {
+        printf("%-6s %-6s %-3s %-5s %-7s %-5s %-30s\n",
+                "Devid", "Type", "BE", "state", "usb-ver", "ports", "BE-path");
+
+        libxl_usbctrlinfo_init(&usbctrlinfo);
+
+        if (!libxl_device_usbctrl_getinfo(ctx, domid,
+                                &usbctrls[i], &usbctrlinfo)) {
+            printf("%-6d %-6s %-3d %-5d %-7d %-5d %-30s\n",
+                    usbctrlinfo.devid,
+                    libxl_usbctrl_type_to_string(usbctrlinfo.type),
+                    usbctrlinfo.backend_id, usbctrlinfo.state,
+                    usbctrlinfo.version, usbctrlinfo.ports,
+                    usbctrlinfo.backend);
+
+            for (j = 1; j <= usbctrlinfo.ports; j++) {
+                libxl_device_usbdev usbdev;
+
+                libxl_device_usbdev_init(&usbdev);
+
+                printf("  Port %d:", j);
+
+                if (!libxl_ctrlport_to_device_usbdev(ctx, domid,
+                                                     usbctrlinfo.devid,
+                                                     j, &usbdev)) {
+                    printf(" Bus %03x Device %03x\n",
+                           usbdev.u.hostdev.hostbus,
+                           usbdev.u.hostdev.hostaddr);
+                } else {
+                    printf("\n");
+                }
+
+                libxl_device_usbdev_dispose(&usbdev);
+            }
+        }
+
+        libxl_usbctrlinfo_dispose(&usbctrlinfo);
+    }
+
+    libxl_device_usbctrl_list_free(usbctrls, numctrl);
+    return 0;
+}
+
 int main_console(int argc, char **argv)
 {
     uint32_t domid;
diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c
index fdc1ac6..b14b881 100644
--- a/tools/libxl/xl_cmdtable.c
+++ b/tools/libxl/xl_cmdtable.c
@@ -553,6 +553,31 @@ struct cmd_spec cmd_table[] = {
     },
 
 #endif
+    { "usbctrl-attach",
+      &main_usbctrl_attach, 0, 1,
+      "Create a virtual USB controller for a domain",
+      "<Domain> [type=pv] [version=<version>] [ports=<number>]",
+    },
+    { "usbctrl-detach",
+      &main_usbctrl_detach, 0, 1,
+      "Remove the virtual USB controller specified by <DevId> for a domain",
+      "<Domain> <DevId>",
+    },
+    { "usbdev-attach",
+      &main_usbdev_attach, 0, 1,
+      "Attach a USB device to a domain",
+      "<Domain> hostbus=<busnum> hostaddr=<devnum> [controller=<DevId> [port=<port>]]",
+    },
+    { "usbdev-detach",
+      &main_usbdev_detach, 0, 1,
+      "Detach a USB device from a domain",
+      "<Domain> <controller> <port>",
+    },
+    { "usb-list",
+      &main_usblist, 0, 0,
+      "List information about all USB controllers and devices for a domain",
+      "<Domain>",
+    },
 };
 
 int cmdtable_len = sizeof(cmd_table)/sizeof(struct cmd_spec);
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RESEND][PATCH V9 6/7] xl: add usbdev-assignable-list command
  2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (4 preceding siblings ...)
  2015-11-25  9:46 ` [RESEND][PATCH V9 5/7] xl: add pvusb commands Chunyan Liu
@ 2015-11-25  9:46 ` Chunyan Liu
  2015-11-25  9:46 ` [RESEND][PATCH V9 7/7] domcreate: support pvusb in configuration file Chunyan Liu
  6 siblings, 0 replies; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig

Add xl usbdev-assignable-list command to list assignable USB devices.
Assignable USB device means the USB device type is assignable and
it's not assigned to any guest yet.

Signed-off-by: Chunyan Liu <cyliu@suse.com>

---
  Same as "libxl: add libxl_device_usbdev_assignable_list API" patch,
  this patch could be sqaushed to previous one. Split because of
  some dispute. Could be squashed if acceptable, otherwise could
  be removed.

 tools/libxl/xl.h          |  1 +
 tools/libxl/xl_cmdimpl.c  | 28 ++++++++++++++++++++++++++++
 tools/libxl/xl_cmdtable.c |  4 ++++
 3 files changed, 33 insertions(+)

diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h
index 309627a..8418fff 100644
--- a/tools/libxl/xl.h
+++ b/tools/libxl/xl.h
@@ -92,6 +92,7 @@ int main_blockdetach(int argc, char **argv);
 int main_vtpmattach(int argc, char **argv);
 int main_vtpmlist(int argc, char **argv);
 int main_vtpmdetach(int argc, char **argv);
+int main_usbdev_assignable_list(int argc, char **argv);
 int main_usbctrl_attach(int argc, char **argv);
 int main_usbctrl_detach(int argc, char **argv);
 int main_usbdev_attach(int argc, char **argv);
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index f98e367..dfc3ad5 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -3449,6 +3449,34 @@ int main_cd_insert(int argc, char **argv)
     return 0;
 }
 
+static void usbdev_assignable_list(void)
+{
+    libxl_device_usbdev *usbdevs;
+    int num, i;
+
+    usbdevs = libxl_device_usbdev_assignable_list(ctx, &num);
+
+    for (i = 0; i < num; i++) {
+        printf("%d.%d\n",
+               usbdevs[i].u.hostdev.hostbus,
+               usbdevs[i].u.hostdev.hostaddr);
+    }
+
+    libxl_device_usbdev_list_free(usbdevs, num);
+}
+
+int main_usbdev_assignable_list(int argc, char **argv)
+{
+    int opt;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usbdev-assignable-list", 0) {
+        /* No options */
+    }
+
+    usbdev_assignable_list();
+    return 0;
+}
+
 int main_usbctrl_attach(int argc, char **argv)
 {
     uint32_t domid;
diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c
index b14b881..df4f6d9 100644
--- a/tools/libxl/xl_cmdtable.c
+++ b/tools/libxl/xl_cmdtable.c
@@ -578,6 +578,10 @@ struct cmd_spec cmd_table[] = {
       "List information about all USB controllers and devices for a domain",
       "<Domain>",
     },
+    { "usbdev-assignable-list",
+      &main_usbdev_assignable_list, 0, 0,
+      "List all assignable USB devices",
+    },
 };
 
 int cmdtable_len = sizeof(cmd_table)/sizeof(struct cmd_spec);
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RESEND][PATCH V9 7/7] domcreate: support pvusb in configuration file
  2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (5 preceding siblings ...)
  2015-11-25  9:46 ` [RESEND][PATCH V9 6/7] xl: add usbdev-assignable-list command Chunyan Liu
@ 2015-11-25  9:46 ` Chunyan Liu
  2015-12-09 16:52   ` George Dunlap
  6 siblings, 1 reply; 12+ messages in thread
From: Chunyan Liu @ 2015-11-25  9:46 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig, Simon Cao

Add code to support pvusb in domain config file. One could specify
usbctrl and usb in domain's configuration file and create domain,
then usb controllers will be created and usb device would be attached
to guest automatically.

One could specify usb controllers and usb devices in config file
like this:
usbctrl=['version=2,ports=4', 'version=1, ports=4', ]
usbdev=['hostbus=2, hostaddr=1, controller=0,port=1', ]

Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Simon Cao <caobosimon@gmail.com>
---
changes:
  - update docs
  - update usb device naming

 docs/man/xl.cfg.pod.5        | 84 ++++++++++++++++++++++++++++++++++++++++++++
 tools/libxl/libxl_create.c   | 73 ++++++++++++++++++++++++++++++++++++--
 tools/libxl/libxl_device.c   |  4 +++
 tools/libxl/libxl_internal.h |  8 +++++
 tools/libxl/xl_cmdimpl.c     | 55 ++++++++++++++++++++++++++++-
 5 files changed, 220 insertions(+), 4 deletions(-)

diff --git a/docs/man/xl.cfg.pod.5 b/docs/man/xl.cfg.pod.5
index b63846a..db00371 100644
--- a/docs/man/xl.cfg.pod.5
+++ b/docs/man/xl.cfg.pod.5
@@ -722,6 +722,90 @@ Note this may be overridden by rdm_policy option in PCI device configuration.
 
 =back
 
+=item B<usbctrl=[ "USBCTRL_SPEC_STRING", "USBCTRL_SPEC_STRING", ... ]>
+
+Specifies the USB controllers created for this guest. Each
+B<USB_SPEC_STRING> has the form C<KEY=VALUE,KEY=VALUE,...> where:
+
+=over 4
+
+=item B<KEY=VALUE>
+
+Possible B<KEY>s are:
+
+=over 4
+
+=item B<type=TYPE>
+
+Specifies the usb controller type.  Currently only 'pv' and 'auto'
+are supported.
+
+=item B<version=VERSION>
+
+Specifies the usb controller version.  Possible values include
+1 (USB1.1) and 2 (USB2.0). Default is 2 (USB2.0).
+
+=item B<ports=PORTS>
+
+Specifies the total ports of the usb controller. The maximum
+number is 31. Default is 8.
+
+USB controler ids start from 0.  In line with the USB spec, however,
+ports on a controller start from 1.
+
+E.g.
+usbctrl=["version=1,ports=4", "version=2,ports=8",]
+The first controller has:
+controller id = 0, and port 1,2,3,4.
+The second controller has:
+controller id = 1, and port 1,2,3,4,5,6,7,8.
+
+=back
+
+=back
+
+=item B<usbdev=[ "USB_SPEC_STRING", "USB_SPEC_STRING", ... ]>
+
+Specifiec the USB devices to be attached to the guest at boot. Each
+B<USB_SPEC_STRING> has the form C<KEY=VALUE,KEY=VALUE,...> where:
+
+=over 4
+
+=item B<KEY=VALUE>
+
+Possible B<KEY>s are:
+
+=over 4
+
+=item B<devtype=hostdev>
+
+Specifies USB device type. Currently only support 'hostdev'.
+
+=item B<hostbus=busnum>
+
+Specifies busnum of the USB device from the host perspective.
+
+=item B<hostaddr=devnum>
+
+Specifies devnum of the USB device from the host perspective.
+
+=item B<controller=CONTROLLER>
+
+Specifies USB controller id, to which controller the USB device is attached.
+
+=item B<port=PORT>
+
+Specifies USB port, to which port the USB device is attached. B<port=PORT>
+is valid only when B<controller=CONTROLLER> is specified.
+
+=back
+
+If no controller is specified, an available controller:port combination
+will be used.  If there are no available controller:port options,
+a new controller will be created.
+
+=back
+
 =item B<pci=[ "PCI_SPEC_STRING", "PCI_SPEC_STRING", ... ]>
 
 Specifies the host PCI devices to passthrough to this guest. Each B<PCI_SPEC_STRING>
diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
index f0fee00..b02bd3e 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -727,6 +727,10 @@ static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs,
 
 static void domcreate_attach_vtpms(libxl__egc *egc, libxl__multidev *multidev,
                                    int ret);
+static void domcreate_attach_usbctrls(libxl__egc *egc,
+                                      libxl__multidev *multidev, int ret);
+static void domcreate_attach_usbdevs(libxl__egc *egc, libxl__multidev *multidev,
+                                     int ret);
 static void domcreate_attach_pci(libxl__egc *egc, libxl__multidev *aodevs,
                                  int ret);
 static void domcreate_attach_dtdev(libxl__egc *egc,
@@ -1385,13 +1389,13 @@ static void domcreate_attach_vtpms(libxl__egc *egc,
    if (d_config->num_vtpms > 0) {
        /* Attach vtpms */
        libxl__multidev_begin(ao, &dcs->multidev);
-       dcs->multidev.callback = domcreate_attach_pci;
+       dcs->multidev.callback = domcreate_attach_usbctrls;
        libxl__add_vtpms(egc, ao, domid, d_config, &dcs->multidev);
        libxl__multidev_prepared(egc, &dcs->multidev, 0);
        return;
    }
 
-   domcreate_attach_pci(egc, multidev, 0);
+   domcreate_attach_usbctrls(egc, multidev, 0);
    return;
 
 error_out:
@@ -1399,6 +1403,69 @@ error_out:
    domcreate_complete(egc, dcs, ret);
 }
 
+static void domcreate_attach_usbctrls(libxl__egc *egc,
+                                      libxl__multidev *multidev, int ret)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
+    STATE_AO_GC(dcs->ao);
+    int domid = dcs->guest_domid;
+
+    libxl_domain_config *const d_config = dcs->guest_config;
+
+    if (ret) {
+        LOG(ERROR, "unable to add vtpm devices");
+        goto error_out;
+    }
+
+    if (d_config->num_usbctrls > 0) {
+        /* Attach usbctrls */
+        libxl__multidev_begin(ao, &dcs->multidev);
+        dcs->multidev.callback = domcreate_attach_usbdevs;
+        libxl__add_usbctrls(egc, ao, domid, d_config, &dcs->multidev);
+        libxl__multidev_prepared(egc, &dcs->multidev, 0);
+        return;
+    }
+
+    domcreate_attach_usbdevs(egc, multidev, 0);
+    return;
+
+error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
+
+static void domcreate_attach_usbdevs(libxl__egc *egc, libxl__multidev *multidev,
+                                int ret)
+{
+    libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
+    STATE_AO_GC(dcs->ao);
+    int domid = dcs->guest_domid;
+
+    libxl_domain_config *const d_config = dcs->guest_config;
+
+    if (ret) {
+        LOG(ERROR, "unable to add usbctrl devices");
+        goto error_out;
+    }
+
+    if (d_config->num_usbdevs > 0) {
+        /* Attach usbctrls */
+        libxl__multidev_begin(ao, &dcs->multidev);
+        dcs->multidev.callback = domcreate_attach_pci;
+        libxl__add_usbdevs(egc, ao, domid, d_config, &dcs->multidev);
+        libxl__multidev_prepared(egc, &dcs->multidev, 0);
+        return;
+    }
+
+    domcreate_attach_pci(egc, multidev, 0);
+    return;
+
+error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
 static void domcreate_attach_pci(libxl__egc *egc, libxl__multidev *multidev,
                                  int ret)
 {
@@ -1411,7 +1478,7 @@ static void domcreate_attach_pci(libxl__egc *egc, libxl__multidev *multidev,
     libxl_domain_config *const d_config = dcs->guest_config;
 
     if (ret) {
-        LOG(ERROR, "unable to add vtpm devices");
+        LOG(ERROR, "unable to add usb devices");
         goto error_out;
     }
 
diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c
index 6715c16..89baf1a 100644
--- a/tools/libxl/libxl_device.c
+++ b/tools/libxl/libxl_device.c
@@ -544,6 +544,8 @@ void libxl__multidev_prepared(libxl__egc *egc,
  * libxl__add_disks
  * libxl__add_nics
  * libxl__add_vtpms
+ * libxl__add_usbctrls
+ * libxl__add_usbs
  */
 
 #define DEFINE_DEVICES_ADD(type)                                        \
@@ -563,6 +565,8 @@ void libxl__multidev_prepared(libxl__egc *egc,
 DEFINE_DEVICES_ADD(disk)
 DEFINE_DEVICES_ADD(nic)
 DEFINE_DEVICES_ADD(vtpm)
+DEFINE_DEVICES_ADD(usbctrl)
+DEFINE_DEVICES_ADD(usbdev)
 
 #undef DEFINE_DEVICES_ADD
 
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 5b70c6e..fa605da 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -3302,6 +3302,14 @@ _hidden void libxl__add_vtpms(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
                              libxl_domain_config *d_config,
                              libxl__multidev *multidev);
 
+_hidden void libxl__add_usbctrls(libxl__egc *egc, libxl__ao *ao,
+                                 uint32_t domid, libxl_domain_config *d_config,
+                                 libxl__multidev *multidev);
+
+_hidden void libxl__add_usbdevs(libxl__egc *egc, libxl__ao *ao,
+                                uint32_t domid, libxl_domain_config *d_config,
+                                libxl__multidev *multidev);
+
 /*----- device model creation -----*/
 
 /* First layer; wraps libxl__spawn_spawn. */
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index dfc3ad5..d6c1273 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -1315,7 +1315,8 @@ static void parse_config_data(const char *config_source,
     const char *buf;
     long l, vcpus = 0;
     XLU_Config *config;
-    XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms;
+    XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms,
+                   *usbctrls, *usbdevs;
     XLU_ConfigList *channels, *ioports, *irqs, *iomem, *viridian, *dtdevs;
     int num_ioports, num_irqs, num_iomem, num_cpus, num_viridian;
     int pci_power_mgmt = 0;
@@ -2132,6 +2133,58 @@ skip_vfb:
         }
     }
 
+    if (!xlu_cfg_get_list(config, "usbctrl", &usbctrls, 0, 0)) {
+        d_config->num_usbctrls = 0;
+        d_config->usbctrls = NULL;
+        while ((buf = xlu_cfg_get_listitem(usbctrls, d_config->num_usbctrls))
+               != NULL) {
+            libxl_device_usbctrl *usbctrl;
+            char *buf2 = strdup(buf);
+            char *p;
+
+            usbctrl = ARRAY_EXTEND_INIT(d_config->usbctrls,
+                                        d_config->num_usbctrls,
+                                        libxl_device_usbctrl_init);
+            p = strtok(buf2, ",");
+            if (!p)
+                goto skip_usbctrl;
+            do {
+                while (*p == ' ')
+                    p++;
+                if (parse_usbctrl_config(usbctrl, p))
+                    exit(1);
+            } while ((p = strtok(NULL, ",")) != NULL);
+skip_usbctrl:
+            free(buf2);
+        }
+    }
+
+    if (!xlu_cfg_get_list(config, "usbdev", &usbdevs, 0, 0)) {
+        d_config->num_usbdevs = 0;
+        d_config->usbdevs = NULL;
+        while ((buf = xlu_cfg_get_listitem(usbdevs, d_config->num_usbdevs))
+               != NULL) {
+            libxl_device_usbdev *usbdev;
+            char *buf2 = strdup(buf);
+            char *p;
+
+            usbdev = ARRAY_EXTEND_INIT_NODEVID(d_config->usbdevs,
+                                               d_config->num_usbdevs,
+                                               libxl_device_usbdev_init);
+            p = strtok(buf2, ",");
+            if (!p)
+                goto skip_usbdev;
+            do {
+                while (*p == ' ')
+                    p++;
+                if (parse_usbdev_config(usbdev, p))
+                    exit(1);
+            } while ((p = strtok(NULL, ",")) != NULL);
+skip_usbdev:
+            free(buf2);
+        }
+    }
+
     switch (xlu_cfg_get_list(config, "cpuid", &cpuids, 0, 1)) {
     case 0:
         {
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RESEND][PATCH V9 3/7] libxl: add pvusb API
  2015-11-25  9:46 ` [RESEND][PATCH V9 3/7] libxl: add pvusb API Chunyan Liu
@ 2015-12-08  5:50   ` Chun Yan Liu
  2015-12-08 11:26     ` George Dunlap
  0 siblings, 1 reply; 12+ messages in thread
From: Chun Yan Liu @ 2015-12-08  5:50 UTC (permalink / raw)
  To: xen-devel, Chun Yan Liu
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap,
	Ian.Jackson, Jim Fehlig, Simon Cao

Any comments?

>>> On 11/25/2015 at 05:46 PM, in message
<1448444775-6974-4-git-send-email-cyliu@suse.com>, Chunyan Liu <cyliu@suse.com>
wrote: 
> Add pvusb APIs, including: 
>  - attach/detach (create/destroy) virtual usb controller. 
>  - attach/detach usb device 
>  - list usb controller and usb devices 
>  - some other helper functions 
>  
> Signed-off-by: Chunyan Liu <cyliu@suse.com> 
> Signed-off-by: Simon Cao <caobosimon@gmail.com> 
>  
> --- 
> changes: 
>   - update naming, all places indicating usb controller named 
>     as usbctrl, all places indicating usb device named as usbdev 
>   - update DEFINE_DEVICE_REMOVE instead of creating a new 
>     DEFINE_DEVICE_REMOVE_EXT 
>   - use libxl__xs_read_checked instead of libxl__xs_read 
>   - update local READ_SUBPATH(_INT) macros to include more common codes 
>   - save drvpath before unbind 
>   - get_assigned_devices: call libxl__device_usbdev_list_for_ctrl 
>     instead of doing all things from scratch 
>   - usb_interface_xenstore_encode: use special char to avoid confusion 
>   - usb readdir_r instead of readdir 
>   - check syscall errno 
>   - remove usbinfo definition 
>   - address other comments except: 
>     libxl__device_usbdev_add/remove and do_usbdev_add/remove, in previous 
>     discussion, we'd like to get usbctrlinfo once and pass usbctrlinfo to 
>     do_usbdev_add/remove. However, during update, adding usbdev process 
>     still needs to try twice to get usbctrlinfo. (Before set_default, 
>     if usbctrl doesn't exist it doesn't doing getting usbctrlinfo actually; 
>     after set_default, needs to get usbctrlinfo then). So, finally, just 
>     change codes to make adding/removing process symmetrical. 
>  
>  tools/libxl/Makefile                 |    2 +- 
>  tools/libxl/libxl.c                  |   50 +- 
>  tools/libxl/libxl.h                  |   77 ++ 
>  tools/libxl/libxl_device.c           |    5 +- 
>  tools/libxl/libxl_internal.h         |   18 + 
>  tools/libxl/libxl_osdeps.h           |   13 + 
>  tools/libxl/libxl_pvusb.c            | 1534  
> ++++++++++++++++++++++++++++++++++ 
>  tools/libxl/libxl_types.idl          |   46 + 
>  tools/libxl/libxl_types_internal.idl |    1 + 
>  tools/libxl/libxl_utils.c            |   18 + 
>  tools/libxl/libxl_utils.h            |    5 + 
>  11 files changed, 1766 insertions(+), 3 deletions(-) 
>  create mode 100644 tools/libxl/libxl_pvusb.c 
>  
> diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile 
> index 6ff5bee..a36145a 100644 
> --- a/tools/libxl/Makefile 
> +++ b/tools/libxl/Makefile 
> @@ -103,7 +103,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o  
> libxl_dm.o libxl_pci.o \ 
>  			libxl_stream_read.o libxl_stream_write.o \ 
>  			libxl_save_callout.o _libxl_save_msgs_callout.o \ 
>  			libxl_qmp.o libxl_event.o libxl_fork.o \ 
> -			libxl_dom_suspend.o $(LIBXL_OBJS-y) 
> +			libxl_dom_suspend.o libxl_pvusb.o $(LIBXL_OBJS-y) 
>  LIBXL_OBJS += libxl_genid.o 
>  LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o 
>   
> diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c 
> index eaa7d75..a479465 100644 
> --- a/tools/libxl/libxl.c 
> +++ b/tools/libxl/libxl.c 
> @@ -4144,6 +4144,36 @@ out: 
>      return rc; 
>  } 
>   
> +static void libxl__initiate_device_disk_remove(libxl__egc *egc, 
> +                                               libxl__ao_device *aodev) 
> +{ 
> +    return libxl__initiate_device_remove(egc, aodev); 
> +} 
> + 
> +static void libxl__initiate_device_nic_remove(libxl__egc *egc, 
> +                                              libxl__ao_device *aodev) 
> +{ 
> +    return libxl__initiate_device_remove(egc, aodev); 
> +} 
> + 
> +static void libxl__initiate_device_vtpm_remove(libxl__egc *egc, 
> +                                               libxl__ao_device *aodev) 
> +{ 
> +    return libxl__initiate_device_remove(egc, aodev); 
> +} 
> + 
> +static void libxl__initiate_device_vkb_remove(libxl__egc *egc, 
> +                                              libxl__ao_device *aodev) 
> +{ 
> +    return libxl__initiate_device_remove(egc, aodev); 
> +} 
> + 
> +static void libxl__initiate_device_vfb_remove(libxl__egc *egc, 
> +                                              libxl__ao_device *aodev) 
> +{ 
> +    return libxl__initiate_device_remove(egc, aodev); 
> +} 
> + 
>   
> /**************************************************************************** 
> **/ 
>   
>  /* Macro for defining device remove/destroy functions in a compact way */ 
> @@ -4158,6 +4188,8 @@ out: 
>   * libxl_device_vkb_destroy 
>   * libxl_device_vfb_remove 
>   * libxl_device_vfb_destroy 
> + * libxl_device_usbctrl_remove 
> + * libxl_device_usbctrl_destroy 
>   */ 
>  #define DEFINE_DEVICE_REMOVE(type, removedestroy, f)                    \ 
>      int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \ 
> @@ -4179,7 +4211,7 @@ out: 
>          aodev->dev = device;                                            \ 
>          aodev->callback = device_addrm_aocomplete;                      \ 
>          aodev->force = f;                                               \ 
> -        libxl__initiate_device_remove(egc, aodev);                      \ 
> +        libxl__initiate_device_##type##_remove(egc, aodev);             \ 
>                                                                          \ 
>      out:                                                                \ 
>          if (rc) return AO_CREATE_FAIL(rc);                                   
>   \ 
> @@ -4209,6 +4241,10 @@ DEFINE_DEVICE_REMOVE(vfb, destroy, 1) 
>  DEFINE_DEVICE_REMOVE(vtpm, remove, 0) 
>  DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) 
>   
> +/* usbctrl */ 
> +DEFINE_DEVICE_REMOVE(usbctrl, remove, 0) 
> +DEFINE_DEVICE_REMOVE(usbctrl, destroy, 1) 
> + 
>  /* channel/console hotunplug is not implemented. There are 2 possibilities: 
>   * 1. add support for secondary consoles to xenconsoled 
>   * 2. dynamically add/remove qemu chardevs via qmp messages. */ 
> @@ -4222,6 +4258,8 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) 
>   * libxl_device_disk_add 
>   * libxl_device_nic_add 
>   * libxl_device_vtpm_add 
> + * libxl_device_usbctrl_add 
> + * libxl_device_usbdev_add 
>   */ 
>   
>  #define DEFINE_DEVICE_ADD(type)                                         \ 
> @@ -4253,6 +4291,12 @@ DEFINE_DEVICE_ADD(nic) 
>  /* vtpm */ 
>  DEFINE_DEVICE_ADD(vtpm) 
>   
> +/* usbctrl */ 
> +DEFINE_DEVICE_ADD(usbctrl) 
> + 
> +/* usb */ 
> +DEFINE_DEVICE_ADD(usbdev) 
> + 
>  #undef DEFINE_DEVICE_ADD 
>   
>   
> /**************************************************************************** 
> **/ 
> @@ -6794,6 +6838,10 @@ int libxl_retrieve_domain_configuration(libxl_ctx  
> *ctx, uint32_t domid, 
>   
>      MERGE(pci, pcidevs, COMPARE_PCI, {}); 
>   
> +    MERGE(usbctrl, usbctrls, COMPARE_USBCTRL, {}); 
> + 
> +    MERGE(usbdev, usbdevs, COMPARE_USB, {}); 
> + 
>      /* Take care of removable device. We maintain invariant in the 
>       * insert / remove operation so that: 
>       * 1. if xenstore is "empty" while JSON is not, the result 
> diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h 
> index 168fedd..609d068 100644 
> --- a/tools/libxl/libxl.h 
> +++ b/tools/libxl/libxl.h 
> @@ -123,6 +123,12 @@ 
>  #define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1 
>   
>  /* 
> + * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices 
> + * through pvusb -- both hotplug and at domain creation time.. 
> + */ 
> +#define LIBXL_HAVE_PVUSB 1 
> + 
> +/* 
>   * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the 
>   * libxl_vendor_device field is present in the hvm sections of 
>   * libxl_domain_build_info. This field tells libxl which 
> @@ -1418,6 +1424,77 @@ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid,  
> libxl_device_disk *disk, 
>                         const libxl_asyncop_how *ao_how) 
>                         LIBXL_EXTERNAL_CALLERS_ONLY; 
>   
> +/* 
> + * USB 
> + * 
> + * For each device removed or added, one of these protocols is available: 
> + * - PV (i.e., PVUSB) 
> + * - DEVICEMODEL (i.e, qemu) 
> + * 
> + * PV is available for either PV or HVM domains.  DEVICEMODEL is only 
> + * available for HVM domains.  The caller can additionally specify 
> + * "AUTO", in which case the library will try to determine the best 
> + * protocol automatically. 
> + * 
> + * At the moment, the only protocol implemented is PV. 
> + * 
> + * One can add/remove USB controllers to/from guest, and attach/detach USB 
> + * devices to/from USB controllers. 
> + * 
> + * To add USB controllers and USB devices, one can adding USB controllers 
> + * first and then attaching USB devices to some USB controller, or adding 
> + * USB devices to guest directly, it will automatically create a USB 
> + * controller for USB devices to attach. 
> + * 
> + * To remove USB controllers or USB devices, one can remove USB devices 
> + * under USB controller one by one and then remove USB controller, or 
> + * remove USB controller directly, it will remove all USB devices under 
> + * it automatically. 
> + * 
> + */ 
> +/* USB Controllers*/ 
> +int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid, 
> +                             libxl_device_usbctrl *usbctrl, 
> +                             const libxl_asyncop_how *ao_how) 
> +                             LIBXL_EXTERNAL_CALLERS_ONLY; 
> + 
> +int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid, 
> +                                libxl_device_usbctrl *usbctrl, 
> +                                const libxl_asyncop_how *ao_how) 
> +                                LIBXL_EXTERNAL_CALLERS_ONLY; 
> + 
> +int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid, 
> +                                 libxl_device_usbctrl *usbctrl, 
> +                                 const libxl_asyncop_how *ao_how) 
> +                                 LIBXL_EXTERNAL_CALLERS_ONLY; 
> + 
> +libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx, 
> +                                                uint32_t domid, int *num); 
> + 
> +void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr); 
> + 
> + 
> +int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid, 
> +                                 libxl_device_usbctrl *usbctrl, 
> +                                 libxl_usbctrlinfo *usbctrlinfo); 
> + 
> +/* USB Devices */ 
> + 
> +int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid, 
> +                            libxl_device_usbdev *usbdev, 
> +                            const libxl_asyncop_how *ao_how) 
> +                            LIBXL_EXTERNAL_CALLERS_ONLY; 
> + 
> +int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid, 
> +                               libxl_device_usbdev *usbdev, 
> +                               const libxl_asyncop_how *ao_how) 
> +                               LIBXL_EXTERNAL_CALLERS_ONLY; 
> + 
> +libxl_device_usbdev * 
> +libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num); 
> + 
> +void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr); 
> + 
>  /* Network Interfaces */ 
>  int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic  
> *nic, 
>                           const libxl_asyncop_how *ao_how) 
> diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c 
> index 8bb5e93..6715c16 100644 
> --- a/tools/libxl/libxl_device.c 
> +++ b/tools/libxl/libxl_device.c 
> @@ -676,7 +676,10 @@ void libxl__devices_destroy(libxl__egc *egc,  
> libxl__devices_remove_state *drs) 
>                  aodev->action = LIBXL__DEVICE_ACTION_REMOVE; 
>                  aodev->dev = dev; 
>                  aodev->force = drs->force; 
> -                libxl__initiate_device_remove(egc, aodev); 
> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) 
> +                    libxl__initiate_device_usbctrl_remove(egc, aodev); 
> +                else 
> +                    libxl__initiate_device_remove(egc, aodev); 
>              } 
>          } 
>      } 
> diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h 
> index 7aff237..5b70c6e 100644 
> --- a/tools/libxl/libxl_internal.h 
> +++ b/tools/libxl/libxl_internal.h 
> @@ -2569,6 +2569,14 @@ _hidden void libxl__device_vtpm_add(libxl__egc *egc,  
> uint32_t domid, 
>                                     libxl_device_vtpm *vtpm, 
>                                     libxl__ao_device *aodev); 
>   
> +_hidden void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid, 
> +                                       libxl_device_usbctrl *usbctrl, 
> +                                       libxl__ao_device *aodev); 
> + 
> +_hidden void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid, 
> +                                      libxl_device_usbdev *usbdev, 
> +                                      libxl__ao_device *aodev); 
> + 
>  /* Internal function to connect a vkb device */ 
>  _hidden int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid, 
>                                    libxl_device_vkb *vkb); 
> @@ -2601,6 +2609,13 @@ _hidden void  
> libxl__wait_device_connection(libxl__egc*, 
>  _hidden void libxl__initiate_device_remove(libxl__egc *egc, 
>                                             libxl__ao_device *aodev); 
>   
> +_hidden int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid, 
> +                               libxl_device_usbctrl *usbctrl, 
> +                               libxl__device *device); 
> + 
> +_hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, 
> +                                                   libxl__ao_device  
> *aodev); 
> + 
>  /* 
>   * libxl__get_hotplug_script_info returns the args and env that should 
>   * be passed to the hotplug script for the requested device. 
> @@ -3958,6 +3973,9 @@ static inline void libxl__update_config_vtpm(libxl__gc  
> *gc, 
>  #define COMPARE_PCI(a, b) ((a)->func == (b)->func &&    \ 
>                             (a)->bus == (b)->bus &&      \ 
>                             (a)->dev == (b)->dev) 
> +#define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \ 
> +                           (a)->port == (b)->port) 
> +#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid) 
>   
>  /* DEVICE_ADD 
>   * 
> diff --git a/tools/libxl/libxl_osdeps.h b/tools/libxl/libxl_osdeps.h 
> index d9661c9..802c762 100644 
> --- a/tools/libxl/libxl_osdeps.h 
> +++ b/tools/libxl/libxl_osdeps.h 
> @@ -24,6 +24,8 @@ 
>  #define _GNU_SOURCE 
>   
>  #if defined(__NetBSD__) 
> +#define SYSFS_USB_DEV          "/sys/bus/usb/devices" 
> +#define SYSFS_USBBACK_DRIVER   "/kern/xen/usb" 
>  #define SYSFS_PCI_DEV          "/sys/bus/pci/devices" 
>  #define SYSFS_PCIBACK_DRIVER   "/kern/xen/pci" 
>  #define NETBACK_NIC_NAME       "xvif%ui%d" 
> @@ -31,6 +33,8 @@ 
>  #elif defined(__OpenBSD__) 
>  #include <util.h> 
>  #elif defined(__linux__) 
> +#define SYSFS_USB_DEV          "/sys/bus/usb/devices" 
> +#define SYSFS_USBBACK_DRIVER   "/sys/bus/usb/drivers/usbback" 
>  #define SYSFS_PCI_DEV          "/sys/bus/pci/devices" 
>  #define SYSFS_PCIBACK_DRIVER   "/sys/bus/pci/drivers/pciback" 
>  #define NETBACK_NIC_NAME       "vif%u.%d" 
> @@ -38,6 +42,8 @@ 
>  #elif defined(__sun__) 
>  #include <stropts.h> 
>  #elif defined(__FreeBSD__) 
> +#define SYSFS_USB_DEV          "/dev/null" 
> +#define SYSFS_USBBACK_DRIVER   "/dev/null" 
>  #define SYSFS_PCI_DEV          "/dev/null" 
>  #define SYSFS_PCIBACK_DRIVER   "/dev/null" 
>  #define NETBACK_NIC_NAME       "xnb%u.%d" 
> @@ -45,6 +51,13 @@ 
>  #include <sys/endian.h> 
>  #endif 
>   
> +#ifndef SYSFS_USBBACK_DRIVER 
> +#error define SYSFS_USBBACK_DRIVER for your platform 
> +#endif 
> +#ifndef SYSFS_USB_DEV 
> +#error define SYSFS_USB_DEV for your platform 
> +#endif 
> + 
>  #ifndef SYSFS_PCIBACK_DRIVER 
>  #error define SYSFS_PCIBACK_DRIVER for your platform 
>  #endif 
> diff --git a/tools/libxl/libxl_pvusb.c b/tools/libxl/libxl_pvusb.c 
> new file mode 100644 
> index 0000000..e35c6b5 
> --- /dev/null 
> +++ b/tools/libxl/libxl_pvusb.c 
> @@ -0,0 +1,1534 @@ 
> +/* 
> + * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. 
> + * Author Chunyan Liu <cyliu@suse.com> 
> + * 
> + * This program is free software; you can redistribute it and/or modify 
> + * it under the terms of the GNU Lesser General Public License as published 
> + * by the Free Software Foundation; version 2.1 only. with the special 
> + * exception on linking described in file LICENSE. 
> + * 
> + * This program is distributed in the hope that it will be useful, 
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of 
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
> + * GNU Lesser General Public License for more details. 
> + */ 
> + 
> +#include "libxl_osdeps.h" /* must come before any other headers */ 
> + 
> +#include "libxl_internal.h" 
> +#include <inttypes.h> 
> + 
> +#define USBBACK_INFO_PATH "/libxl/usbback" 
> + 
> +#define USBHUB_CLASS_CODE 9 
> + 
> +static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid, 
> +                                            libxl_device_usbctrl *usbctrl) 
> +{ 
> +    int rc; 
> +    libxl_domain_type domtype = libxl__domain_type(gc, domid); 
> + 
> +    if (!usbctrl->version) 
> +        usbctrl->version = 2; 
> + 
> +    if (!usbctrl->ports) 
> +        usbctrl->ports = 8; 
> + 
> +    if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) { 
> +        if (domtype == LIBXL_DOMAIN_TYPE_PV) { 
> +            usbctrl->type = LIBXL_USBCTRL_TYPE_PV; 
> +        } else if (domtype == LIBXL_DOMAIN_TYPE_HVM) { 
> +            /* FIXME: See if we can detect PV frontend */ 
> +            usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL; 
> +        } 
> +    } 
> + 
> +    rc = libxl__resolve_domid(gc, usbctrl->backend_domname, 
> +                              &usbctrl->backend_domid); 
> +    return rc; 
> +} 
> + 
> +int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid, 
> +                               libxl_device_usbctrl *usbctrl, 
> +                               libxl__device *device) 
> +{ 
> +    device->backend_devid   = usbctrl->devid; 
> +    device->backend_domid   = usbctrl->backend_domid; 
> +    device->backend_kind    = LIBXL__DEVICE_KIND_VUSB; 
> +    device->devid           = usbctrl->devid; 
> +    device->domid           = domid; 
> +    device->kind            = LIBXL__DEVICE_KIND_VUSB; 
> + 
> +    return 0; 
> +} 
> + 
> +/* Add usbctrl information to xenstore. 
> + * 
> + * Adding a usb controller will add a new 'vusb' device in xenstore, and 
> + * add corresponding frontend, backend information to it. According to 
> + * "update_json", decide wether to update json config file. 
> + */ 
> +static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t  
> domid, 
> +                                              libxl_device_usbctrl  
> *usbctrl, 
> +                                              bool update_json) 
> +{ 
> +    libxl__device *device; 
> +    flexarray_t *front; 
> +    flexarray_t *back; 
> +    xs_transaction_t t = XBT_NULL; 
> +    int i, rc; 
> +    libxl_domain_config d_config; 
> +    libxl_device_usbctrl usbctrl_saved; 
> +    libxl__domain_userdata_lock *lock = NULL; 
> + 
> +    libxl_domain_config_init(&d_config); 
> +    libxl_device_usbctrl_init(&usbctrl_saved); 
> +    libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl); 
> + 
> +    GCNEW(device); 
> +    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device); 
> +    if (rc) goto out; 
> + 
> +    front = flexarray_make(gc, 4, 1); 
> +    back = flexarray_make(gc, 12, 1); 
> + 
> +    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid)); 
> +    flexarray_append_pair(back, "online", "1"); 
> +    flexarray_append_pair(back, "state", 
> +                          GCSPRINTF("%d", XenbusStateInitialising)); 
> +    flexarray_append_pair(back, "type", 
> +                          (char  
> *)libxl_usbctrl_type_to_string(usbctrl->type)); 
> +    flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version)); 
> +    flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports)); 
> +    flexarray_append_pair(back, "port", ""); 
> +    for (i = 0; i < usbctrl->ports; i++) 
> +        flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), ""); 
> + 
> +    flexarray_append_pair(front, "backend-id", 
> +                          GCSPRINTF("%d", usbctrl->backend_domid)); 
> +    flexarray_append_pair(front, "state", 
> +                          GCSPRINTF("%d", XenbusStateInitialising)); 
> + 
> +    if (update_json) { 
> +        lock = libxl__lock_domain_userdata(gc, domid); 
> +        if (!lock) { 
> +            rc = ERROR_LOCK_FAIL; 
> +            goto out; 
> +        } 
> + 
> +        rc = libxl__get_domain_configuration(gc, domid, &d_config); 
> +        if (rc) goto out; 
> + 
> +        DEVICE_ADD(usbctrl, usbctrls, domid, &usbctrl_saved, 
> +                   COMPARE_USBCTRL, &d_config); 
> +    } 
> + 
> +    for (;;) { 
> +        rc = libxl__xs_transaction_start(gc, &t); 
> +        if (rc) goto out; 
> + 
> +        rc = libxl__device_exists(gc, t, device); 
> +        if (rc < 0) goto out; 
> +        if (rc == 1) { 
> +            /* already exists in xenstore */ 
> +            LOG(ERROR, "device already exists in xenstore"); 
> +            rc = ERROR_DEVICE_EXISTS; 
> +            goto out; 
> +        } 
> + 
> +        if (update_json) { 
> +            rc = libxl__set_domain_configuration(gc, domid, &d_config); 
> +            if (rc) goto out; 
> +        } 
> + 
> +        libxl__device_generic_add(gc, t, device, 
> +                          libxl__xs_kvs_of_flexarray(gc, back, back->count), 
> +                          libxl__xs_kvs_of_flexarray(gc, front,  
> front->count), 
> +                          NULL); 
> + 
> +        rc = libxl__xs_transaction_commit(gc, &t); 
> +        if (!rc) break; 
> +        if (rc < 0) goto out; 
> +    } 
> + 
> +out: 
> +    libxl__xs_transaction_abort(gc, &t); 
> +    if (lock) libxl__unlock_domain_userdata(lock); 
> +    libxl_device_usbctrl_dispose(&usbctrl_saved); 
> +    libxl_domain_config_dispose(&d_config); 
> +    return rc; 
> +} 
> + 
> +/* AO operation to add a usb controller. 
> + * 
> + * Generally, it does: 
> + * 1) fill in necessary usb controler information with default value 
> + * 2) write usb controller frontend/backend info to xenstore, update json 
> + *    config file if necessary. 
> + * 3) wait for device connection. PVUSB frontend and backend driver will 
> + *    probe xenstore paths and build connection between frontend and  
> backend. 
> + * 
> + * Before calling this function, aodev should be properly filled: 
> + * aodev->ao, aodev->callback, aodev->update_json, ... 
> + */ 
> +void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid, 
> +                               libxl_device_usbctrl *usbctrl, 
> +                               libxl__ao_device *aodev) 
> +{ 
> +    STATE_AO_GC(aodev->ao); 
> +    libxl__device *device; 
> +    int rc; 
> + 
> +    rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl); 
> +    if (rc < 0) goto out; 
> + 
> +    if (usbctrl->devid == -1) { 
> +        usbctrl->devid = libxl__device_nextid(gc, domid, "vusb"); 
> +        if (usbctrl->devid < 0) { 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> +    } 
> + 
> +    if (usbctrl->type != LIBXL_USBCTRL_TYPE_PV) { 
> +        LOG(ERROR, "Unsupported USB controller type"); 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, 
> +                                            aodev->update_json); 
> +    if (rc) goto out; 
> + 
> +    GCNEW(device); 
> +    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device); 
> +    if (rc) goto out; 
> + 
> +    aodev->dev = device; 
> +    aodev->action = LIBXL__DEVICE_ACTION_ADD; 
> +    libxl__wait_device_connection(egc, aodev); 
> +    return; 
> + 
> +out: 
> +    aodev->rc = rc; 
> +    aodev->callback(egc, aodev); 
> +    return; 
> +} 
> + 
> +static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t  
> domid, 
> +                                                 libxl_devid usbctrl, 
> +                                                 libxl_device_usbdev  
> **usbdevs, 
> +                                                 int *num); 
> + 
> +static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid, 
> +                                       libxl_device_usbdev *usbdev); 
> + 
> +/* AO function to remove a usb controller. 
> + * 
> + * Generally, it does: 
> + * 1) check if the usb controller exists or not 
> + * 2) remove all usb devices under controller 
> + * 3) remove usb controller information from xenstore 
> + * 
> + * Before calling this function, aodev should be properly filled: 
> + * aodev->ao, aodev->dev, aodev->callback, ... 
> + */ 
> +void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, 
> +                                           libxl__ao_device *aodev) 
> +{ 
> +    STATE_AO_GC(aodev->ao); 
> +    libxl_ctx *ctx = CTX; 
> +    libxl_device_usbdev *usbdevs = NULL; 
> +    int num_usbdev = 0; 
> +    int i, rc; 
> +    uint32_t domid = ao->domid; 
> +    int usbctrl_devid = aodev->dev->devid; 
> +    libxl_device_usbctrl usbctrl; 
> +    libxl_usbctrlinfo usbctrlinfo; 
> + 
> +    libxl_device_usbctrl_init(&usbctrl); 
> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
> +    usbctrl.devid = usbctrl_devid; 
> + 
> +    rc = libxl_device_usbctrl_getinfo(ctx, domid, &usbctrl, &usbctrlinfo); 
> +    if (rc) goto out; 
> + 
> +    if (usbctrlinfo.type != LIBXL_USBCTRL_TYPE_PV) { 
> +        LOG(ERROR, "Unsupported USB controller type"); 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    /* Remove usb devices first */ 
> +    rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid, 
> +                                               &usbdevs, &num_usbdev); 
> +    if (rc) goto out; 
> + 
> +    for (i = 0; i < num_usbdev; i++) { 
> +        if (libxl__device_usbdev_remove(gc, domid, &usbdevs[i])) { 
> +            LOG(ERROR, "libxl__device_usbdev_remove failed: controller %d,  
> " 
> +                "port %d", usbdevs[i].ctrl, usbdevs[i].port); 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> +    } 
> + 
> +    libxl_device_usbctrl_dispose(&usbctrl); 
> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
> + 
> +    /* Remove usbctrl */ 
> +    return libxl__initiate_device_remove(egc, aodev); 
> + 
> +out: 
> +    libxl_device_usbctrl_dispose(&usbctrl); 
> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
> +    aodev->rc = rc; 
> +    aodev->callback(egc, aodev); 
> +    return; 
> +} 
> + 
> +libxl_device_usbctrl * 
> +libxl_device_usbctrl_list(libxl_ctx *ctx, uint32_t domid, int *num) 
> +{ 
> +    GC_INIT(ctx); 
> +    libxl_device_usbctrl *usbctrls = NULL; 
> +    char *path = NULL; 
> +    char **entry = NULL; 
> +    unsigned int nentries = 0; 
> + 
> +    *num = 0; 
> + 
> +    path = GCSPRINTF("%s/device/vusb", 
> +                     libxl__xs_get_dompath(gc, domid)); 
> +    entry = libxl__xs_directory(gc, XBT_NULL, path, &nentries); 
> + 
> +    if (entry && nentries) { 
> +        usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * nentries); 
> +        libxl_device_usbctrl *usbctrl; 
> +        libxl_device_usbctrl *end = usbctrls + nentries; 
> +        for (usbctrl = usbctrls; 
> +             usbctrl < end; 
> +             usbctrl++, entry++, (*num)++) { 
> +            const char *tmp, *be_path, *fe_path; 
> +            int ret; 
> + 
> +            libxl_device_usbctrl_init(usbctrl); 
> +            usbctrl->devid = atoi(*entry); 
> + 
> +#define READ_SUBPATH(path, subpath) ({                                  \ 
> +        ret = libxl__xs_read_checked(gc, XBT_NULL,                      \ 
> +                                     GCSPRINTF("%s/" subpath, path),    \ 
> +                                     &tmp);                             \ 
> +        if (ret) goto out;                                              \ 
> +        (char *)tmp;                                                    \ 
> +    }) 
> + 
> +#define READ_SUBPATH_INT(path, subpath) ({                              \ 
> +        ret = libxl__xs_read_checked(gc, XBT_NULL,                      \ 
> +                                     GCSPRINTF("%s/" subpath, path),    \ 
> +                                     &tmp);                             \ 
> +        if (ret) goto out;                                              \ 
> +        tmp ? atoi(tmp) : -1;                                           \ 
> +    }) 
> + 
> +            fe_path = GCSPRINTF("%s/%s", path, *entry); 
> +            be_path = READ_SUBPATH(fe_path, "backend"); 
> +            usbctrl->backend_domid = READ_SUBPATH_INT(fe_path, "backend-id"); 
> +            usbctrl->version = READ_SUBPATH_INT(be_path, "usb-ver"); 
> +            usbctrl->ports = READ_SUBPATH_INT(be_path, "num-ports"); 
> +            libxl_usbctrl_type_from_string(READ_SUBPATH(be_path, "type"), 
> +                                           &usbctrl->type); 
> + 
> +#undef READ_SUBPATH 
> +#undef READ_SUBPATH_INT 
> +       } 
> +    } 
> + 
> +    GC_FREE; 
> +    return usbctrls; 
> + 
> +out: 
> +    LOG(ERROR, "Unable to list USB Controllers"); 
> +    libxl_device_usbctrl_list_free(usbctrls, *num); 
> +    GC_FREE; 
> +    *num = 0; 
> +    return NULL; 
> +} 
> + 
> +int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid, 
> +                                 libxl_device_usbctrl *usbctrl, 
> +                                 libxl_usbctrlinfo *usbctrlinfo) 
> +{ 
> +    GC_INIT(ctx); 
> +    const char *dompath, *fe_path, *be_path, *tmp; 
> +    int rc; 
> + 
> +    usbctrlinfo->devid = usbctrl->devid; 
> + 
> +#define READ_SUBPATH(path, subpath) ({                                  \ 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \ 
> +                                    GCSPRINTF("%s/" subpath, path),     \ 
> +                                    &tmp);                              \ 
> +        if (rc) goto out;                                               \ 
> +        (char *)tmp;                                                    \ 
> +    }) 
> + 
> +#define READ_SUBPATH_INT(path, subpath) ({                              \ 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \ 
> +                                    GCSPRINTF("%s/" subpath, path),     \ 
> +                                    &tmp);                              \ 
> +        if (rc) goto out;                                               \ 
> +        tmp ? atoi(tmp) : -1;                                           \ 
> +    }) 
> + 
> +    dompath = libxl__xs_get_dompath(gc, domid); 
> +    fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrl->devid); 
> +    be_path = READ_SUBPATH(fe_path, "backend"); 
> +    usbctrlinfo->backend = libxl__strdup(NOGC, be_path); 
> +    usbctrlinfo->backend_id = READ_SUBPATH_INT(fe_path, "backend-id"); 
> +    usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state"); 
> +    usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel"); 
> +    usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref"); 
> +    usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref"); 
> +    tmp = READ_SUBPATH(be_path, "frontend"); 
> +    usbctrlinfo->frontend = libxl__strdup(NOGC, tmp); 
> +    usbctrlinfo->frontend_id = READ_SUBPATH_INT(be_path, "frontend-id"); 
> +    usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports"); 
> +    usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver");; 
> +    tmp = READ_SUBPATH(be_path, "type"); 
> +    libxl_usbctrl_type_from_string(tmp, &usbctrlinfo->type); 
> + 
> +#undef READ_SUBPATH 
> +#undef READ_SUBPATH_INT 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    GC_FREE; 
> +    return rc; 
> +} 
> + 
> +int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, 
> +                                  uint32_t domid, 
> +                                  int devid, 
> +                                  libxl_device_usbctrl *usbctrl) 
> +{ 
> +    libxl_device_usbctrl *usbctrls; 
> +    int nb = 0; 
> +    int i, rc; 
> + 
> +    usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb); 
> +    if (!usbctrls) 
> +        return ERROR_FAIL; 
> + 
> +    rc = ERROR_FAIL; 
> +    for (i = 0; i < nb; i++) { 
> +        if (devid == usbctrls[i].devid) { 
> +            libxl_device_usbctrl_copy(ctx, usbctrl, &usbctrls[i]); 
> +            rc = 0; 
> +            break; 
> +        } 
> +    } 
> + 
> +    libxl_device_usbctrl_list_free(usbctrls, nb); 
> +    return rc; 
> +} 
> + 
> +static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr) 
> +{ 
> +    DIR *dir; 
> +    char *busid = NULL; 
> + 
> +    /* invalid hostbus or hostaddr */ 
> +    if (bus < 1 || addr < 1) 
> +        return NULL; 
> + 
> +    dir = opendir(SYSFS_USB_DEV); 
> +    if (!dir) { 
> +        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV); 
> +        return NULL; 
> +    } 
> + 
> +    size_t need = offsetof(struct dirent, d_name) + 
> +        pathconf(SYSFS_USB_DEV, _PC_NAME_MAX) + 1; 
> +    struct dirent *de_buf = libxl__zalloc(gc, need); 
> +    struct dirent *de; 
> + 
> +    for (;;) { 
> +        char *filename; 
> +        void *buf; 
> +        int busnum = -1; 
> +        int devnum = -1; 
> + 
> +        int r = readdir_r(dir, de_buf, &de); 
> +        if (r) { 
> +            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV); 
> +            break; 
> +        } 
> +        if (!de) 
> +            break; 
> + 
> +        if (!strcmp(de->d_name, ".") || 
> +            !strcmp(de->d_name, "..")) 
> +            continue; 
> + 
> +        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", de->d_name); 
> +        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
> +            devnum = atoi(buf); 
> + 
> +        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", de->d_name); 
> +        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
> +            busnum = atoi(buf); 
> + 
> +        if (bus == busnum && addr == devnum) { 
> +            busid = libxl__strdup(gc, de->d_name); 
> +            break; 
> +        } 
> +    } 
> + 
> +    closedir(dir); 
> +    return busid; 
> +} 
> + 
> +static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid, 
> +                                     uint8_t *bus, uint8_t *addr) 
> +{ 
> +    char *filename; 
> +    void *buf; 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid); 
> +    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
> +        *bus = atoi((char *)buf); 
> +    else 
> +        return ERROR_FAIL; 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid); 
> +    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
> +        *addr = atoi((char *)buf); 
> +    else 
> +        return ERROR_FAIL; 
> + 
> +    return 0; 
> +} 
> + 
> +static int get_assigned_devices(libxl__gc *gc, 
> +                                libxl_device_usbdev **list, int *num) 
> +{ 
> +    char **domlist; 
> +    unsigned int ndom = 0; 
> +    int i, j, k; 
> +    int rc; 
> + 
> +    *list = NULL; 
> +    *num = 0; 
> + 
> +    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom); 
> +    for (i = 0; i < ndom; i++) { 
> +        char *path; 
> +        char **usbctrls; 
> +        unsigned int nc = 0; 
> +        uint32_t domid = atoi(domlist[i]); 
> + 
> +        path = GCSPRINTF("%s/device/vusb", libxl__xs_get_dompath(gc,  
> domid)); 
> +        usbctrls = libxl__xs_directory(gc, XBT_NULL, path, &nc); 
> + 
> +        for (j = 0; j < nc; j++) { 
> +            libxl_device_usbdev *tmp = NULL; 
> +            int nd = 0; 
> + 
> +            rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, 
> +                                                       atoi(usbctrls[j]), 
> +                                                       &tmp, &nd); 
> +            if (rc) goto out; 
> + 
> +            if (!nd) continue; 
> + 
> +            GCREALLOC_ARRAY(*list, *num + nd); 
> +            for (k = 0; k < nd; k++) { 
> +                libxl_device_usbdev_copy(CTX, *list + *num, tmp + k); 
> +                (*num)++; 
> +            } 
> +        } 
> +    } 
> + 
> +    return 0; 
> + 
> +out: 
> +    LOG(ERROR, "fail to get assigned devices"); 
> +    return rc; 
> +} 
> + 
> +static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num, 
> +                               libxl_device_usbdev *usbdev) 
> +{ 
> +    int i; 
> + 
> +    for (i = 0; i < num; i++) { 
> +        if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus && 
> +            usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr) 
> +            return true; 
> +    } 
> + 
> +    return false; 
> +} 
> + 
> +/* check if USB device type is assignable */ 
> +static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev  
> *usbdev) 
> +{ 
> +    int classcode; 
> +    char *filename; 
> +    void *buf = NULL; 
> +    char *busid = NULL; 
> + 
> +    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, 
> +                                    usbdev->u.hostdev.hostaddr); 
> +    if (!busid) return false; 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", busid); 
> +    if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
> +        return false; 
> + 
> +    classcode = atoi(buf); 
> +    return classcode != USBHUB_CLASS_CODE; 
> +} 
> + 
> +/* get usb devices under certain usb controller */ 
> +static int 
> +libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, 
> +                                      uint32_t domid, 
> +                                      libxl_devid usbctrl, 
> +                                      libxl_device_usbdev **usbdevs, 
> +                                      int *num) 
> +{ 
> +    const char *fe_path, *be_path, *num_devs; 
> +    int n, i, rc; 
> + 
> +    *usbdevs = NULL; 
> +    *num = 0; 
> + 
> +    fe_path = GCSPRINTF("%s/device/vusb/%d", 
> +                        libxl__xs_get_dompath(gc, domid), usbctrl); 
> + 
> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                GCSPRINTF("%s/backend", fe_path), 
> +                                &be_path); 
> +    if (rc) goto out; 
> + 
> +    if (!be_path) { 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                GCSPRINTF("%s/num-ports", be_path), 
> +                                &num_devs); 
> +    if (rc) goto out; 
> + 
> +    n = num_devs ? atoi(num_devs) : 0; 
> + 
> +    for (i = 0; i < n; i++) { 
> +        const char *busid; 
> +        libxl_device_usbdev *usbdev; 
> + 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                    GCSPRINTF("%s/port/%d", be_path, i +  
> 1), 
> +                                    &busid); 
> +        if (rc) goto out; 
> + 
> +        if (busid && strcmp(busid, "")) { 
> +            GCREALLOC_ARRAY(*usbdevs, *num + 1); 
> +            usbdev = *usbdevs + *num; 
> +            (*num)++; 
> +            libxl_device_usbdev_init(usbdev); 
> +            usbdev->ctrl = usbctrl; 
> +            usbdev->port = i + 1; 
> +            usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; 
> +            rc = usbdev_busaddr_from_busid(gc, busid, 
> +                                           &usbdev->u.hostdev.hostbus, 
> +                                           &usbdev->u.hostdev.hostaddr); 
> +            if (rc) goto out; 
> +        } 
> +    } 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    return rc; 
> +} 
> + 
> +/* get all usb devices of the domain */ 
> +libxl_device_usbdev * 
> +libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num) 
> +{ 
> +    GC_INIT(ctx); 
> +    libxl_device_usbdev *usbdevs = NULL; 
> +    const char *path; 
> +    char **usbctrls; 
> +    unsigned int nc = 0; 
> +    int i, j; 
> + 
> +    *num = 0; 
> + 
> +    path = GCSPRINTF("%s/device/vusb", 
> +                        libxl__xs_get_dompath(gc, domid)); 
> +    usbctrls = libxl__xs_directory(gc, XBT_NULL, path, &nc); 
> + 
> +    for (i = 0; i < nc; i++) { 
> +        int r, nd = 0; 
> +        libxl_device_usbdev *tmp = NULL; 
> + 
> +        r = libxl__device_usbdev_list_for_usbctrl(gc, domid, 
> +                                                  atoi(usbctrls[i]), 
> +                                                  &tmp, &nd); 
> +        if (!r || !nd) continue; 
> + 
> +        usbdevs = libxl__realloc(NOGC, usbdevs, 
> +                                 sizeof(*usbdevs) * (*num + nd)); 
> +        for (j = 0; j < nd; j++) { 
> +            libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j); 
> +            (*num)++; 
> +        } 
> +    } 
> + 
> +    GC_FREE; 
> +    return usbdevs; 
> +} 
> + 
> +/* find first unused controller:port and give that to usb device */ 
> +static int 
> +libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid, 
> +                                         libxl_device_usbdev *usbdev) 
> +{ 
> +    libxl_device_usbctrl *usbctrls = NULL; 
> +    int numctrl = 0; 
> +    int i, j, rc; 
> + 
> +    usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl); 
> +    if (!numctrl || !usbctrls) { 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    rc = ERROR_FAIL; 
> +    for (i = 0; i < numctrl; i++) { 
> +        for (j = 0; j < usbctrls[i].ports; j++) { 
> +            const char *path, *tmp; 
> + 
> +            path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
> +                             libxl__xs_get_dompath(gc,  
> LIBXL_TOOLSTACK_DOMID), 
> +                             domid, usbctrls[i].devid, j + 1); 
> +            tmp = libxl__xs_read(gc, XBT_NULL, path); 
> +            if (tmp && !strcmp(tmp, "")) { 
> +                usbdev->ctrl = usbctrls[i].devid; 
> +                usbdev->port = j + 1; 
> +                rc = 0; 
> +                goto out; 
> +            } 
> +        } 
> +    } 
> + 
> +out: 
> +    libxl_device_usbctrl_list_free(usbctrls, numctrl); 
> +    return rc; 
> +} 
> + 
> +/* Fill in usb information with default value. 
> + * 
> + * Generally, it does: 
> + * 1) if "controller" is not specified: 
> + *    - if "port" is not specified, try to find an available  
> controller:port, 
> + *      if found, use that; otherwise, create a new controller, use this 
> + *      controller and its first port 
> + *    - if "port" is specified, report error. 
> + * 2) if "controller" is specified, but port is not specified: 
> + *    try to find an available port under this controller, if found, use 
> + *    that, otherwise, report error. 
> + * 3) if both "controller" and "port" are specified: 
> + *    check the controller:port is available, if not, report error. 
> + */ 
> +static int libxl__device_usbdev_setdefault(libxl__gc *gc, 
> +                                           uint32_t domid, 
> +                                           libxl_device_usbdev *usbdev, 
> +                                           bool update_json) 
> +{ 
> +    int rc; 
> + 
> +    if (!usbdev->type) 
> +        usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; 
> + 
> +    if (usbdev->ctrl == -1) { 
> +        if (usbdev->port) { 
> +            LOG(ERROR, "USB controller must be specified if you specify  
> port"); 
> +            return ERROR_INVAL; 
> +        } 
> + 
> +        rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev); 
> +        /* If no existing controller to host this usb device, add a new one  
> */ 
> +        if (rc) { 
> +            libxl_device_usbctrl *usbctrl; 
> + 
> +            GCNEW(usbctrl); 
> +            libxl_device_usbctrl_init(usbctrl); 
> +            rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl); 
> +            if (rc < 0) goto out; 
> + 
> +            if (usbctrl->devid == -1) { 
> +                usbctrl->devid = libxl__device_nextid(gc, domid, "vusb"); 
> +                if (usbctrl->devid < 0) { 
> +                    rc = ERROR_FAIL; 
> +                    goto out; 
> +                } 
> +            } 
> + 
> +            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, 
> +                                                    update_json); 
> +            if (rc) goto out; 
> + 
> +            usbdev->ctrl = usbctrl->devid; 
> +            usbdev->port = 1; 
> +        } 
> +    } else if (!usbdev->port) { 
> +        /* Valid port starts from 1. Choose port for us. */ 
> +        int i, ports; 
> +        const char *fe_path, *be_path, *tmp; 
> + 
> +        fe_path = GCSPRINTF("%s/device/vusb/%d", 
> +                         libxl__xs_get_dompath(gc, domid), usbdev->ctrl); 
> + 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                    GCSPRINTF("%s/backend", fe_path),  
> &be_path); 
> +        if (rc) goto out; 
> + 
> +        if (!be_path) { 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> + 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                    GCSPRINTF("%s/num-ports", be_path),  
> &tmp); 
> +        if (rc) goto out; 
> + 
> +        ports = tmp ? atoi(tmp) : 0; 
> + 
> +        for (i = 0; i < ports; i++) { 
> +            rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                        GCSPRINTF("%s/port/%d", be_path, i  
> + 1), 
> +                                        &tmp); 
> +            if (rc) goto out; 
> + 
> +            if (tmp && !strcmp(tmp, "")) { 
> +                usbdev->port = i + 1; 
> +                break; 
> +            } 
> +        } 
> + 
> +        if (!usbdev->port) { 
> +            LOG(ERROR, "No available port under specified controller"); 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> +    } else { 
> +        const char *be_path, *tmp; 
> + 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                    GCSPRINTF("%s/device/vusb/%d/backend", 
> +                                              libxl__xs_get_dompath(gc,  
> domid), 
> +                                              usbdev->ctrl), 
> +                                    &be_path); 
> +        if (rc) goto out; 
> + 
> +        if (!be_path) { 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> + 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                                    GCSPRINTF("%s/port/%d", 
> +                                              be_path, usbdev->port), 
> +                                    &tmp); 
> +        if (rc) goto out; 
> + 
> +        if (tmp && strcmp(tmp, "")) { 
> +            LOG(ERROR, "The controller port isn't available"); 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> +    } 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    return rc; 
> +} 
> + 
> +/* Add usb information to xenstore 
> + * 
> + * Adding a usb device won't create new 'vusb' device, but only write 
> + * the device busid to the controller:port in xenstore. 
> + */ 
> +static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid, 
> +                                             libxl_device_usbdev *usbdev, 
> +                                             bool update_json) 
> +{ 
> +    char *be_path, *busid; 
> +    int rc; 
> +    xs_transaction_t t = XBT_NULL; 
> +    libxl_domain_config d_config; 
> +    libxl_device_usbdev usbdev_saved; 
> +    libxl__domain_userdata_lock *lock = NULL; 
> + 
> +    libxl_domain_config_init(&d_config); 
> +    libxl_device_usbdev_init(&usbdev_saved); 
> +    libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev); 
> + 
> +    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, 
> +                                    usbdev->u.hostdev.hostaddr); 
> +    if (!busid) { 
> +        LOG(DEBUG, "Fail to get busid of usb device"); 
> +        goto out; 
> +    } 
> + 
> +    if (update_json) { 
> +        lock = libxl__lock_domain_userdata(gc, domid); 
> +        if (!lock) { 
> +            rc = ERROR_LOCK_FAIL; 
> +            goto out; 
> +        } 
> + 
> +        rc = libxl__get_domain_configuration(gc, domid, &d_config); 
> +        if (rc) goto out; 
> + 
> +        DEVICE_ADD(usbdev, usbdevs, domid, &usbdev_saved, 
> +                   COMPARE_USB, &d_config); 
> +    } 
> + 
> +    for (;;) { 
> +        rc = libxl__xs_transaction_start(gc, &t); 
> +        if (rc) goto out; 
> + 
> +        if (update_json) { 
> +            rc = libxl__set_domain_configuration(gc, domid, &d_config); 
> +            if (rc) goto out; 
> +        } 
> + 
> +        be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
> +                            libxl__xs_get_dompath(gc,  
> LIBXL_TOOLSTACK_DOMID), 
> +                            domid, usbdev->ctrl, usbdev->port); 
> + 
> +        LOG(DEBUG, "Adding usb device %s to xenstore: controller %d, port  
> %d", 
> +            busid, usbdev->ctrl, usbdev->port); 
> + 
> +        if (libxl__xs_write_checked(gc, t, be_path, busid)) 
> +            goto out; 
> + 
> +        rc = libxl__xs_transaction_commit(gc, &t); 
> +        if (!rc) break; 
> +        if (rc < 0) goto out; 
> +    } 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    if (lock) libxl__unlock_domain_userdata(lock); 
> +    libxl_device_usbdev_dispose(&usbdev_saved); 
> +    libxl_domain_config_dispose(&d_config); 
> +    return rc; 
> +} 
> + 
> +static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t  
> domid, 
> +                                                libxl_device_usbdev  
> *usbdev) 
> +{ 
> +    char *be_path; 
> + 
> +    be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
> +                        libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID), 
> +                        domid, usbdev->ctrl, usbdev->port); 
> + 
> +    LOG(DEBUG, "Removing usb device from xenstore: controller %d, port %d", 
> +        usbdev->ctrl, usbdev->port); 
> + 
> +    return libxl__xs_write_checked(gc, XBT_NULL, be_path, ""); 
> +} 
> + 
> +static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid, 
> +                                        libxl_device_usbdev *usbdev) 
> +{ 
> +    return libxl__xs_read(gc, XBT_NULL, 
> +                          GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
> +                              libxl__xs_get_dompath(gc,  
> LIBXL_TOOLSTACK_DOMID), 
> +                          domid, usbdev->ctrl, usbdev->port)); 
> +} 
> + 
> +/* get original driver path of usb interface, stored in @drvpath */ 
> +static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char  
> **drvpath) 
> +{ 
> +    char *spath, *dp = NULL; 
> +    struct stat st; 
> +    int rc; 
> + 
> +    spath = GCSPRINTF(SYSFS_USB_DEV"/%s/driver", intf); 
> + 
> +    rc = lstat(spath, &st); 
> +    if (rc == 0) { 
> +        /* Find the canonical path to the driver. */ 
> +        dp = libxl__zalloc(gc, PATH_MAX); 
> +        dp = realpath(spath, dp); 
> +    } else if (errno != ENOENT) { 
> +        LOGE(ERROR, "lstat failed: '%s'", spath); 
> +        return ERROR_FAIL; 
> +    } 
> + 
> +    *drvpath = dp; 
> + 
> +    return 0; 
> +} 
> + 
> +static int sysfs_write_intf(libxl__gc *gc, const char *intf, const char  
> *path) 
> +{ 
> +    int rc, fd = -1; 
> + 
> +    fd = open(path, O_WRONLY); 
> +    if (fd < 0) { 
> +        LOGE(ERROR, "open file failed: '%s'", path); 
> +        return ERROR_FAIL; 
> +    } 
> + 
> +    rc = write(fd, intf, strlen(intf)); 
> +    close(fd); 
> +    if (rc < 0) { 
> +        LOGE(ERROR, "write '%s' to '%s' failed", intf, path); 
> +        return errno; 
> +    } 
> +    if (rc != strlen(intf)) { 
> +        LOG(ERROR, "write '%s' to '%s' failed: incorrect write count", 
> +            intf, path); 
> +        return ERROR_FAIL; 
> +    } 
> + 
> +    return 0; 
> +} 
> + 
> +static int unbind_usbintf(libxl__gc *gc, const char *intf) 
> +{ 
> +    char *path; 
> + 
> +    path = GCSPRINTF(SYSFS_USB_DEV"/%s/driver/unbind", intf); 
> +    return sysfs_write_intf(gc, intf, path); 
> +} 
> + 
> +static int bind_usbintf(libxl__gc *gc, const char *intf, const char  
> *drvpath) 
> +{ 
> +    char *path; 
> +    struct stat st; 
> + 
> +    path = GCSPRINTF("%s/%s", drvpath, intf); 
> +    /* if already bound, return */ 
> +    if (!lstat(path, &st)) 
> +        return 0; 
> + 
> +    path = GCSPRINTF("%s/bind", drvpath); 
> +    return sysfs_write_intf(gc, intf, path); 
> +} 
> + 
> +/* Is usb interface bound to usbback? */ 
> +static int usbintf_is_assigned(libxl__gc *gc, char *intf) 
> +{ 
> +    char *spath; 
> +    int rc; 
> +    struct stat st; 
> + 
> +    spath = GCSPRINTF(SYSFS_USBBACK_DRIVER"/%s", intf); 
> +    rc = lstat(spath, &st); 
> + 
> +    if (rc == 0) 
> +        return 1; 
> +    if (rc < 0 && errno == ENOENT) 
> +        return 0; 
> +    LOGE(ERROR, "Accessing %s", spath); 
> +    return -1; 
> +} 
> + 
> +static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid, 
> +                                     char ***intfs, int *num) 
> +{ 
> +    DIR *dir; 
> +    char *buf; 
> +    int rc; 
> + 
> +    *intfs = NULL; 
> +    *num = 0; 
> + 
> +    buf = GCSPRINTF("%s:", busid); 
> + 
> +    dir = opendir(SYSFS_USB_DEV); 
> +    if (!dir) { 
> +        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV); 
> +        return ERROR_FAIL; 
> +    } 
> + 
> +    size_t need = offsetof(struct dirent, d_name) + 
> +        pathconf(SYSFS_USB_DEV, _PC_NAME_MAX) + 1; 
> +    struct dirent *de_buf = libxl__zalloc(gc, need); 
> +    struct dirent *de; 
> + 
> +    for (;;) { 
> +        int r = readdir_r(dir, de_buf, &de); 
> + 
> +        if (r) { 
> +            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV); 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> +        if (!de) 
> +            break; 
> + 
> +        if (!strcmp(de->d_name, ".") || 
> +            !strcmp(de->d_name, "..")) 
> +            continue; 
> + 
> +        if (!strncmp(de->d_name, buf, strlen(buf))) { 
> +            GCREALLOC_ARRAY(*intfs, *num + 1); 
> +            (*intfs)[*num] = libxl__strdup(gc, de->d_name); 
> +            (*num)++; 
> +        } 
> +    } 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    closedir(dir); 
> +    return rc; 
> +} 
> + 
> +/* Encode usb interface so that it could be written to xenstore as a key. 
> + * 
> + * Since xenstore key cannot include '.' or ':', we'll change '.' to '_', 
> + * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1. 
> + * This will be used to save original driver of USB device to xenstore. 
> + */ 
> +static char *usb_interface_xenstore_encode(libxl__gc *gc, const char  
> *busid) 
> +{ 
> +    char *str = libxl__strdup(gc, busid); 
> +    int i, len = strlen(str); 
> + 
> +    for (i = 0; i < len; i++) { 
> +        if (str[i] == '.') 
> +            str[i] = '_'; 
> +        if (str[i] == ':') 
> +            str[i] = '@'; 
> +    } 
> +    return str; 
> +} 
> + 
> +/* Unbind USB device from "usbback" driver. 
> + * 
> + * If there are many interfaces under USB device, check each interface, 
> + * unbind from "usbback" driver and rebind to its original driver. 
> + */ 
> +static int usbback_dev_unassign(libxl__gc *gc, const char *busid) 
> +{ 
> +    char **intfs = NULL; 
> +    char *usbdev_encode = NULL; 
> +    char *path = NULL; 
> +    int i, num = 0; 
> +    int rc; 
> + 
> +    if (usbdev_get_all_interfaces(gc, busid, &intfs, &num) < 0) 
> +        return ERROR_FAIL; 
> + 
> +    usbdev_encode = usb_interface_xenstore_encode(gc, busid); 
> + 
> +    for (i = 0; i < num; i++) { 
> +        char *intf = intfs[i]; 
> +        char *usbintf_encode = NULL; 
> +        const char *drvpath; 
> + 
> +        /* check if the USB interface is already bound to "usbback" */ 
> +        if (usbintf_is_assigned(gc, intf) > 0) { 
> +            /* unbind interface from usbback driver */ 
> +            if (unbind_usbintf(gc, intf) < 0) { 
> +                rc = ERROR_FAIL; 
> +                goto out; 
> +            } 
> +        } 
> + 
> +        /* try to rebind USB interface to its originial driver. 
> +         * If rebinding failed, export warning so that user can 
> +         * handle it later. 
> +         */ 
> +        usbintf_encode = usb_interface_xenstore_encode(gc, intf); 
> +        path = GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path", 
> +                         usbdev_encode, usbintf_encode); 
> +        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath); 
> +        if (rc) continue; 
> + 
> +        if (drvpath && bind_usbintf(gc, intf, drvpath)) 
> +            LOGE(WARN, "Couldn't rebind %s to %s", intf, drvpath); 
> +    } 
> + 
> +    /* finally, remove xenstore driver path */ 
> +    path = GCSPRINTF(USBBACK_INFO_PATH"/%s", usbdev_encode); 
> +    libxl__xs_rm_checked(gc, XBT_NULL, path); 
> +    rc = 0; 
> + 
> +out: 
> +    return rc; 
> +} 
> + 
> +/* Bind USB device to "usbback" driver. 
> + * 
> + * If there are many interfaces under USB device, check each interface, 
> + * unbind from original driver and bind to "usbback" driver. 
> + */ 
> +static int usbback_dev_assign(libxl__gc *gc, const char *busid) 
> +{ 
> +    char **intfs = NULL; 
> +    int num = 0, i; 
> +    int rc; 
> +    char *usbdev_encode = NULL; 
> + 
> +    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num); 
> +    if (rc) 
> +        return rc; 
> + 
> +    usbdev_encode = usb_interface_xenstore_encode(gc, busid); 
> + 
> +    for (i = 0; i < num; i++) { 
> +        char *intf = intfs[i]; 
> +        char *drvpath = NULL; 
> + 
> +        /* already assigned to usbback */ 
> +        if (usbintf_is_assigned(gc, intf) > 0) 
> +            continue; 
> + 
> +        rc = usbintf_get_drvpath(gc, intf, &drvpath); 
> +        if (rc < 0) goto out; 
> + 
> +        if (drvpath) { 
> +            /* write driver path to xenstore for later rebinding */ 
> +            char *usbintf_encode = NULL; 
> +            char *path; 
> + 
> +            usbintf_encode = usb_interface_xenstore_encode(gc, intf); 
> +            path = GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path", 
> +                             usbdev_encode, usbintf_encode); 
> +            if (libxl__xs_write_checked(gc, XBT_NULL, path, drvpath) < 0) 
> +                goto out; 
> + 
> +            /* unbind interface from original driver */ 
> +            if (unbind_usbintf(gc, intf) < 0) { 
> +                rc = ERROR_FAIL; 
> +                goto out; 
> +            } 
> +        } 
> + 
> +        /* bind interface to usbback */ 
> +        if (bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER) < 0) { 
> +            LOG(ERROR, "Couldn't bind %s to %s", intf,  
> SYSFS_USBBACK_DRIVER); 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> +    } 
> + 
> +    return 0; 
> + 
> +out: 
> +    /* some interfaces might be bound to usbback, unbind it and 
> +     * rebind it to its original driver 
> +     */ 
> +    usbback_dev_unassign(gc, busid); 
> +    return rc; 
> +} 
> + 
> +static int do_usbdev_add(libxl__gc *gc, uint32_t domid, 
> +                         libxl_device_usbdev *usbdev, 
> +                         bool update_json) 
> +{ 
> +    int rc; 
> +    char *busid; 
> +    libxl_device_usbctrl usbctrl; 
> +    libxl_usbctrlinfo usbctrlinfo; 
> + 
> +    libxl_device_usbctrl_init(&usbctrl); 
> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
> +    usbctrl.devid = usbdev->ctrl; 
> + 
> +    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); 
> +    if (rc) goto out; 
> + 
> +    switch (usbctrlinfo.type) { 
> +    case LIBXL_USBCTRL_TYPE_PV: 
> +        busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, 
> +                                        usbdev->u.hostdev.hostaddr); 
> +        if (!busid) { 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> + 
> +        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,  
> update_json); 
> +        if (rc) goto out; 
> + 
> +        rc = usbback_dev_assign(gc, busid); 
> +        if (rc) { 
> +            libxl__device_usbdev_remove_xenstore(gc, domid, usbdev); 
> +            goto out; 
> +        } 
> +        break; 
> +    case LIBXL_USBCTRL_TYPE_DEVICEMODEL: 
> +    default: 
> +        LOG(ERROR, "Unsupported usb controller type"); 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    libxl_device_usbctrl_dispose(&usbctrl); 
> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
> +    return rc; 
> +} 
> + 
> +/* AO operation to add a usb device. 
> + * 
> + * Generally, it does: 
> + * 1) check if the usb device type is assignable 
> + * 2) check if the usb device is already assigned to a domain 
> + * 3) add 'busid' of the usb device to xenstore contoller/port/. 
> + *    (PVUSB driver watches the xenstore changes and will detect that.) 
> + * 4) unbind usb device from original driver and bind to usbback. 
> + *    If usb device has many interfaces, then: 
> + *    - unbind each interface from its original driver and bind to usbback. 
> + *    - store the original driver to xenstore for later rebinding when 
> + *      detaching the device. 
> + * 
> + * Before calling this function, aodev should be properly filled: 
> + * aodev->ao, aodev->callback, aodev->update_json, ... 
> + */ 
> +void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid, 
> +                              libxl_device_usbdev *usbdev, 
> +                              libxl__ao_device *aodev) 
> +{ 
> +    STATE_AO_GC(aodev->ao); 
> +    int rc; 
> +    libxl_device_usbdev *assigned; 
> +    int num_assigned; 
> +    libxl_device_usbctrl usbctrl; 
> +    libxl_usbctrlinfo usbctrlinfo; 
> + 
> +    libxl_device_usbctrl_init(&usbctrl); 
> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
> + 
> +    /* Currently only support adding USB device from Dom0 backend. 
> +     * So, if USB controller is specified, check its backend domain, 
> +     * if it's not Dom0, report error. 
> +     */ 
> +    if (usbdev->ctrl != -1) { 
> +        usbctrl.devid = usbdev->ctrl; 
> +        rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl,  
> &usbctrlinfo); 
> +        if (rc) goto out; 
> + 
> +        if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) { 
> +            LOG(ERROR, "Don't support adding USB device from non-Dom0  
> backend"); 
> +            rc = ERROR_INVAL; 
> +            goto out; 
> +        } 
> +    } 
> + 
> +    /* check usb device is assignable type */ 
> +    if (!is_usbdev_assignable(gc, usbdev)) { 
> +        LOG(ERROR, "USB device is not assignable."); 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    /* check usb device is already assigned */ 
> +    rc = get_assigned_devices(gc, &assigned, &num_assigned); 
> +    if (rc) { 
> +        LOG(ERROR, "cannot determine if device is assigned," 
> +                   " refusing to continue"); 
> +        goto out; 
> +    } 
> + 
> +    if (is_usbdev_in_array(assigned, num_assigned, usbdev)) { 
> +        LOG(ERROR, "USB device already attached to a domain"); 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    /* fill default values, e.g, if usbdev->ctrl and usbdev->port 
> +     * not specified, choose available controller:port and fill in. */ 
> +    rc = libxl__device_usbdev_setdefault(gc, domid, usbdev, 
> +                                         aodev->update_json); 
> +    if (rc) goto out; 
> + 
> +    /* do actual adding usb device operation */ 
> +    rc = do_usbdev_add(gc, domid, usbdev, aodev->update_json); 
> + 
> +out: 
> +    libxl_device_usbctrl_dispose(&usbctrl); 
> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
> +    aodev->rc = rc; 
> +    aodev->callback(egc, aodev); 
> +    return; 
> +} 
> + 
> +static int do_usbdev_remove(libxl__gc *gc, uint32_t domid, 
> +                            libxl_device_usbdev *usbdev) 
> +{ 
> +    int rc; 
> +    char *busid; 
> +    libxl_device_usbctrl usbctrl; 
> +    libxl_usbctrlinfo usbctrlinfo; 
> + 
> +    libxl_device_usbctrl_init(&usbctrl); 
> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
> +    usbctrl.devid = usbdev->ctrl; 
> + 
> +    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); 
> +    if (rc) goto out; 
> + 
> +    switch (usbctrlinfo.type) { 
> +    case LIBXL_USBCTRL_TYPE_PV: 
> +        busid = usbdev_busid_from_ctrlport(gc, domid, usbdev); 
> +        if (!busid) { 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> + 
> +        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev); 
> +        if (rc) goto out; 
> + 
> +        rc = usbback_dev_unassign(gc, busid); 
> +        if (rc) { 
> +            /* Till here, usb device information is already removed 
> +             * from xenstore, usb list couldn't list it any more. 
> +             * If unassign usb device from usbback failed, export 
> +             * warning only so that user could handle driver status 
> +             * later. 
> +             */ 
> +            LOG(WARN, "unbind usb device from usbback and rebind to its " 
> +                      "original driver failed"); 
> +        } 
> +        break; 
> +    case LIBXL_USBCTRL_TYPE_DEVICEMODEL: 
> +    default: 
> +        LOG(ERROR, "Unsupported usb controller type"); 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    libxl_device_usbctrl_dispose(&usbctrl); 
> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
> +    return rc; 
> +} 
> + 
> +/* Operation to remove usb device. 
> + * 
> + * Generally, it does: 
> + * 1) check if the usb device is assigned to the domain 
> + * 2) remove the usb device from xenstore controller/port. 
> + * 3) unbind usb device from usbback and rebind to its original driver. 
> + *    If usb device has many interfaces, do it to each interface. 
> + */ 
> +static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid, 
> +                                       libxl_device_usbdev *usbdev) 
> +{ 
> +    libxl_usbctrlinfo usbctrlinfo; 
> +    libxl_device_usbctrl usbctrl; 
> +    int rc; 
> + 
> +    if (usbdev->ctrl < 0 || usbdev->port < 1) { 
> +        LOG(ERROR, "Invalid USB device"); 
> +        return ERROR_FAIL; 
> +    } 
> + 
> +    libxl_device_usbctrl_init(&usbctrl); 
> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
> +    usbctrl.devid = usbdev->ctrl; 
> + 
> +    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); 
> +    if (rc) goto out; 
> + 
> +    if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) { 
> +        LOG(ERROR, "Don't support removing USB device from non-Dom0  
> backend"); 
> +        rc = ERROR_INVAL; 
> +        goto out; 
> +    } 
> + 
> +    /* do actual removing usb device operation */ 
> +    rc = do_usbdev_remove(gc, domid, usbdev); 
> + 
> +out: 
> +    libxl_device_usbctrl_dispose(&usbctrl); 
> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
> +    return rc; 
> +} 
> + 
> +int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid, 
> +                               libxl_device_usbdev *usbdev, 
> +                               const libxl_asyncop_how *ao_how) 
> + 
> +{ 
> +    AO_CREATE(ctx, domid, ao_how); 
> +    int rc; 
> + 
> +    rc = libxl__device_usbdev_remove(gc, domid, usbdev); 
> + 
> +    libxl__ao_complete(egc, ao, rc); 
> +    return AO_INPROGRESS; 
> +} 
> + 
> +int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, 
> +                                    uint32_t domid, 
> +                                    int ctrl, 
> +                                    int port, 
> +                                    libxl_device_usbdev *usbdev) 
> +{ 
> +    GC_INIT(ctx); 
> +    const char *dompath, *be_path, *busid; 
> +    int rc; 
> + 
> +    dompath = libxl__xs_get_dompath(gc, domid); 
> + 
> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                  GCSPRINTF("%s/device/vusb/%d/backend", dompath, ctrl), 
> +                  &be_path); 
> +    if (rc) goto out; 
> + 
> +    if (!be_path) { 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
> +                           GCSPRINTF("%s/port/%d", be_path, port), 
> +                           &busid); 
> +    if (rc) goto out; 
> + 
> +    if (!busid || !strcmp(busid, "")) { 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    usbdev->ctrl = ctrl; 
> +    usbdev->port = port; 
> +    usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; 
> +    rc = usbdev_busaddr_from_busid(gc, busid, 
> +                                   &usbdev->u.hostdev.hostbus, 
> +                                   &usbdev->u.hostdev.hostaddr); 
> + 
> +out: 
> +    GC_FREE; 
> +    return rc; 
> +} 
> +/* 
> + * Local variables: 
> + * mode: C 
> + * c-basic-offset: 4 
> + * indent-tabs-mode: nil 
> + * End: 
> + */ 
> diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl 
> index 4d78f86..ed7da81 100644 
> --- a/tools/libxl/libxl_types.idl 
> +++ b/tools/libxl/libxl_types.idl 
> @@ -603,6 +603,35 @@ libxl_device_rdm = Struct("device_rdm", [ 
>      ("policy", libxl_rdm_reserve_policy), 
>      ]) 
>   
> +libxl_usbctrl_type = Enumeration("usbctrl_type", [ 
> +    (0, "AUTO"), 
> +    (1, "PV"), 
> +    (2, "DEVICEMODEL"), 
> +    ]) 
> + 
> +libxl_usbdev_type = Enumeration("usbdev_type", [ 
> +    (1, "hostdev"), 
> +    ]) 
> + 
> +libxl_device_usbctrl = Struct("device_usbctrl", [ 
> +    ("type", libxl_usbctrl_type), 
> +    ("devid", libxl_devid), 
> +    ("version", integer), 
> +    ("ports", integer), 
> +    ("backend_domid", libxl_domid), 
> +    ("backend_domname", string), 
> +   ]) 
> + 
> +libxl_device_usbdev = Struct("device_usbdev", [ 
> +    ("ctrl", libxl_devid), 
> +    ("port", integer), 
> +    ("u", KeyedUnion(None, libxl_usbdev_type, "type", 
> +           [("hostdev", Struct(None, [ 
> +                 ("hostbus",   uint8), 
> +                 ("hostaddr",  uint8)])), 
> +           ])), 
> +    ]) 
> + 
>  libxl_device_dtdev = Struct("device_dtdev", [ 
>      ("path", string), 
>      ]) 
> @@ -641,6 +670,8 @@ libxl_domain_config = Struct("domain_config", [ 
>      # a channel manifests as a console with a name, 
>      # see docs/misc/channels.txt 
>      ("channels", Array(libxl_device_channel, "num_channels")), 
> +    ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")), 
> +    ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")), 
>   
>      ("on_poweroff", libxl_action_on_shutdown), 
>      ("on_reboot", libxl_action_on_shutdown), 
> @@ -684,6 +715,21 @@ libxl_vtpminfo = Struct("vtpminfo", [ 
>      ("uuid", libxl_uuid), 
>      ], dir=DIR_OUT) 
>   
> +libxl_usbctrlinfo = Struct("usbctrlinfo", [ 
> +    ("type", libxl_usbctrl_type), 
> +    ("devid", libxl_devid), 
> +    ("version", integer), 
> +    ("ports", integer), 
> +    ("backend", string), 
> +    ("backend_id", uint32), 
> +    ("frontend", string), 
> +    ("frontend_id", uint32), 
> +    ("state", integer), 
> +    ("evtch", integer), 
> +    ("ref_urb", integer), 
> +    ("ref_conn", integer), 
> +    ], dir=DIR_OUT) 
> + 
>  libxl_vcpuinfo = Struct("vcpuinfo", [ 
>      ("vcpuid", uint32), 
>      ("cpu", uint32), 
> diff --git a/tools/libxl/libxl_types_internal.idl  
> b/tools/libxl/libxl_types_internal.idl 
> index 5e55685..696f5f8 100644 
> --- a/tools/libxl/libxl_types_internal.idl 
> +++ b/tools/libxl/libxl_types_internal.idl 
> @@ -22,6 +22,7 @@ libxl__device_kind = Enumeration("device_kind", [ 
>      (6, "VKBD"), 
>      (7, "CONSOLE"), 
>      (8, "VTPM"), 
> +    (9, "VUSB"), 
>      ]) 
>   
>  libxl__console_backend = Enumeration("console_backend", [ 
> diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c 
> index 7f612a6..f7ba880 100644 
> --- a/tools/libxl/libxl_utils.c 
> +++ b/tools/libxl/libxl_utils.c 
> @@ -1314,6 +1314,24 @@ int libxl__random_bytes(libxl__gc *gc, uint8_t *buf,  
> size_t len) 
>      return ret; 
>  } 
>   
> +void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr) 
> +{ 
> +   int i; 
> + 
> +   for (i = 0; i < nr; i++) 
> +       libxl_device_usbctrl_dispose(&list[i]); 
> +   free(list); 
> +} 
> + 
> +void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr) 
> +{ 
> +   int i; 
> + 
> +   for (i = 0; i < nr; i++) 
> +       libxl_device_usbdev_dispose(&list[i]); 
> +   free(list); 
> +} 
> + 
>  /* 
>   * Local variables: 
>   * mode: C 
> diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h 
> index 339ebdf..4495417 100644 
> --- a/tools/libxl/libxl_utils.h 
> +++ b/tools/libxl/libxl_utils.h 
> @@ -76,6 +76,11 @@ int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t  
> domid, 
>                                 libxl_uuid *uuid, libxl_device_vtpm *vtpm); 
>  int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid, 
>                                 int devid, libxl_device_vtpm *vtpm); 
> +int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid, 
> +                                  int devid, libxl_device_usbctrl  
> *usbctrl); 
> +int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid, 
> +                                    int ctrl, int port, 
> +                                    libxl_device_usbdev *usbdev); 
>   
>  int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits); 
>      /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RESEND][PATCH V9 3/7] libxl: add pvusb API
  2015-12-08  5:50   ` Chun Yan Liu
@ 2015-12-08 11:26     ` George Dunlap
  0 siblings, 0 replies; 12+ messages in thread
From: George Dunlap @ 2015-12-08 11:26 UTC (permalink / raw)
  To: Chun Yan Liu, xen-devel
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap,
	Ian.Jackson, Jim Fehlig, Simon Cao

On 08/12/15 05:50, Chun Yan Liu wrote:
> Any comments?

I'd just started looking at this yesterday. :-)

One comment for future reference: This series doesn't apply to staging,
nor to staging the date which you sent it (25 November); I had to apply
it to a commit before 5 November before it would apply cleanly.  In the
future please be sure when you send a series to rebase it to staging.

(No need to rebase it now until you have some comments to address.)

 -George

> 
>>>> On 11/25/2015 at 05:46 PM, in message
> <1448444775-6974-4-git-send-email-cyliu@suse.com>, Chunyan Liu <cyliu@suse.com>
> wrote: 
>> Add pvusb APIs, including: 
>>  - attach/detach (create/destroy) virtual usb controller. 
>>  - attach/detach usb device 
>>  - list usb controller and usb devices 
>>  - some other helper functions 
>>  
>> Signed-off-by: Chunyan Liu <cyliu@suse.com> 
>> Signed-off-by: Simon Cao <caobosimon@gmail.com> 
>>  
>> --- 
>> changes: 
>>   - update naming, all places indicating usb controller named 
>>     as usbctrl, all places indicating usb device named as usbdev 
>>   - update DEFINE_DEVICE_REMOVE instead of creating a new 
>>     DEFINE_DEVICE_REMOVE_EXT 
>>   - use libxl__xs_read_checked instead of libxl__xs_read 
>>   - update local READ_SUBPATH(_INT) macros to include more common codes 
>>   - save drvpath before unbind 
>>   - get_assigned_devices: call libxl__device_usbdev_list_for_ctrl 
>>     instead of doing all things from scratch 
>>   - usb_interface_xenstore_encode: use special char to avoid confusion 
>>   - usb readdir_r instead of readdir 
>>   - check syscall errno 
>>   - remove usbinfo definition 
>>   - address other comments except: 
>>     libxl__device_usbdev_add/remove and do_usbdev_add/remove, in previous 
>>     discussion, we'd like to get usbctrlinfo once and pass usbctrlinfo to 
>>     do_usbdev_add/remove. However, during update, adding usbdev process 
>>     still needs to try twice to get usbctrlinfo. (Before set_default, 
>>     if usbctrl doesn't exist it doesn't doing getting usbctrlinfo actually; 
>>     after set_default, needs to get usbctrlinfo then). So, finally, just 
>>     change codes to make adding/removing process symmetrical. 
>>  
>>  tools/libxl/Makefile                 |    2 +- 
>>  tools/libxl/libxl.c                  |   50 +- 
>>  tools/libxl/libxl.h                  |   77 ++ 
>>  tools/libxl/libxl_device.c           |    5 +- 
>>  tools/libxl/libxl_internal.h         |   18 + 
>>  tools/libxl/libxl_osdeps.h           |   13 + 
>>  tools/libxl/libxl_pvusb.c            | 1534  
>> ++++++++++++++++++++++++++++++++++ 
>>  tools/libxl/libxl_types.idl          |   46 + 
>>  tools/libxl/libxl_types_internal.idl |    1 + 
>>  tools/libxl/libxl_utils.c            |   18 + 
>>  tools/libxl/libxl_utils.h            |    5 + 
>>  11 files changed, 1766 insertions(+), 3 deletions(-) 
>>  create mode 100644 tools/libxl/libxl_pvusb.c 
>>  
>> diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile 
>> index 6ff5bee..a36145a 100644 
>> --- a/tools/libxl/Makefile 
>> +++ b/tools/libxl/Makefile 
>> @@ -103,7 +103,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o  
>> libxl_dm.o libxl_pci.o \ 
>>  			libxl_stream_read.o libxl_stream_write.o \ 
>>  			libxl_save_callout.o _libxl_save_msgs_callout.o \ 
>>  			libxl_qmp.o libxl_event.o libxl_fork.o \ 
>> -			libxl_dom_suspend.o $(LIBXL_OBJS-y) 
>> +			libxl_dom_suspend.o libxl_pvusb.o $(LIBXL_OBJS-y) 
>>  LIBXL_OBJS += libxl_genid.o 
>>  LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o 
>>   
>> diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c 
>> index eaa7d75..a479465 100644 
>> --- a/tools/libxl/libxl.c 
>> +++ b/tools/libxl/libxl.c 
>> @@ -4144,6 +4144,36 @@ out: 
>>      return rc; 
>>  } 
>>   
>> +static void libxl__initiate_device_disk_remove(libxl__egc *egc, 
>> +                                               libxl__ao_device *aodev) 
>> +{ 
>> +    return libxl__initiate_device_remove(egc, aodev); 
>> +} 
>> + 
>> +static void libxl__initiate_device_nic_remove(libxl__egc *egc, 
>> +                                              libxl__ao_device *aodev) 
>> +{ 
>> +    return libxl__initiate_device_remove(egc, aodev); 
>> +} 
>> + 
>> +static void libxl__initiate_device_vtpm_remove(libxl__egc *egc, 
>> +                                               libxl__ao_device *aodev) 
>> +{ 
>> +    return libxl__initiate_device_remove(egc, aodev); 
>> +} 
>> + 
>> +static void libxl__initiate_device_vkb_remove(libxl__egc *egc, 
>> +                                              libxl__ao_device *aodev) 
>> +{ 
>> +    return libxl__initiate_device_remove(egc, aodev); 
>> +} 
>> + 
>> +static void libxl__initiate_device_vfb_remove(libxl__egc *egc, 
>> +                                              libxl__ao_device *aodev) 
>> +{ 
>> +    return libxl__initiate_device_remove(egc, aodev); 
>> +} 
>> + 
>>   
>> /**************************************************************************** 
>> **/ 
>>   
>>  /* Macro for defining device remove/destroy functions in a compact way */ 
>> @@ -4158,6 +4188,8 @@ out: 
>>   * libxl_device_vkb_destroy 
>>   * libxl_device_vfb_remove 
>>   * libxl_device_vfb_destroy 
>> + * libxl_device_usbctrl_remove 
>> + * libxl_device_usbctrl_destroy 
>>   */ 
>>  #define DEFINE_DEVICE_REMOVE(type, removedestroy, f)                    \ 
>>      int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \ 
>> @@ -4179,7 +4211,7 @@ out: 
>>          aodev->dev = device;                                            \ 
>>          aodev->callback = device_addrm_aocomplete;                      \ 
>>          aodev->force = f;                                               \ 
>> -        libxl__initiate_device_remove(egc, aodev);                      \ 
>> +        libxl__initiate_device_##type##_remove(egc, aodev);             \ 
>>                                                                          \ 
>>      out:                                                                \ 
>>          if (rc) return AO_CREATE_FAIL(rc);                                   
>>   \ 
>> @@ -4209,6 +4241,10 @@ DEFINE_DEVICE_REMOVE(vfb, destroy, 1) 
>>  DEFINE_DEVICE_REMOVE(vtpm, remove, 0) 
>>  DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) 
>>   
>> +/* usbctrl */ 
>> +DEFINE_DEVICE_REMOVE(usbctrl, remove, 0) 
>> +DEFINE_DEVICE_REMOVE(usbctrl, destroy, 1) 
>> + 
>>  /* channel/console hotunplug is not implemented. There are 2 possibilities: 
>>   * 1. add support for secondary consoles to xenconsoled 
>>   * 2. dynamically add/remove qemu chardevs via qmp messages. */ 
>> @@ -4222,6 +4258,8 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) 
>>   * libxl_device_disk_add 
>>   * libxl_device_nic_add 
>>   * libxl_device_vtpm_add 
>> + * libxl_device_usbctrl_add 
>> + * libxl_device_usbdev_add 
>>   */ 
>>   
>>  #define DEFINE_DEVICE_ADD(type)                                         \ 
>> @@ -4253,6 +4291,12 @@ DEFINE_DEVICE_ADD(nic) 
>>  /* vtpm */ 
>>  DEFINE_DEVICE_ADD(vtpm) 
>>   
>> +/* usbctrl */ 
>> +DEFINE_DEVICE_ADD(usbctrl) 
>> + 
>> +/* usb */ 
>> +DEFINE_DEVICE_ADD(usbdev) 
>> + 
>>  #undef DEFINE_DEVICE_ADD 
>>   
>>   
>> /**************************************************************************** 
>> **/ 
>> @@ -6794,6 +6838,10 @@ int libxl_retrieve_domain_configuration(libxl_ctx  
>> *ctx, uint32_t domid, 
>>   
>>      MERGE(pci, pcidevs, COMPARE_PCI, {}); 
>>   
>> +    MERGE(usbctrl, usbctrls, COMPARE_USBCTRL, {}); 
>> + 
>> +    MERGE(usbdev, usbdevs, COMPARE_USB, {}); 
>> + 
>>      /* Take care of removable device. We maintain invariant in the 
>>       * insert / remove operation so that: 
>>       * 1. if xenstore is "empty" while JSON is not, the result 
>> diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h 
>> index 168fedd..609d068 100644 
>> --- a/tools/libxl/libxl.h 
>> +++ b/tools/libxl/libxl.h 
>> @@ -123,6 +123,12 @@ 
>>  #define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1 
>>   
>>  /* 
>> + * LIBXL_HAVE_PVUSB indicates functions for plugging in USB devices 
>> + * through pvusb -- both hotplug and at domain creation time.. 
>> + */ 
>> +#define LIBXL_HAVE_PVUSB 1 
>> + 
>> +/* 
>>   * LIBXL_HAVE_BUILDINFO_HVM_VENDOR_DEVICE indicates that the 
>>   * libxl_vendor_device field is present in the hvm sections of 
>>   * libxl_domain_build_info. This field tells libxl which 
>> @@ -1418,6 +1424,77 @@ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid,  
>> libxl_device_disk *disk, 
>>                         const libxl_asyncop_how *ao_how) 
>>                         LIBXL_EXTERNAL_CALLERS_ONLY; 
>>   
>> +/* 
>> + * USB 
>> + * 
>> + * For each device removed or added, one of these protocols is available: 
>> + * - PV (i.e., PVUSB) 
>> + * - DEVICEMODEL (i.e, qemu) 
>> + * 
>> + * PV is available for either PV or HVM domains.  DEVICEMODEL is only 
>> + * available for HVM domains.  The caller can additionally specify 
>> + * "AUTO", in which case the library will try to determine the best 
>> + * protocol automatically. 
>> + * 
>> + * At the moment, the only protocol implemented is PV. 
>> + * 
>> + * One can add/remove USB controllers to/from guest, and attach/detach USB 
>> + * devices to/from USB controllers. 
>> + * 
>> + * To add USB controllers and USB devices, one can adding USB controllers 
>> + * first and then attaching USB devices to some USB controller, or adding 
>> + * USB devices to guest directly, it will automatically create a USB 
>> + * controller for USB devices to attach. 
>> + * 
>> + * To remove USB controllers or USB devices, one can remove USB devices 
>> + * under USB controller one by one and then remove USB controller, or 
>> + * remove USB controller directly, it will remove all USB devices under 
>> + * it automatically. 
>> + * 
>> + */ 
>> +/* USB Controllers*/ 
>> +int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid, 
>> +                             libxl_device_usbctrl *usbctrl, 
>> +                             const libxl_asyncop_how *ao_how) 
>> +                             LIBXL_EXTERNAL_CALLERS_ONLY; 
>> + 
>> +int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid, 
>> +                                libxl_device_usbctrl *usbctrl, 
>> +                                const libxl_asyncop_how *ao_how) 
>> +                                LIBXL_EXTERNAL_CALLERS_ONLY; 
>> + 
>> +int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid, 
>> +                                 libxl_device_usbctrl *usbctrl, 
>> +                                 const libxl_asyncop_how *ao_how) 
>> +                                 LIBXL_EXTERNAL_CALLERS_ONLY; 
>> + 
>> +libxl_device_usbctrl *libxl_device_usbctrl_list(libxl_ctx *ctx, 
>> +                                                uint32_t domid, int *num); 
>> + 
>> +void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr); 
>> + 
>> + 
>> +int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid, 
>> +                                 libxl_device_usbctrl *usbctrl, 
>> +                                 libxl_usbctrlinfo *usbctrlinfo); 
>> + 
>> +/* USB Devices */ 
>> + 
>> +int libxl_device_usbdev_add(libxl_ctx *ctx, uint32_t domid, 
>> +                            libxl_device_usbdev *usbdev, 
>> +                            const libxl_asyncop_how *ao_how) 
>> +                            LIBXL_EXTERNAL_CALLERS_ONLY; 
>> + 
>> +int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid, 
>> +                               libxl_device_usbdev *usbdev, 
>> +                               const libxl_asyncop_how *ao_how) 
>> +                               LIBXL_EXTERNAL_CALLERS_ONLY; 
>> + 
>> +libxl_device_usbdev * 
>> +libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num); 
>> + 
>> +void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr); 
>> + 
>>  /* Network Interfaces */ 
>>  int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic  
>> *nic, 
>>                           const libxl_asyncop_how *ao_how) 
>> diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c 
>> index 8bb5e93..6715c16 100644 
>> --- a/tools/libxl/libxl_device.c 
>> +++ b/tools/libxl/libxl_device.c 
>> @@ -676,7 +676,10 @@ void libxl__devices_destroy(libxl__egc *egc,  
>> libxl__devices_remove_state *drs) 
>>                  aodev->action = LIBXL__DEVICE_ACTION_REMOVE; 
>>                  aodev->dev = dev; 
>>                  aodev->force = drs->force; 
>> -                libxl__initiate_device_remove(egc, aodev); 
>> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) 
>> +                    libxl__initiate_device_usbctrl_remove(egc, aodev); 
>> +                else 
>> +                    libxl__initiate_device_remove(egc, aodev); 
>>              } 
>>          } 
>>      } 
>> diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h 
>> index 7aff237..5b70c6e 100644 
>> --- a/tools/libxl/libxl_internal.h 
>> +++ b/tools/libxl/libxl_internal.h 
>> @@ -2569,6 +2569,14 @@ _hidden void libxl__device_vtpm_add(libxl__egc *egc,  
>> uint32_t domid, 
>>                                     libxl_device_vtpm *vtpm, 
>>                                     libxl__ao_device *aodev); 
>>   
>> +_hidden void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid, 
>> +                                       libxl_device_usbctrl *usbctrl, 
>> +                                       libxl__ao_device *aodev); 
>> + 
>> +_hidden void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid, 
>> +                                      libxl_device_usbdev *usbdev, 
>> +                                      libxl__ao_device *aodev); 
>> + 
>>  /* Internal function to connect a vkb device */ 
>>  _hidden int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid, 
>>                                    libxl_device_vkb *vkb); 
>> @@ -2601,6 +2609,13 @@ _hidden void  
>> libxl__wait_device_connection(libxl__egc*, 
>>  _hidden void libxl__initiate_device_remove(libxl__egc *egc, 
>>                                             libxl__ao_device *aodev); 
>>   
>> +_hidden int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid, 
>> +                               libxl_device_usbctrl *usbctrl, 
>> +                               libxl__device *device); 
>> + 
>> +_hidden void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, 
>> +                                                   libxl__ao_device  
>> *aodev); 
>> + 
>>  /* 
>>   * libxl__get_hotplug_script_info returns the args and env that should 
>>   * be passed to the hotplug script for the requested device. 
>> @@ -3958,6 +3973,9 @@ static inline void libxl__update_config_vtpm(libxl__gc  
>> *gc, 
>>  #define COMPARE_PCI(a, b) ((a)->func == (b)->func &&    \ 
>>                             (a)->bus == (b)->bus &&      \ 
>>                             (a)->dev == (b)->dev) 
>> +#define COMPARE_USB(a, b) ((a)->ctrl == (b)->ctrl && \ 
>> +                           (a)->port == (b)->port) 
>> +#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid) 
>>   
>>  /* DEVICE_ADD 
>>   * 
>> diff --git a/tools/libxl/libxl_osdeps.h b/tools/libxl/libxl_osdeps.h 
>> index d9661c9..802c762 100644 
>> --- a/tools/libxl/libxl_osdeps.h 
>> +++ b/tools/libxl/libxl_osdeps.h 
>> @@ -24,6 +24,8 @@ 
>>  #define _GNU_SOURCE 
>>   
>>  #if defined(__NetBSD__) 
>> +#define SYSFS_USB_DEV          "/sys/bus/usb/devices" 
>> +#define SYSFS_USBBACK_DRIVER   "/kern/xen/usb" 
>>  #define SYSFS_PCI_DEV          "/sys/bus/pci/devices" 
>>  #define SYSFS_PCIBACK_DRIVER   "/kern/xen/pci" 
>>  #define NETBACK_NIC_NAME       "xvif%ui%d" 
>> @@ -31,6 +33,8 @@ 
>>  #elif defined(__OpenBSD__) 
>>  #include <util.h> 
>>  #elif defined(__linux__) 
>> +#define SYSFS_USB_DEV          "/sys/bus/usb/devices" 
>> +#define SYSFS_USBBACK_DRIVER   "/sys/bus/usb/drivers/usbback" 
>>  #define SYSFS_PCI_DEV          "/sys/bus/pci/devices" 
>>  #define SYSFS_PCIBACK_DRIVER   "/sys/bus/pci/drivers/pciback" 
>>  #define NETBACK_NIC_NAME       "vif%u.%d" 
>> @@ -38,6 +42,8 @@ 
>>  #elif defined(__sun__) 
>>  #include <stropts.h> 
>>  #elif defined(__FreeBSD__) 
>> +#define SYSFS_USB_DEV          "/dev/null" 
>> +#define SYSFS_USBBACK_DRIVER   "/dev/null" 
>>  #define SYSFS_PCI_DEV          "/dev/null" 
>>  #define SYSFS_PCIBACK_DRIVER   "/dev/null" 
>>  #define NETBACK_NIC_NAME       "xnb%u.%d" 
>> @@ -45,6 +51,13 @@ 
>>  #include <sys/endian.h> 
>>  #endif 
>>   
>> +#ifndef SYSFS_USBBACK_DRIVER 
>> +#error define SYSFS_USBBACK_DRIVER for your platform 
>> +#endif 
>> +#ifndef SYSFS_USB_DEV 
>> +#error define SYSFS_USB_DEV for your platform 
>> +#endif 
>> + 
>>  #ifndef SYSFS_PCIBACK_DRIVER 
>>  #error define SYSFS_PCIBACK_DRIVER for your platform 
>>  #endif 
>> diff --git a/tools/libxl/libxl_pvusb.c b/tools/libxl/libxl_pvusb.c 
>> new file mode 100644 
>> index 0000000..e35c6b5 
>> --- /dev/null 
>> +++ b/tools/libxl/libxl_pvusb.c 
>> @@ -0,0 +1,1534 @@ 
>> +/* 
>> + * Copyright (C) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany. 
>> + * Author Chunyan Liu <cyliu@suse.com> 
>> + * 
>> + * This program is free software; you can redistribute it and/or modify 
>> + * it under the terms of the GNU Lesser General Public License as published 
>> + * by the Free Software Foundation; version 2.1 only. with the special 
>> + * exception on linking described in file LICENSE. 
>> + * 
>> + * This program is distributed in the hope that it will be useful, 
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of 
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
>> + * GNU Lesser General Public License for more details. 
>> + */ 
>> + 
>> +#include "libxl_osdeps.h" /* must come before any other headers */ 
>> + 
>> +#include "libxl_internal.h" 
>> +#include <inttypes.h> 
>> + 
>> +#define USBBACK_INFO_PATH "/libxl/usbback" 
>> + 
>> +#define USBHUB_CLASS_CODE 9 
>> + 
>> +static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid, 
>> +                                            libxl_device_usbctrl *usbctrl) 
>> +{ 
>> +    int rc; 
>> +    libxl_domain_type domtype = libxl__domain_type(gc, domid); 
>> + 
>> +    if (!usbctrl->version) 
>> +        usbctrl->version = 2; 
>> + 
>> +    if (!usbctrl->ports) 
>> +        usbctrl->ports = 8; 
>> + 
>> +    if (usbctrl->type == LIBXL_USBCTRL_TYPE_AUTO) { 
>> +        if (domtype == LIBXL_DOMAIN_TYPE_PV) { 
>> +            usbctrl->type = LIBXL_USBCTRL_TYPE_PV; 
>> +        } else if (domtype == LIBXL_DOMAIN_TYPE_HVM) { 
>> +            /* FIXME: See if we can detect PV frontend */ 
>> +            usbctrl->type = LIBXL_USBCTRL_TYPE_DEVICEMODEL; 
>> +        } 
>> +    } 
>> + 
>> +    rc = libxl__resolve_domid(gc, usbctrl->backend_domname, 
>> +                              &usbctrl->backend_domid); 
>> +    return rc; 
>> +} 
>> + 
>> +int libxl__device_from_usbctrl(libxl__gc *gc, uint32_t domid, 
>> +                               libxl_device_usbctrl *usbctrl, 
>> +                               libxl__device *device) 
>> +{ 
>> +    device->backend_devid   = usbctrl->devid; 
>> +    device->backend_domid   = usbctrl->backend_domid; 
>> +    device->backend_kind    = LIBXL__DEVICE_KIND_VUSB; 
>> +    device->devid           = usbctrl->devid; 
>> +    device->domid           = domid; 
>> +    device->kind            = LIBXL__DEVICE_KIND_VUSB; 
>> + 
>> +    return 0; 
>> +} 
>> + 
>> +/* Add usbctrl information to xenstore. 
>> + * 
>> + * Adding a usb controller will add a new 'vusb' device in xenstore, and 
>> + * add corresponding frontend, backend information to it. According to 
>> + * "update_json", decide wether to update json config file. 
>> + */ 
>> +static int libxl__device_usbctrl_add_xenstore(libxl__gc *gc, uint32_t  
>> domid, 
>> +                                              libxl_device_usbctrl  
>> *usbctrl, 
>> +                                              bool update_json) 
>> +{ 
>> +    libxl__device *device; 
>> +    flexarray_t *front; 
>> +    flexarray_t *back; 
>> +    xs_transaction_t t = XBT_NULL; 
>> +    int i, rc; 
>> +    libxl_domain_config d_config; 
>> +    libxl_device_usbctrl usbctrl_saved; 
>> +    libxl__domain_userdata_lock *lock = NULL; 
>> + 
>> +    libxl_domain_config_init(&d_config); 
>> +    libxl_device_usbctrl_init(&usbctrl_saved); 
>> +    libxl_device_usbctrl_copy(CTX, &usbctrl_saved, usbctrl); 
>> + 
>> +    GCNEW(device); 
>> +    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device); 
>> +    if (rc) goto out; 
>> + 
>> +    front = flexarray_make(gc, 4, 1); 
>> +    back = flexarray_make(gc, 12, 1); 
>> + 
>> +    flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", domid)); 
>> +    flexarray_append_pair(back, "online", "1"); 
>> +    flexarray_append_pair(back, "state", 
>> +                          GCSPRINTF("%d", XenbusStateInitialising)); 
>> +    flexarray_append_pair(back, "type", 
>> +                          (char  
>> *)libxl_usbctrl_type_to_string(usbctrl->type)); 
>> +    flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version)); 
>> +    flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports)); 
>> +    flexarray_append_pair(back, "port", ""); 
>> +    for (i = 0; i < usbctrl->ports; i++) 
>> +        flexarray_append_pair(back, GCSPRINTF("port/%d", i + 1), ""); 
>> + 
>> +    flexarray_append_pair(front, "backend-id", 
>> +                          GCSPRINTF("%d", usbctrl->backend_domid)); 
>> +    flexarray_append_pair(front, "state", 
>> +                          GCSPRINTF("%d", XenbusStateInitialising)); 
>> + 
>> +    if (update_json) { 
>> +        lock = libxl__lock_domain_userdata(gc, domid); 
>> +        if (!lock) { 
>> +            rc = ERROR_LOCK_FAIL; 
>> +            goto out; 
>> +        } 
>> + 
>> +        rc = libxl__get_domain_configuration(gc, domid, &d_config); 
>> +        if (rc) goto out; 
>> + 
>> +        DEVICE_ADD(usbctrl, usbctrls, domid, &usbctrl_saved, 
>> +                   COMPARE_USBCTRL, &d_config); 
>> +    } 
>> + 
>> +    for (;;) { 
>> +        rc = libxl__xs_transaction_start(gc, &t); 
>> +        if (rc) goto out; 
>> + 
>> +        rc = libxl__device_exists(gc, t, device); 
>> +        if (rc < 0) goto out; 
>> +        if (rc == 1) { 
>> +            /* already exists in xenstore */ 
>> +            LOG(ERROR, "device already exists in xenstore"); 
>> +            rc = ERROR_DEVICE_EXISTS; 
>> +            goto out; 
>> +        } 
>> + 
>> +        if (update_json) { 
>> +            rc = libxl__set_domain_configuration(gc, domid, &d_config); 
>> +            if (rc) goto out; 
>> +        } 
>> + 
>> +        libxl__device_generic_add(gc, t, device, 
>> +                          libxl__xs_kvs_of_flexarray(gc, back, back->count), 
>> +                          libxl__xs_kvs_of_flexarray(gc, front,  
>> front->count), 
>> +                          NULL); 
>> + 
>> +        rc = libxl__xs_transaction_commit(gc, &t); 
>> +        if (!rc) break; 
>> +        if (rc < 0) goto out; 
>> +    } 
>> + 
>> +out: 
>> +    libxl__xs_transaction_abort(gc, &t); 
>> +    if (lock) libxl__unlock_domain_userdata(lock); 
>> +    libxl_device_usbctrl_dispose(&usbctrl_saved); 
>> +    libxl_domain_config_dispose(&d_config); 
>> +    return rc; 
>> +} 
>> + 
>> +/* AO operation to add a usb controller. 
>> + * 
>> + * Generally, it does: 
>> + * 1) fill in necessary usb controler information with default value 
>> + * 2) write usb controller frontend/backend info to xenstore, update json 
>> + *    config file if necessary. 
>> + * 3) wait for device connection. PVUSB frontend and backend driver will 
>> + *    probe xenstore paths and build connection between frontend and  
>> backend. 
>> + * 
>> + * Before calling this function, aodev should be properly filled: 
>> + * aodev->ao, aodev->callback, aodev->update_json, ... 
>> + */ 
>> +void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid, 
>> +                               libxl_device_usbctrl *usbctrl, 
>> +                               libxl__ao_device *aodev) 
>> +{ 
>> +    STATE_AO_GC(aodev->ao); 
>> +    libxl__device *device; 
>> +    int rc; 
>> + 
>> +    rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl); 
>> +    if (rc < 0) goto out; 
>> + 
>> +    if (usbctrl->devid == -1) { 
>> +        usbctrl->devid = libxl__device_nextid(gc, domid, "vusb"); 
>> +        if (usbctrl->devid < 0) { 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> +    } 
>> + 
>> +    if (usbctrl->type != LIBXL_USBCTRL_TYPE_PV) { 
>> +        LOG(ERROR, "Unsupported USB controller type"); 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, 
>> +                                            aodev->update_json); 
>> +    if (rc) goto out; 
>> + 
>> +    GCNEW(device); 
>> +    rc = libxl__device_from_usbctrl(gc, domid, usbctrl, device); 
>> +    if (rc) goto out; 
>> + 
>> +    aodev->dev = device; 
>> +    aodev->action = LIBXL__DEVICE_ACTION_ADD; 
>> +    libxl__wait_device_connection(egc, aodev); 
>> +    return; 
>> + 
>> +out: 
>> +    aodev->rc = rc; 
>> +    aodev->callback(egc, aodev); 
>> +    return; 
>> +} 
>> + 
>> +static int libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, uint32_t  
>> domid, 
>> +                                                 libxl_devid usbctrl, 
>> +                                                 libxl_device_usbdev  
>> **usbdevs, 
>> +                                                 int *num); 
>> + 
>> +static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid, 
>> +                                       libxl_device_usbdev *usbdev); 
>> + 
>> +/* AO function to remove a usb controller. 
>> + * 
>> + * Generally, it does: 
>> + * 1) check if the usb controller exists or not 
>> + * 2) remove all usb devices under controller 
>> + * 3) remove usb controller information from xenstore 
>> + * 
>> + * Before calling this function, aodev should be properly filled: 
>> + * aodev->ao, aodev->dev, aodev->callback, ... 
>> + */ 
>> +void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, 
>> +                                           libxl__ao_device *aodev) 
>> +{ 
>> +    STATE_AO_GC(aodev->ao); 
>> +    libxl_ctx *ctx = CTX; 
>> +    libxl_device_usbdev *usbdevs = NULL; 
>> +    int num_usbdev = 0; 
>> +    int i, rc; 
>> +    uint32_t domid = ao->domid; 
>> +    int usbctrl_devid = aodev->dev->devid; 
>> +    libxl_device_usbctrl usbctrl; 
>> +    libxl_usbctrlinfo usbctrlinfo; 
>> + 
>> +    libxl_device_usbctrl_init(&usbctrl); 
>> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
>> +    usbctrl.devid = usbctrl_devid; 
>> + 
>> +    rc = libxl_device_usbctrl_getinfo(ctx, domid, &usbctrl, &usbctrlinfo); 
>> +    if (rc) goto out; 
>> + 
>> +    if (usbctrlinfo.type != LIBXL_USBCTRL_TYPE_PV) { 
>> +        LOG(ERROR, "Unsupported USB controller type"); 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    /* Remove usb devices first */ 
>> +    rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, usbctrl_devid, 
>> +                                               &usbdevs, &num_usbdev); 
>> +    if (rc) goto out; 
>> + 
>> +    for (i = 0; i < num_usbdev; i++) { 
>> +        if (libxl__device_usbdev_remove(gc, domid, &usbdevs[i])) { 
>> +            LOG(ERROR, "libxl__device_usbdev_remove failed: controller %d,  
>> " 
>> +                "port %d", usbdevs[i].ctrl, usbdevs[i].port); 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> +    } 
>> + 
>> +    libxl_device_usbctrl_dispose(&usbctrl); 
>> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
>> + 
>> +    /* Remove usbctrl */ 
>> +    return libxl__initiate_device_remove(egc, aodev); 
>> + 
>> +out: 
>> +    libxl_device_usbctrl_dispose(&usbctrl); 
>> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
>> +    aodev->rc = rc; 
>> +    aodev->callback(egc, aodev); 
>> +    return; 
>> +} 
>> + 
>> +libxl_device_usbctrl * 
>> +libxl_device_usbctrl_list(libxl_ctx *ctx, uint32_t domid, int *num) 
>> +{ 
>> +    GC_INIT(ctx); 
>> +    libxl_device_usbctrl *usbctrls = NULL; 
>> +    char *path = NULL; 
>> +    char **entry = NULL; 
>> +    unsigned int nentries = 0; 
>> + 
>> +    *num = 0; 
>> + 
>> +    path = GCSPRINTF("%s/device/vusb", 
>> +                     libxl__xs_get_dompath(gc, domid)); 
>> +    entry = libxl__xs_directory(gc, XBT_NULL, path, &nentries); 
>> + 
>> +    if (entry && nentries) { 
>> +        usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * nentries); 
>> +        libxl_device_usbctrl *usbctrl; 
>> +        libxl_device_usbctrl *end = usbctrls + nentries; 
>> +        for (usbctrl = usbctrls; 
>> +             usbctrl < end; 
>> +             usbctrl++, entry++, (*num)++) { 
>> +            const char *tmp, *be_path, *fe_path; 
>> +            int ret; 
>> + 
>> +            libxl_device_usbctrl_init(usbctrl); 
>> +            usbctrl->devid = atoi(*entry); 
>> + 
>> +#define READ_SUBPATH(path, subpath) ({                                  \ 
>> +        ret = libxl__xs_read_checked(gc, XBT_NULL,                      \ 
>> +                                     GCSPRINTF("%s/" subpath, path),    \ 
>> +                                     &tmp);                             \ 
>> +        if (ret) goto out;                                              \ 
>> +        (char *)tmp;                                                    \ 
>> +    }) 
>> + 
>> +#define READ_SUBPATH_INT(path, subpath) ({                              \ 
>> +        ret = libxl__xs_read_checked(gc, XBT_NULL,                      \ 
>> +                                     GCSPRINTF("%s/" subpath, path),    \ 
>> +                                     &tmp);                             \ 
>> +        if (ret) goto out;                                              \ 
>> +        tmp ? atoi(tmp) : -1;                                           \ 
>> +    }) 
>> + 
>> +            fe_path = GCSPRINTF("%s/%s", path, *entry); 
>> +            be_path = READ_SUBPATH(fe_path, "backend"); 
>> +            usbctrl->backend_domid = READ_SUBPATH_INT(fe_path, "backend-id"); 
>> +            usbctrl->version = READ_SUBPATH_INT(be_path, "usb-ver"); 
>> +            usbctrl->ports = READ_SUBPATH_INT(be_path, "num-ports"); 
>> +            libxl_usbctrl_type_from_string(READ_SUBPATH(be_path, "type"), 
>> +                                           &usbctrl->type); 
>> + 
>> +#undef READ_SUBPATH 
>> +#undef READ_SUBPATH_INT 
>> +       } 
>> +    } 
>> + 
>> +    GC_FREE; 
>> +    return usbctrls; 
>> + 
>> +out: 
>> +    LOG(ERROR, "Unable to list USB Controllers"); 
>> +    libxl_device_usbctrl_list_free(usbctrls, *num); 
>> +    GC_FREE; 
>> +    *num = 0; 
>> +    return NULL; 
>> +} 
>> + 
>> +int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid, 
>> +                                 libxl_device_usbctrl *usbctrl, 
>> +                                 libxl_usbctrlinfo *usbctrlinfo) 
>> +{ 
>> +    GC_INIT(ctx); 
>> +    const char *dompath, *fe_path, *be_path, *tmp; 
>> +    int rc; 
>> + 
>> +    usbctrlinfo->devid = usbctrl->devid; 
>> + 
>> +#define READ_SUBPATH(path, subpath) ({                                  \ 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \ 
>> +                                    GCSPRINTF("%s/" subpath, path),     \ 
>> +                                    &tmp);                              \ 
>> +        if (rc) goto out;                                               \ 
>> +        (char *)tmp;                                                    \ 
>> +    }) 
>> + 
>> +#define READ_SUBPATH_INT(path, subpath) ({                              \ 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL,                       \ 
>> +                                    GCSPRINTF("%s/" subpath, path),     \ 
>> +                                    &tmp);                              \ 
>> +        if (rc) goto out;                                               \ 
>> +        tmp ? atoi(tmp) : -1;                                           \ 
>> +    }) 
>> + 
>> +    dompath = libxl__xs_get_dompath(gc, domid); 
>> +    fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrl->devid); 
>> +    be_path = READ_SUBPATH(fe_path, "backend"); 
>> +    usbctrlinfo->backend = libxl__strdup(NOGC, be_path); 
>> +    usbctrlinfo->backend_id = READ_SUBPATH_INT(fe_path, "backend-id"); 
>> +    usbctrlinfo->state = READ_SUBPATH_INT(fe_path, "state"); 
>> +    usbctrlinfo->evtch = READ_SUBPATH_INT(fe_path, "event-channel"); 
>> +    usbctrlinfo->ref_urb = READ_SUBPATH_INT(fe_path, "urb-ring-ref"); 
>> +    usbctrlinfo->ref_conn = READ_SUBPATH_INT(fe_path, "urb-ring-ref"); 
>> +    tmp = READ_SUBPATH(be_path, "frontend"); 
>> +    usbctrlinfo->frontend = libxl__strdup(NOGC, tmp); 
>> +    usbctrlinfo->frontend_id = READ_SUBPATH_INT(be_path, "frontend-id"); 
>> +    usbctrlinfo->ports = READ_SUBPATH_INT(be_path, "num-ports"); 
>> +    usbctrlinfo->version = READ_SUBPATH_INT(be_path, "usb-ver");; 
>> +    tmp = READ_SUBPATH(be_path, "type"); 
>> +    libxl_usbctrl_type_from_string(tmp, &usbctrlinfo->type); 
>> + 
>> +#undef READ_SUBPATH 
>> +#undef READ_SUBPATH_INT 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    GC_FREE; 
>> +    return rc; 
>> +} 
>> + 
>> +int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, 
>> +                                  uint32_t domid, 
>> +                                  int devid, 
>> +                                  libxl_device_usbctrl *usbctrl) 
>> +{ 
>> +    libxl_device_usbctrl *usbctrls; 
>> +    int nb = 0; 
>> +    int i, rc; 
>> + 
>> +    usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb); 
>> +    if (!usbctrls) 
>> +        return ERROR_FAIL; 
>> + 
>> +    rc = ERROR_FAIL; 
>> +    for (i = 0; i < nb; i++) { 
>> +        if (devid == usbctrls[i].devid) { 
>> +            libxl_device_usbctrl_copy(ctx, usbctrl, &usbctrls[i]); 
>> +            rc = 0; 
>> +            break; 
>> +        } 
>> +    } 
>> + 
>> +    libxl_device_usbctrl_list_free(usbctrls, nb); 
>> +    return rc; 
>> +} 
>> + 
>> +static char *usbdev_busaddr_to_busid(libxl__gc *gc, int bus, int addr) 
>> +{ 
>> +    DIR *dir; 
>> +    char *busid = NULL; 
>> + 
>> +    /* invalid hostbus or hostaddr */ 
>> +    if (bus < 1 || addr < 1) 
>> +        return NULL; 
>> + 
>> +    dir = opendir(SYSFS_USB_DEV); 
>> +    if (!dir) { 
>> +        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV); 
>> +        return NULL; 
>> +    } 
>> + 
>> +    size_t need = offsetof(struct dirent, d_name) + 
>> +        pathconf(SYSFS_USB_DEV, _PC_NAME_MAX) + 1; 
>> +    struct dirent *de_buf = libxl__zalloc(gc, need); 
>> +    struct dirent *de; 
>> + 
>> +    for (;;) { 
>> +        char *filename; 
>> +        void *buf; 
>> +        int busnum = -1; 
>> +        int devnum = -1; 
>> + 
>> +        int r = readdir_r(dir, de_buf, &de); 
>> +        if (r) { 
>> +            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV); 
>> +            break; 
>> +        } 
>> +        if (!de) 
>> +            break; 
>> + 
>> +        if (!strcmp(de->d_name, ".") || 
>> +            !strcmp(de->d_name, "..")) 
>> +            continue; 
>> + 
>> +        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", de->d_name); 
>> +        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
>> +            devnum = atoi(buf); 
>> + 
>> +        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", de->d_name); 
>> +        if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
>> +            busnum = atoi(buf); 
>> + 
>> +        if (bus == busnum && addr == devnum) { 
>> +            busid = libxl__strdup(gc, de->d_name); 
>> +            break; 
>> +        } 
>> +    } 
>> + 
>> +    closedir(dir); 
>> +    return busid; 
>> +} 
>> + 
>> +static int usbdev_busaddr_from_busid(libxl__gc *gc, const char *busid, 
>> +                                     uint8_t *bus, uint8_t *addr) 
>> +{ 
>> +    char *filename; 
>> +    void *buf; 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid); 
>> +    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
>> +        *bus = atoi((char *)buf); 
>> +    else 
>> +        return ERROR_FAIL; 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid); 
>> +    if (!libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
>> +        *addr = atoi((char *)buf); 
>> +    else 
>> +        return ERROR_FAIL; 
>> + 
>> +    return 0; 
>> +} 
>> + 
>> +static int get_assigned_devices(libxl__gc *gc, 
>> +                                libxl_device_usbdev **list, int *num) 
>> +{ 
>> +    char **domlist; 
>> +    unsigned int ndom = 0; 
>> +    int i, j, k; 
>> +    int rc; 
>> + 
>> +    *list = NULL; 
>> +    *num = 0; 
>> + 
>> +    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &ndom); 
>> +    for (i = 0; i < ndom; i++) { 
>> +        char *path; 
>> +        char **usbctrls; 
>> +        unsigned int nc = 0; 
>> +        uint32_t domid = atoi(domlist[i]); 
>> + 
>> +        path = GCSPRINTF("%s/device/vusb", libxl__xs_get_dompath(gc,  
>> domid)); 
>> +        usbctrls = libxl__xs_directory(gc, XBT_NULL, path, &nc); 
>> + 
>> +        for (j = 0; j < nc; j++) { 
>> +            libxl_device_usbdev *tmp = NULL; 
>> +            int nd = 0; 
>> + 
>> +            rc = libxl__device_usbdev_list_for_usbctrl(gc, domid, 
>> +                                                       atoi(usbctrls[j]), 
>> +                                                       &tmp, &nd); 
>> +            if (rc) goto out; 
>> + 
>> +            if (!nd) continue; 
>> + 
>> +            GCREALLOC_ARRAY(*list, *num + nd); 
>> +            for (k = 0; k < nd; k++) { 
>> +                libxl_device_usbdev_copy(CTX, *list + *num, tmp + k); 
>> +                (*num)++; 
>> +            } 
>> +        } 
>> +    } 
>> + 
>> +    return 0; 
>> + 
>> +out: 
>> +    LOG(ERROR, "fail to get assigned devices"); 
>> +    return rc; 
>> +} 
>> + 
>> +static bool is_usbdev_in_array(libxl_device_usbdev *usbdevs, int num, 
>> +                               libxl_device_usbdev *usbdev) 
>> +{ 
>> +    int i; 
>> + 
>> +    for (i = 0; i < num; i++) { 
>> +        if (usbdevs[i].u.hostdev.hostbus == usbdev->u.hostdev.hostbus && 
>> +            usbdevs[i].u.hostdev.hostaddr == usbdev->u.hostdev.hostaddr) 
>> +            return true; 
>> +    } 
>> + 
>> +    return false; 
>> +} 
>> + 
>> +/* check if USB device type is assignable */ 
>> +static bool is_usbdev_assignable(libxl__gc *gc, libxl_device_usbdev  
>> *usbdev) 
>> +{ 
>> +    int classcode; 
>> +    char *filename; 
>> +    void *buf = NULL; 
>> +    char *busid = NULL; 
>> + 
>> +    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, 
>> +                                    usbdev->u.hostdev.hostaddr); 
>> +    if (!busid) return false; 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", busid); 
>> +    if (libxl__read_sysfs_file_contents(gc, filename, &buf, NULL)) 
>> +        return false; 
>> + 
>> +    classcode = atoi(buf); 
>> +    return classcode != USBHUB_CLASS_CODE; 
>> +} 
>> + 
>> +/* get usb devices under certain usb controller */ 
>> +static int 
>> +libxl__device_usbdev_list_for_usbctrl(libxl__gc *gc, 
>> +                                      uint32_t domid, 
>> +                                      libxl_devid usbctrl, 
>> +                                      libxl_device_usbdev **usbdevs, 
>> +                                      int *num) 
>> +{ 
>> +    const char *fe_path, *be_path, *num_devs; 
>> +    int n, i, rc; 
>> + 
>> +    *usbdevs = NULL; 
>> +    *num = 0; 
>> + 
>> +    fe_path = GCSPRINTF("%s/device/vusb/%d", 
>> +                        libxl__xs_get_dompath(gc, domid), usbctrl); 
>> + 
>> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                GCSPRINTF("%s/backend", fe_path), 
>> +                                &be_path); 
>> +    if (rc) goto out; 
>> + 
>> +    if (!be_path) { 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                GCSPRINTF("%s/num-ports", be_path), 
>> +                                &num_devs); 
>> +    if (rc) goto out; 
>> + 
>> +    n = num_devs ? atoi(num_devs) : 0; 
>> + 
>> +    for (i = 0; i < n; i++) { 
>> +        const char *busid; 
>> +        libxl_device_usbdev *usbdev; 
>> + 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                    GCSPRINTF("%s/port/%d", be_path, i +  
>> 1), 
>> +                                    &busid); 
>> +        if (rc) goto out; 
>> + 
>> +        if (busid && strcmp(busid, "")) { 
>> +            GCREALLOC_ARRAY(*usbdevs, *num + 1); 
>> +            usbdev = *usbdevs + *num; 
>> +            (*num)++; 
>> +            libxl_device_usbdev_init(usbdev); 
>> +            usbdev->ctrl = usbctrl; 
>> +            usbdev->port = i + 1; 
>> +            usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; 
>> +            rc = usbdev_busaddr_from_busid(gc, busid, 
>> +                                           &usbdev->u.hostdev.hostbus, 
>> +                                           &usbdev->u.hostdev.hostaddr); 
>> +            if (rc) goto out; 
>> +        } 
>> +    } 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    return rc; 
>> +} 
>> + 
>> +/* get all usb devices of the domain */ 
>> +libxl_device_usbdev * 
>> +libxl_device_usbdev_list(libxl_ctx *ctx, uint32_t domid, int *num) 
>> +{ 
>> +    GC_INIT(ctx); 
>> +    libxl_device_usbdev *usbdevs = NULL; 
>> +    const char *path; 
>> +    char **usbctrls; 
>> +    unsigned int nc = 0; 
>> +    int i, j; 
>> + 
>> +    *num = 0; 
>> + 
>> +    path = GCSPRINTF("%s/device/vusb", 
>> +                        libxl__xs_get_dompath(gc, domid)); 
>> +    usbctrls = libxl__xs_directory(gc, XBT_NULL, path, &nc); 
>> + 
>> +    for (i = 0; i < nc; i++) { 
>> +        int r, nd = 0; 
>> +        libxl_device_usbdev *tmp = NULL; 
>> + 
>> +        r = libxl__device_usbdev_list_for_usbctrl(gc, domid, 
>> +                                                  atoi(usbctrls[i]), 
>> +                                                  &tmp, &nd); 
>> +        if (!r || !nd) continue; 
>> + 
>> +        usbdevs = libxl__realloc(NOGC, usbdevs, 
>> +                                 sizeof(*usbdevs) * (*num + nd)); 
>> +        for (j = 0; j < nd; j++) { 
>> +            libxl_device_usbdev_copy(ctx, usbdevs + *num, tmp + j); 
>> +            (*num)++; 
>> +        } 
>> +    } 
>> + 
>> +    GC_FREE; 
>> +    return usbdevs; 
>> +} 
>> + 
>> +/* find first unused controller:port and give that to usb device */ 
>> +static int 
>> +libxl__device_usbdev_set_default_usbctrl(libxl__gc *gc, uint32_t domid, 
>> +                                         libxl_device_usbdev *usbdev) 
>> +{ 
>> +    libxl_device_usbctrl *usbctrls = NULL; 
>> +    int numctrl = 0; 
>> +    int i, j, rc; 
>> + 
>> +    usbctrls = libxl_device_usbctrl_list(CTX, domid, &numctrl); 
>> +    if (!numctrl || !usbctrls) { 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    rc = ERROR_FAIL; 
>> +    for (i = 0; i < numctrl; i++) { 
>> +        for (j = 0; j < usbctrls[i].ports; j++) { 
>> +            const char *path, *tmp; 
>> + 
>> +            path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
>> +                             libxl__xs_get_dompath(gc,  
>> LIBXL_TOOLSTACK_DOMID), 
>> +                             domid, usbctrls[i].devid, j + 1); 
>> +            tmp = libxl__xs_read(gc, XBT_NULL, path); 
>> +            if (tmp && !strcmp(tmp, "")) { 
>> +                usbdev->ctrl = usbctrls[i].devid; 
>> +                usbdev->port = j + 1; 
>> +                rc = 0; 
>> +                goto out; 
>> +            } 
>> +        } 
>> +    } 
>> + 
>> +out: 
>> +    libxl_device_usbctrl_list_free(usbctrls, numctrl); 
>> +    return rc; 
>> +} 
>> + 
>> +/* Fill in usb information with default value. 
>> + * 
>> + * Generally, it does: 
>> + * 1) if "controller" is not specified: 
>> + *    - if "port" is not specified, try to find an available  
>> controller:port, 
>> + *      if found, use that; otherwise, create a new controller, use this 
>> + *      controller and its first port 
>> + *    - if "port" is specified, report error. 
>> + * 2) if "controller" is specified, but port is not specified: 
>> + *    try to find an available port under this controller, if found, use 
>> + *    that, otherwise, report error. 
>> + * 3) if both "controller" and "port" are specified: 
>> + *    check the controller:port is available, if not, report error. 
>> + */ 
>> +static int libxl__device_usbdev_setdefault(libxl__gc *gc, 
>> +                                           uint32_t domid, 
>> +                                           libxl_device_usbdev *usbdev, 
>> +                                           bool update_json) 
>> +{ 
>> +    int rc; 
>> + 
>> +    if (!usbdev->type) 
>> +        usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; 
>> + 
>> +    if (usbdev->ctrl == -1) { 
>> +        if (usbdev->port) { 
>> +            LOG(ERROR, "USB controller must be specified if you specify  
>> port"); 
>> +            return ERROR_INVAL; 
>> +        } 
>> + 
>> +        rc = libxl__device_usbdev_set_default_usbctrl(gc, domid, usbdev); 
>> +        /* If no existing controller to host this usb device, add a new one  
>> */ 
>> +        if (rc) { 
>> +            libxl_device_usbctrl *usbctrl; 
>> + 
>> +            GCNEW(usbctrl); 
>> +            libxl_device_usbctrl_init(usbctrl); 
>> +            rc = libxl__device_usbctrl_setdefault(gc, domid, usbctrl); 
>> +            if (rc < 0) goto out; 
>> + 
>> +            if (usbctrl->devid == -1) { 
>> +                usbctrl->devid = libxl__device_nextid(gc, domid, "vusb"); 
>> +                if (usbctrl->devid < 0) { 
>> +                    rc = ERROR_FAIL; 
>> +                    goto out; 
>> +                } 
>> +            } 
>> + 
>> +            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, 
>> +                                                    update_json); 
>> +            if (rc) goto out; 
>> + 
>> +            usbdev->ctrl = usbctrl->devid; 
>> +            usbdev->port = 1; 
>> +        } 
>> +    } else if (!usbdev->port) { 
>> +        /* Valid port starts from 1. Choose port for us. */ 
>> +        int i, ports; 
>> +        const char *fe_path, *be_path, *tmp; 
>> + 
>> +        fe_path = GCSPRINTF("%s/device/vusb/%d", 
>> +                         libxl__xs_get_dompath(gc, domid), usbdev->ctrl); 
>> + 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                    GCSPRINTF("%s/backend", fe_path),  
>> &be_path); 
>> +        if (rc) goto out; 
>> + 
>> +        if (!be_path) { 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> + 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                    GCSPRINTF("%s/num-ports", be_path),  
>> &tmp); 
>> +        if (rc) goto out; 
>> + 
>> +        ports = tmp ? atoi(tmp) : 0; 
>> + 
>> +        for (i = 0; i < ports; i++) { 
>> +            rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                        GCSPRINTF("%s/port/%d", be_path, i  
>> + 1), 
>> +                                        &tmp); 
>> +            if (rc) goto out; 
>> + 
>> +            if (tmp && !strcmp(tmp, "")) { 
>> +                usbdev->port = i + 1; 
>> +                break; 
>> +            } 
>> +        } 
>> + 
>> +        if (!usbdev->port) { 
>> +            LOG(ERROR, "No available port under specified controller"); 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> +    } else { 
>> +        const char *be_path, *tmp; 
>> + 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                    GCSPRINTF("%s/device/vusb/%d/backend", 
>> +                                              libxl__xs_get_dompath(gc,  
>> domid), 
>> +                                              usbdev->ctrl), 
>> +                                    &be_path); 
>> +        if (rc) goto out; 
>> + 
>> +        if (!be_path) { 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> + 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                                    GCSPRINTF("%s/port/%d", 
>> +                                              be_path, usbdev->port), 
>> +                                    &tmp); 
>> +        if (rc) goto out; 
>> + 
>> +        if (tmp && strcmp(tmp, "")) { 
>> +            LOG(ERROR, "The controller port isn't available"); 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> +    } 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    return rc; 
>> +} 
>> + 
>> +/* Add usb information to xenstore 
>> + * 
>> + * Adding a usb device won't create new 'vusb' device, but only write 
>> + * the device busid to the controller:port in xenstore. 
>> + */ 
>> +static int libxl__device_usbdev_add_xenstore(libxl__gc *gc, uint32_t domid, 
>> +                                             libxl_device_usbdev *usbdev, 
>> +                                             bool update_json) 
>> +{ 
>> +    char *be_path, *busid; 
>> +    int rc; 
>> +    xs_transaction_t t = XBT_NULL; 
>> +    libxl_domain_config d_config; 
>> +    libxl_device_usbdev usbdev_saved; 
>> +    libxl__domain_userdata_lock *lock = NULL; 
>> + 
>> +    libxl_domain_config_init(&d_config); 
>> +    libxl_device_usbdev_init(&usbdev_saved); 
>> +    libxl_device_usbdev_copy(CTX, &usbdev_saved, usbdev); 
>> + 
>> +    busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, 
>> +                                    usbdev->u.hostdev.hostaddr); 
>> +    if (!busid) { 
>> +        LOG(DEBUG, "Fail to get busid of usb device"); 
>> +        goto out; 
>> +    } 
>> + 
>> +    if (update_json) { 
>> +        lock = libxl__lock_domain_userdata(gc, domid); 
>> +        if (!lock) { 
>> +            rc = ERROR_LOCK_FAIL; 
>> +            goto out; 
>> +        } 
>> + 
>> +        rc = libxl__get_domain_configuration(gc, domid, &d_config); 
>> +        if (rc) goto out; 
>> + 
>> +        DEVICE_ADD(usbdev, usbdevs, domid, &usbdev_saved, 
>> +                   COMPARE_USB, &d_config); 
>> +    } 
>> + 
>> +    for (;;) { 
>> +        rc = libxl__xs_transaction_start(gc, &t); 
>> +        if (rc) goto out; 
>> + 
>> +        if (update_json) { 
>> +            rc = libxl__set_domain_configuration(gc, domid, &d_config); 
>> +            if (rc) goto out; 
>> +        } 
>> + 
>> +        be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
>> +                            libxl__xs_get_dompath(gc,  
>> LIBXL_TOOLSTACK_DOMID), 
>> +                            domid, usbdev->ctrl, usbdev->port); 
>> + 
>> +        LOG(DEBUG, "Adding usb device %s to xenstore: controller %d, port  
>> %d", 
>> +            busid, usbdev->ctrl, usbdev->port); 
>> + 
>> +        if (libxl__xs_write_checked(gc, t, be_path, busid)) 
>> +            goto out; 
>> + 
>> +        rc = libxl__xs_transaction_commit(gc, &t); 
>> +        if (!rc) break; 
>> +        if (rc < 0) goto out; 
>> +    } 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    if (lock) libxl__unlock_domain_userdata(lock); 
>> +    libxl_device_usbdev_dispose(&usbdev_saved); 
>> +    libxl_domain_config_dispose(&d_config); 
>> +    return rc; 
>> +} 
>> + 
>> +static int libxl__device_usbdev_remove_xenstore(libxl__gc *gc, uint32_t  
>> domid, 
>> +                                                libxl_device_usbdev  
>> *usbdev) 
>> +{ 
>> +    char *be_path; 
>> + 
>> +    be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
>> +                        libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID), 
>> +                        domid, usbdev->ctrl, usbdev->port); 
>> + 
>> +    LOG(DEBUG, "Removing usb device from xenstore: controller %d, port %d", 
>> +        usbdev->ctrl, usbdev->port); 
>> + 
>> +    return libxl__xs_write_checked(gc, XBT_NULL, be_path, ""); 
>> +} 
>> + 
>> +static char *usbdev_busid_from_ctrlport(libxl__gc *gc, uint32_t domid, 
>> +                                        libxl_device_usbdev *usbdev) 
>> +{ 
>> +    return libxl__xs_read(gc, XBT_NULL, 
>> +                          GCSPRINTF("%s/backend/vusb/%d/%d/port/%d", 
>> +                              libxl__xs_get_dompath(gc,  
>> LIBXL_TOOLSTACK_DOMID), 
>> +                          domid, usbdev->ctrl, usbdev->port)); 
>> +} 
>> + 
>> +/* get original driver path of usb interface, stored in @drvpath */ 
>> +static int usbintf_get_drvpath(libxl__gc *gc, const char *intf, char  
>> **drvpath) 
>> +{ 
>> +    char *spath, *dp = NULL; 
>> +    struct stat st; 
>> +    int rc; 
>> + 
>> +    spath = GCSPRINTF(SYSFS_USB_DEV"/%s/driver", intf); 
>> + 
>> +    rc = lstat(spath, &st); 
>> +    if (rc == 0) { 
>> +        /* Find the canonical path to the driver. */ 
>> +        dp = libxl__zalloc(gc, PATH_MAX); 
>> +        dp = realpath(spath, dp); 
>> +    } else if (errno != ENOENT) { 
>> +        LOGE(ERROR, "lstat failed: '%s'", spath); 
>> +        return ERROR_FAIL; 
>> +    } 
>> + 
>> +    *drvpath = dp; 
>> + 
>> +    return 0; 
>> +} 
>> + 
>> +static int sysfs_write_intf(libxl__gc *gc, const char *intf, const char  
>> *path) 
>> +{ 
>> +    int rc, fd = -1; 
>> + 
>> +    fd = open(path, O_WRONLY); 
>> +    if (fd < 0) { 
>> +        LOGE(ERROR, "open file failed: '%s'", path); 
>> +        return ERROR_FAIL; 
>> +    } 
>> + 
>> +    rc = write(fd, intf, strlen(intf)); 
>> +    close(fd); 
>> +    if (rc < 0) { 
>> +        LOGE(ERROR, "write '%s' to '%s' failed", intf, path); 
>> +        return errno; 
>> +    } 
>> +    if (rc != strlen(intf)) { 
>> +        LOG(ERROR, "write '%s' to '%s' failed: incorrect write count", 
>> +            intf, path); 
>> +        return ERROR_FAIL; 
>> +    } 
>> + 
>> +    return 0; 
>> +} 
>> + 
>> +static int unbind_usbintf(libxl__gc *gc, const char *intf) 
>> +{ 
>> +    char *path; 
>> + 
>> +    path = GCSPRINTF(SYSFS_USB_DEV"/%s/driver/unbind", intf); 
>> +    return sysfs_write_intf(gc, intf, path); 
>> +} 
>> + 
>> +static int bind_usbintf(libxl__gc *gc, const char *intf, const char  
>> *drvpath) 
>> +{ 
>> +    char *path; 
>> +    struct stat st; 
>> + 
>> +    path = GCSPRINTF("%s/%s", drvpath, intf); 
>> +    /* if already bound, return */ 
>> +    if (!lstat(path, &st)) 
>> +        return 0; 
>> + 
>> +    path = GCSPRINTF("%s/bind", drvpath); 
>> +    return sysfs_write_intf(gc, intf, path); 
>> +} 
>> + 
>> +/* Is usb interface bound to usbback? */ 
>> +static int usbintf_is_assigned(libxl__gc *gc, char *intf) 
>> +{ 
>> +    char *spath; 
>> +    int rc; 
>> +    struct stat st; 
>> + 
>> +    spath = GCSPRINTF(SYSFS_USBBACK_DRIVER"/%s", intf); 
>> +    rc = lstat(spath, &st); 
>> + 
>> +    if (rc == 0) 
>> +        return 1; 
>> +    if (rc < 0 && errno == ENOENT) 
>> +        return 0; 
>> +    LOGE(ERROR, "Accessing %s", spath); 
>> +    return -1; 
>> +} 
>> + 
>> +static int usbdev_get_all_interfaces(libxl__gc *gc, const char *busid, 
>> +                                     char ***intfs, int *num) 
>> +{ 
>> +    DIR *dir; 
>> +    char *buf; 
>> +    int rc; 
>> + 
>> +    *intfs = NULL; 
>> +    *num = 0; 
>> + 
>> +    buf = GCSPRINTF("%s:", busid); 
>> + 
>> +    dir = opendir(SYSFS_USB_DEV); 
>> +    if (!dir) { 
>> +        LOGE(ERROR, "opendir failed: '%s'", SYSFS_USB_DEV); 
>> +        return ERROR_FAIL; 
>> +    } 
>> + 
>> +    size_t need = offsetof(struct dirent, d_name) + 
>> +        pathconf(SYSFS_USB_DEV, _PC_NAME_MAX) + 1; 
>> +    struct dirent *de_buf = libxl__zalloc(gc, need); 
>> +    struct dirent *de; 
>> + 
>> +    for (;;) { 
>> +        int r = readdir_r(dir, de_buf, &de); 
>> + 
>> +        if (r) { 
>> +            LOGE(ERROR, "failed to readdir %s", SYSFS_USB_DEV); 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> +        if (!de) 
>> +            break; 
>> + 
>> +        if (!strcmp(de->d_name, ".") || 
>> +            !strcmp(de->d_name, "..")) 
>> +            continue; 
>> + 
>> +        if (!strncmp(de->d_name, buf, strlen(buf))) { 
>> +            GCREALLOC_ARRAY(*intfs, *num + 1); 
>> +            (*intfs)[*num] = libxl__strdup(gc, de->d_name); 
>> +            (*num)++; 
>> +        } 
>> +    } 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    closedir(dir); 
>> +    return rc; 
>> +} 
>> + 
>> +/* Encode usb interface so that it could be written to xenstore as a key. 
>> + * 
>> + * Since xenstore key cannot include '.' or ':', we'll change '.' to '_', 
>> + * change ':' to '@'. For example, 3-1:2.1 will be encoded to 3-1@2_1. 
>> + * This will be used to save original driver of USB device to xenstore. 
>> + */ 
>> +static char *usb_interface_xenstore_encode(libxl__gc *gc, const char  
>> *busid) 
>> +{ 
>> +    char *str = libxl__strdup(gc, busid); 
>> +    int i, len = strlen(str); 
>> + 
>> +    for (i = 0; i < len; i++) { 
>> +        if (str[i] == '.') 
>> +            str[i] = '_'; 
>> +        if (str[i] == ':') 
>> +            str[i] = '@'; 
>> +    } 
>> +    return str; 
>> +} 
>> + 
>> +/* Unbind USB device from "usbback" driver. 
>> + * 
>> + * If there are many interfaces under USB device, check each interface, 
>> + * unbind from "usbback" driver and rebind to its original driver. 
>> + */ 
>> +static int usbback_dev_unassign(libxl__gc *gc, const char *busid) 
>> +{ 
>> +    char **intfs = NULL; 
>> +    char *usbdev_encode = NULL; 
>> +    char *path = NULL; 
>> +    int i, num = 0; 
>> +    int rc; 
>> + 
>> +    if (usbdev_get_all_interfaces(gc, busid, &intfs, &num) < 0) 
>> +        return ERROR_FAIL; 
>> + 
>> +    usbdev_encode = usb_interface_xenstore_encode(gc, busid); 
>> + 
>> +    for (i = 0; i < num; i++) { 
>> +        char *intf = intfs[i]; 
>> +        char *usbintf_encode = NULL; 
>> +        const char *drvpath; 
>> + 
>> +        /* check if the USB interface is already bound to "usbback" */ 
>> +        if (usbintf_is_assigned(gc, intf) > 0) { 
>> +            /* unbind interface from usbback driver */ 
>> +            if (unbind_usbintf(gc, intf) < 0) { 
>> +                rc = ERROR_FAIL; 
>> +                goto out; 
>> +            } 
>> +        } 
>> + 
>> +        /* try to rebind USB interface to its originial driver. 
>> +         * If rebinding failed, export warning so that user can 
>> +         * handle it later. 
>> +         */ 
>> +        usbintf_encode = usb_interface_xenstore_encode(gc, intf); 
>> +        path = GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path", 
>> +                         usbdev_encode, usbintf_encode); 
>> +        rc = libxl__xs_read_checked(gc, XBT_NULL, path, &drvpath); 
>> +        if (rc) continue; 
>> + 
>> +        if (drvpath && bind_usbintf(gc, intf, drvpath)) 
>> +            LOGE(WARN, "Couldn't rebind %s to %s", intf, drvpath); 
>> +    } 
>> + 
>> +    /* finally, remove xenstore driver path */ 
>> +    path = GCSPRINTF(USBBACK_INFO_PATH"/%s", usbdev_encode); 
>> +    libxl__xs_rm_checked(gc, XBT_NULL, path); 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    return rc; 
>> +} 
>> + 
>> +/* Bind USB device to "usbback" driver. 
>> + * 
>> + * If there are many interfaces under USB device, check each interface, 
>> + * unbind from original driver and bind to "usbback" driver. 
>> + */ 
>> +static int usbback_dev_assign(libxl__gc *gc, const char *busid) 
>> +{ 
>> +    char **intfs = NULL; 
>> +    int num = 0, i; 
>> +    int rc; 
>> +    char *usbdev_encode = NULL; 
>> + 
>> +    rc = usbdev_get_all_interfaces(gc, busid, &intfs, &num); 
>> +    if (rc) 
>> +        return rc; 
>> + 
>> +    usbdev_encode = usb_interface_xenstore_encode(gc, busid); 
>> + 
>> +    for (i = 0; i < num; i++) { 
>> +        char *intf = intfs[i]; 
>> +        char *drvpath = NULL; 
>> + 
>> +        /* already assigned to usbback */ 
>> +        if (usbintf_is_assigned(gc, intf) > 0) 
>> +            continue; 
>> + 
>> +        rc = usbintf_get_drvpath(gc, intf, &drvpath); 
>> +        if (rc < 0) goto out; 
>> + 
>> +        if (drvpath) { 
>> +            /* write driver path to xenstore for later rebinding */ 
>> +            char *usbintf_encode = NULL; 
>> +            char *path; 
>> + 
>> +            usbintf_encode = usb_interface_xenstore_encode(gc, intf); 
>> +            path = GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path", 
>> +                             usbdev_encode, usbintf_encode); 
>> +            if (libxl__xs_write_checked(gc, XBT_NULL, path, drvpath) < 0) 
>> +                goto out; 
>> + 
>> +            /* unbind interface from original driver */ 
>> +            if (unbind_usbintf(gc, intf) < 0) { 
>> +                rc = ERROR_FAIL; 
>> +                goto out; 
>> +            } 
>> +        } 
>> + 
>> +        /* bind interface to usbback */ 
>> +        if (bind_usbintf(gc, intf, SYSFS_USBBACK_DRIVER) < 0) { 
>> +            LOG(ERROR, "Couldn't bind %s to %s", intf,  
>> SYSFS_USBBACK_DRIVER); 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> +    } 
>> + 
>> +    return 0; 
>> + 
>> +out: 
>> +    /* some interfaces might be bound to usbback, unbind it and 
>> +     * rebind it to its original driver 
>> +     */ 
>> +    usbback_dev_unassign(gc, busid); 
>> +    return rc; 
>> +} 
>> + 
>> +static int do_usbdev_add(libxl__gc *gc, uint32_t domid, 
>> +                         libxl_device_usbdev *usbdev, 
>> +                         bool update_json) 
>> +{ 
>> +    int rc; 
>> +    char *busid; 
>> +    libxl_device_usbctrl usbctrl; 
>> +    libxl_usbctrlinfo usbctrlinfo; 
>> + 
>> +    libxl_device_usbctrl_init(&usbctrl); 
>> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
>> +    usbctrl.devid = usbdev->ctrl; 
>> + 
>> +    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); 
>> +    if (rc) goto out; 
>> + 
>> +    switch (usbctrlinfo.type) { 
>> +    case LIBXL_USBCTRL_TYPE_PV: 
>> +        busid = usbdev_busaddr_to_busid(gc, usbdev->u.hostdev.hostbus, 
>> +                                        usbdev->u.hostdev.hostaddr); 
>> +        if (!busid) { 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> + 
>> +        rc = libxl__device_usbdev_add_xenstore(gc, domid, usbdev,  
>> update_json); 
>> +        if (rc) goto out; 
>> + 
>> +        rc = usbback_dev_assign(gc, busid); 
>> +        if (rc) { 
>> +            libxl__device_usbdev_remove_xenstore(gc, domid, usbdev); 
>> +            goto out; 
>> +        } 
>> +        break; 
>> +    case LIBXL_USBCTRL_TYPE_DEVICEMODEL: 
>> +    default: 
>> +        LOG(ERROR, "Unsupported usb controller type"); 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    libxl_device_usbctrl_dispose(&usbctrl); 
>> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
>> +    return rc; 
>> +} 
>> + 
>> +/* AO operation to add a usb device. 
>> + * 
>> + * Generally, it does: 
>> + * 1) check if the usb device type is assignable 
>> + * 2) check if the usb device is already assigned to a domain 
>> + * 3) add 'busid' of the usb device to xenstore contoller/port/. 
>> + *    (PVUSB driver watches the xenstore changes and will detect that.) 
>> + * 4) unbind usb device from original driver and bind to usbback. 
>> + *    If usb device has many interfaces, then: 
>> + *    - unbind each interface from its original driver and bind to usbback. 
>> + *    - store the original driver to xenstore for later rebinding when 
>> + *      detaching the device. 
>> + * 
>> + * Before calling this function, aodev should be properly filled: 
>> + * aodev->ao, aodev->callback, aodev->update_json, ... 
>> + */ 
>> +void libxl__device_usbdev_add(libxl__egc *egc, uint32_t domid, 
>> +                              libxl_device_usbdev *usbdev, 
>> +                              libxl__ao_device *aodev) 
>> +{ 
>> +    STATE_AO_GC(aodev->ao); 
>> +    int rc; 
>> +    libxl_device_usbdev *assigned; 
>> +    int num_assigned; 
>> +    libxl_device_usbctrl usbctrl; 
>> +    libxl_usbctrlinfo usbctrlinfo; 
>> + 
>> +    libxl_device_usbctrl_init(&usbctrl); 
>> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
>> + 
>> +    /* Currently only support adding USB device from Dom0 backend. 
>> +     * So, if USB controller is specified, check its backend domain, 
>> +     * if it's not Dom0, report error. 
>> +     */ 
>> +    if (usbdev->ctrl != -1) { 
>> +        usbctrl.devid = usbdev->ctrl; 
>> +        rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl,  
>> &usbctrlinfo); 
>> +        if (rc) goto out; 
>> + 
>> +        if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) { 
>> +            LOG(ERROR, "Don't support adding USB device from non-Dom0  
>> backend"); 
>> +            rc = ERROR_INVAL; 
>> +            goto out; 
>> +        } 
>> +    } 
>> + 
>> +    /* check usb device is assignable type */ 
>> +    if (!is_usbdev_assignable(gc, usbdev)) { 
>> +        LOG(ERROR, "USB device is not assignable."); 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    /* check usb device is already assigned */ 
>> +    rc = get_assigned_devices(gc, &assigned, &num_assigned); 
>> +    if (rc) { 
>> +        LOG(ERROR, "cannot determine if device is assigned," 
>> +                   " refusing to continue"); 
>> +        goto out; 
>> +    } 
>> + 
>> +    if (is_usbdev_in_array(assigned, num_assigned, usbdev)) { 
>> +        LOG(ERROR, "USB device already attached to a domain"); 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    /* fill default values, e.g, if usbdev->ctrl and usbdev->port 
>> +     * not specified, choose available controller:port and fill in. */ 
>> +    rc = libxl__device_usbdev_setdefault(gc, domid, usbdev, 
>> +                                         aodev->update_json); 
>> +    if (rc) goto out; 
>> + 
>> +    /* do actual adding usb device operation */ 
>> +    rc = do_usbdev_add(gc, domid, usbdev, aodev->update_json); 
>> + 
>> +out: 
>> +    libxl_device_usbctrl_dispose(&usbctrl); 
>> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
>> +    aodev->rc = rc; 
>> +    aodev->callback(egc, aodev); 
>> +    return; 
>> +} 
>> + 
>> +static int do_usbdev_remove(libxl__gc *gc, uint32_t domid, 
>> +                            libxl_device_usbdev *usbdev) 
>> +{ 
>> +    int rc; 
>> +    char *busid; 
>> +    libxl_device_usbctrl usbctrl; 
>> +    libxl_usbctrlinfo usbctrlinfo; 
>> + 
>> +    libxl_device_usbctrl_init(&usbctrl); 
>> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
>> +    usbctrl.devid = usbdev->ctrl; 
>> + 
>> +    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); 
>> +    if (rc) goto out; 
>> + 
>> +    switch (usbctrlinfo.type) { 
>> +    case LIBXL_USBCTRL_TYPE_PV: 
>> +        busid = usbdev_busid_from_ctrlport(gc, domid, usbdev); 
>> +        if (!busid) { 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> + 
>> +        rc = libxl__device_usbdev_remove_xenstore(gc, domid, usbdev); 
>> +        if (rc) goto out; 
>> + 
>> +        rc = usbback_dev_unassign(gc, busid); 
>> +        if (rc) { 
>> +            /* Till here, usb device information is already removed 
>> +             * from xenstore, usb list couldn't list it any more. 
>> +             * If unassign usb device from usbback failed, export 
>> +             * warning only so that user could handle driver status 
>> +             * later. 
>> +             */ 
>> +            LOG(WARN, "unbind usb device from usbback and rebind to its " 
>> +                      "original driver failed"); 
>> +        } 
>> +        break; 
>> +    case LIBXL_USBCTRL_TYPE_DEVICEMODEL: 
>> +    default: 
>> +        LOG(ERROR, "Unsupported usb controller type"); 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    libxl_device_usbctrl_dispose(&usbctrl); 
>> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
>> +    return rc; 
>> +} 
>> + 
>> +/* Operation to remove usb device. 
>> + * 
>> + * Generally, it does: 
>> + * 1) check if the usb device is assigned to the domain 
>> + * 2) remove the usb device from xenstore controller/port. 
>> + * 3) unbind usb device from usbback and rebind to its original driver. 
>> + *    If usb device has many interfaces, do it to each interface. 
>> + */ 
>> +static int libxl__device_usbdev_remove(libxl__gc *gc, uint32_t domid, 
>> +                                       libxl_device_usbdev *usbdev) 
>> +{ 
>> +    libxl_usbctrlinfo usbctrlinfo; 
>> +    libxl_device_usbctrl usbctrl; 
>> +    int rc; 
>> + 
>> +    if (usbdev->ctrl < 0 || usbdev->port < 1) { 
>> +        LOG(ERROR, "Invalid USB device"); 
>> +        return ERROR_FAIL; 
>> +    } 
>> + 
>> +    libxl_device_usbctrl_init(&usbctrl); 
>> +    libxl_usbctrlinfo_init(&usbctrlinfo); 
>> +    usbctrl.devid = usbdev->ctrl; 
>> + 
>> +    rc = libxl_device_usbctrl_getinfo(CTX, domid, &usbctrl, &usbctrlinfo); 
>> +    if (rc) goto out; 
>> + 
>> +    if (usbctrlinfo.backend_id != LIBXL_TOOLSTACK_DOMID) { 
>> +        LOG(ERROR, "Don't support removing USB device from non-Dom0  
>> backend"); 
>> +        rc = ERROR_INVAL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    /* do actual removing usb device operation */ 
>> +    rc = do_usbdev_remove(gc, domid, usbdev); 
>> + 
>> +out: 
>> +    libxl_device_usbctrl_dispose(&usbctrl); 
>> +    libxl_usbctrlinfo_dispose(&usbctrlinfo); 
>> +    return rc; 
>> +} 
>> + 
>> +int libxl_device_usbdev_remove(libxl_ctx *ctx, uint32_t domid, 
>> +                               libxl_device_usbdev *usbdev, 
>> +                               const libxl_asyncop_how *ao_how) 
>> + 
>> +{ 
>> +    AO_CREATE(ctx, domid, ao_how); 
>> +    int rc; 
>> + 
>> +    rc = libxl__device_usbdev_remove(gc, domid, usbdev); 
>> + 
>> +    libxl__ao_complete(egc, ao, rc); 
>> +    return AO_INPROGRESS; 
>> +} 
>> + 
>> +int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, 
>> +                                    uint32_t domid, 
>> +                                    int ctrl, 
>> +                                    int port, 
>> +                                    libxl_device_usbdev *usbdev) 
>> +{ 
>> +    GC_INIT(ctx); 
>> +    const char *dompath, *be_path, *busid; 
>> +    int rc; 
>> + 
>> +    dompath = libxl__xs_get_dompath(gc, domid); 
>> + 
>> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                  GCSPRINTF("%s/device/vusb/%d/backend", dompath, ctrl), 
>> +                  &be_path); 
>> +    if (rc) goto out; 
>> + 
>> +    if (!be_path) { 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    rc = libxl__xs_read_checked(gc, XBT_NULL, 
>> +                           GCSPRINTF("%s/port/%d", be_path, port), 
>> +                           &busid); 
>> +    if (rc) goto out; 
>> + 
>> +    if (!busid || !strcmp(busid, "")) { 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    usbdev->ctrl = ctrl; 
>> +    usbdev->port = port; 
>> +    usbdev->type = LIBXL_USBDEV_TYPE_HOSTDEV; 
>> +    rc = usbdev_busaddr_from_busid(gc, busid, 
>> +                                   &usbdev->u.hostdev.hostbus, 
>> +                                   &usbdev->u.hostdev.hostaddr); 
>> + 
>> +out: 
>> +    GC_FREE; 
>> +    return rc; 
>> +} 
>> +/* 
>> + * Local variables: 
>> + * mode: C 
>> + * c-basic-offset: 4 
>> + * indent-tabs-mode: nil 
>> + * End: 
>> + */ 
>> diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl 
>> index 4d78f86..ed7da81 100644 
>> --- a/tools/libxl/libxl_types.idl 
>> +++ b/tools/libxl/libxl_types.idl 
>> @@ -603,6 +603,35 @@ libxl_device_rdm = Struct("device_rdm", [ 
>>      ("policy", libxl_rdm_reserve_policy), 
>>      ]) 
>>   
>> +libxl_usbctrl_type = Enumeration("usbctrl_type", [ 
>> +    (0, "AUTO"), 
>> +    (1, "PV"), 
>> +    (2, "DEVICEMODEL"), 
>> +    ]) 
>> + 
>> +libxl_usbdev_type = Enumeration("usbdev_type", [ 
>> +    (1, "hostdev"), 
>> +    ]) 
>> + 
>> +libxl_device_usbctrl = Struct("device_usbctrl", [ 
>> +    ("type", libxl_usbctrl_type), 
>> +    ("devid", libxl_devid), 
>> +    ("version", integer), 
>> +    ("ports", integer), 
>> +    ("backend_domid", libxl_domid), 
>> +    ("backend_domname", string), 
>> +   ]) 
>> + 
>> +libxl_device_usbdev = Struct("device_usbdev", [ 
>> +    ("ctrl", libxl_devid), 
>> +    ("port", integer), 
>> +    ("u", KeyedUnion(None, libxl_usbdev_type, "type", 
>> +           [("hostdev", Struct(None, [ 
>> +                 ("hostbus",   uint8), 
>> +                 ("hostaddr",  uint8)])), 
>> +           ])), 
>> +    ]) 
>> + 
>>  libxl_device_dtdev = Struct("device_dtdev", [ 
>>      ("path", string), 
>>      ]) 
>> @@ -641,6 +670,8 @@ libxl_domain_config = Struct("domain_config", [ 
>>      # a channel manifests as a console with a name, 
>>      # see docs/misc/channels.txt 
>>      ("channels", Array(libxl_device_channel, "num_channels")), 
>> +    ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")), 
>> +    ("usbdevs", Array(libxl_device_usbdev, "num_usbdevs")), 
>>   
>>      ("on_poweroff", libxl_action_on_shutdown), 
>>      ("on_reboot", libxl_action_on_shutdown), 
>> @@ -684,6 +715,21 @@ libxl_vtpminfo = Struct("vtpminfo", [ 
>>      ("uuid", libxl_uuid), 
>>      ], dir=DIR_OUT) 
>>   
>> +libxl_usbctrlinfo = Struct("usbctrlinfo", [ 
>> +    ("type", libxl_usbctrl_type), 
>> +    ("devid", libxl_devid), 
>> +    ("version", integer), 
>> +    ("ports", integer), 
>> +    ("backend", string), 
>> +    ("backend_id", uint32), 
>> +    ("frontend", string), 
>> +    ("frontend_id", uint32), 
>> +    ("state", integer), 
>> +    ("evtch", integer), 
>> +    ("ref_urb", integer), 
>> +    ("ref_conn", integer), 
>> +    ], dir=DIR_OUT) 
>> + 
>>  libxl_vcpuinfo = Struct("vcpuinfo", [ 
>>      ("vcpuid", uint32), 
>>      ("cpu", uint32), 
>> diff --git a/tools/libxl/libxl_types_internal.idl  
>> b/tools/libxl/libxl_types_internal.idl 
>> index 5e55685..696f5f8 100644 
>> --- a/tools/libxl/libxl_types_internal.idl 
>> +++ b/tools/libxl/libxl_types_internal.idl 
>> @@ -22,6 +22,7 @@ libxl__device_kind = Enumeration("device_kind", [ 
>>      (6, "VKBD"), 
>>      (7, "CONSOLE"), 
>>      (8, "VTPM"), 
>> +    (9, "VUSB"), 
>>      ]) 
>>   
>>  libxl__console_backend = Enumeration("console_backend", [ 
>> diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c 
>> index 7f612a6..f7ba880 100644 
>> --- a/tools/libxl/libxl_utils.c 
>> +++ b/tools/libxl/libxl_utils.c 
>> @@ -1314,6 +1314,24 @@ int libxl__random_bytes(libxl__gc *gc, uint8_t *buf,  
>> size_t len) 
>>      return ret; 
>>  } 
>>   
>> +void libxl_device_usbctrl_list_free(libxl_device_usbctrl *list, int nr) 
>> +{ 
>> +   int i; 
>> + 
>> +   for (i = 0; i < nr; i++) 
>> +       libxl_device_usbctrl_dispose(&list[i]); 
>> +   free(list); 
>> +} 
>> + 
>> +void libxl_device_usbdev_list_free(libxl_device_usbdev *list, int nr) 
>> +{ 
>> +   int i; 
>> + 
>> +   for (i = 0; i < nr; i++) 
>> +       libxl_device_usbdev_dispose(&list[i]); 
>> +   free(list); 
>> +} 
>> + 
>>  /* 
>>   * Local variables: 
>>   * mode: C 
>> diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h 
>> index 339ebdf..4495417 100644 
>> --- a/tools/libxl/libxl_utils.h 
>> +++ b/tools/libxl/libxl_utils.h 
>> @@ -76,6 +76,11 @@ int libxl_uuid_to_device_vtpm(libxl_ctx *ctx, uint32_t  
>> domid, 
>>                                 libxl_uuid *uuid, libxl_device_vtpm *vtpm); 
>>  int libxl_devid_to_device_vtpm(libxl_ctx *ctx, uint32_t domid, 
>>                                 int devid, libxl_device_vtpm *vtpm); 
>> +int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, uint32_t domid, 
>> +                                  int devid, libxl_device_usbctrl  
>> *usbctrl); 
>> +int libxl_ctrlport_to_device_usbdev(libxl_ctx *ctx, uint32_t domid, 
>> +                                    int ctrl, int port, 
>> +                                    libxl_device_usbdev *usbdev); 
>>   
>>  int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits); 
>>      /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be 
>  
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RESEND][PATCH V9 5/7] xl: add pvusb commands
  2015-11-25  9:46 ` [RESEND][PATCH V9 5/7] xl: add pvusb commands Chunyan Liu
@ 2015-12-09 16:42   ` George Dunlap
  0 siblings, 0 replies; 12+ messages in thread
From: George Dunlap @ 2015-12-09 16:42 UTC (permalink / raw)
  To: Chunyan Liu
  Cc: Jürgen Groß,
	Wei Liu, Ian Campbell, Ian Jackson, xen-devel, Jim Fehlig,
	Simon Cao

On Wed, Nov 25, 2015 at 9:46 AM, Chunyan Liu <cyliu@suse.com> wrote:
> Add pvusb commands: usbctrl-attach, usbctrl-detach, usb-list,
> usbdev-attach and usbdev-detach.
>
> To attach a usb device to guest through pvusb, one could follow
> following example:
>
>  #xl usbctrl-attach test_vm version=1 ports=8
>
>  #xl usb-list test_vm
>  will show the usb controllers and port usage under the domain.
>
>  #xl usbdev-attach test_vm hostbus=1 hostaddr=2
>  will find the first usable controller:port, and attach usb
>  device whose busnum is 1 and devnum is 6.
>  One could also specify which <controller> and which <port>.
>
>  #xl usbdev-detach test_vm 0 1
>  will detach USB device under controller 0 port 1.
>
>  #xl usbctrl-detach test_vm dev_id
>  will destroy the controller with specified dev_id. Dev_id
>  can be traced in usb-list info.
>
> Signed-off-by: Chunyan Liu <cyliu@suse.com>
> Signed-off-by: Simon Cao <caobosimon@gmail.com>

Reviewed-by: George Dunlap <george.dunlap@citrix.com>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RESEND][PATCH V9 7/7] domcreate: support pvusb in configuration file
  2015-11-25  9:46 ` [RESEND][PATCH V9 7/7] domcreate: support pvusb in configuration file Chunyan Liu
@ 2015-12-09 16:52   ` George Dunlap
  0 siblings, 0 replies; 12+ messages in thread
From: George Dunlap @ 2015-12-09 16:52 UTC (permalink / raw)
  To: Chunyan Liu
  Cc: Jürgen Groß,
	Wei Liu, Ian Campbell, Ian Jackson, xen-devel, Jim Fehlig,
	Simon Cao

On Wed, Nov 25, 2015 at 9:46 AM, Chunyan Liu <cyliu@suse.com> wrote:
> Add code to support pvusb in domain config file. One could specify
> usbctrl and usb in domain's configuration file and create domain,
> then usb controllers will be created and usb device would be attached
> to guest automatically.
>
> One could specify usb controllers and usb devices in config file
> like this:
> usbctrl=['version=2,ports=4', 'version=1, ports=4', ]
> usbdev=['hostbus=2, hostaddr=1, controller=0,port=1', ]
>
> Signed-off-by: Chunyan Liu <cyliu@suse.com>
> Signed-off-by: Simon Cao <caobosimon@gmail.com>

Reviewed-by: George Dunlap <george.dunlap@citrix.com>

With one change...

> +=item B<usbdev=[ "USB_SPEC_STRING", "USB_SPEC_STRING", ... ]>
> +
> +Specifiec the USB devices to be attached to the guest at boot. Each

This should be "Specifies".  (In my review of v8 I made a typo -- oops!)

 -George

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2015-12-09 16:52 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-25  9:46 [RESEND][PATCH V9 0/7] xen pvusb toolstack work Chunyan Liu
2015-11-25  9:46 ` [RESEND][PATCH V9 1/7] libxl: export some functions for pvusb use Chunyan Liu
2015-11-25  9:46 ` [RESEND][PATCH V9 2/7] libxl_utils: add internal function to read sysfs file contents Chunyan Liu
2015-11-25  9:46 ` [RESEND][PATCH V9 3/7] libxl: add pvusb API Chunyan Liu
2015-12-08  5:50   ` Chun Yan Liu
2015-12-08 11:26     ` George Dunlap
2015-11-25  9:46 ` [RESEND][PATCH V9 4/7] libxl: add libxl_device_usbdev_assignable_list API Chunyan Liu
2015-11-25  9:46 ` [RESEND][PATCH V9 5/7] xl: add pvusb commands Chunyan Liu
2015-12-09 16:42   ` George Dunlap
2015-11-25  9:46 ` [RESEND][PATCH V9 6/7] xl: add usbdev-assignable-list command Chunyan Liu
2015-11-25  9:46 ` [RESEND][PATCH V9 7/7] domcreate: support pvusb in configuration file Chunyan Liu
2015-12-09 16:52   ` George Dunlap

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.