All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
From: Chunyan Liu <cyliu@suse.com>
To: xen-devel@lists.xen.org
Cc: wei.liu2@citrix.com, ian.campbell@citrix.com,
	george.dunlap@eu.citrix.com, Ian.Jackson@eu.citrix.com,
	Chunyan Liu <cyliu@suse.com>, Simon Cao <caobosimon@gmail.com>
Subject: [PATCH V4 3/7] libxl: add pvusb API
Date: Wed, 10 Jun 2015 11:20:37 +0800	[thread overview]
Message-ID: <1433906441-3280-4-git-send-email-cyliu@suse.com> (raw)
In-Reply-To: <1433906441-3280-1-git-send-email-cyliu@suse.com>

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:
  - make libxl_device_usbctrl_add async, to be consistent with
    libxl_device_usbctrl_remove, using MACROs DEFINE_DEVICE_ADD
    and DEFINE_DEVICES_ADD, adjusting related codes.
  - correct update_json related processing.
  - use libxl__* helper functions instead of calloc, realloc
    and strdup, etc. whereever possible.
  - merge protocol definition pv|qemu in this patch
  - treat it as warning at rebind failure instead of error in usb_remove
  - add documentation docs/misc/pvusb.txt to describe pvusb
    details (toolstack design, libxl design, xenstore info, etc.)
  - other fixes addring Wei and George's comments

 docs/misc/pvusb.txt                  |  243 +++++++
 tools/libxl/Makefile                 |    2 +-
 tools/libxl/libxl.c                  |    6 +
 tools/libxl/libxl.h                  |   65 ++
 tools/libxl/libxl_internal.h         |   16 +-
 tools/libxl/libxl_osdeps.h           |   13 +
 tools/libxl/libxl_pvusb.c            | 1249 ++++++++++++++++++++++++++++++++++
 tools/libxl/libxl_types.idl          |   52 ++
 tools/libxl/libxl_types_internal.idl |    1 +
 tools/libxl/libxl_utils.c            |   16 +
 10 files changed, 1661 insertions(+), 2 deletions(-)
 create mode 100644 docs/misc/pvusb.txt
 create mode 100644 tools/libxl/libxl_pvusb.c

diff --git a/docs/misc/pvusb.txt b/docs/misc/pvusb.txt
new file mode 100644
index 0000000..4cdf965
--- /dev/null
+++ b/docs/misc/pvusb.txt
@@ -0,0 +1,243 @@
+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).
+
+Additionally, for easy use of PVUSB, you need support in the toolstack
+to get the two sides to talk to each other.
+
+2. Specifying a host USB device
+
+QEMU hmp 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 Hub could 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.
diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index cc9c152..b820105 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -95,7 +95,7 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \
 			libxl_internal.o libxl_utils.o libxl_uuid.o \
 			libxl_json.o libxl_aoutils.o libxl_numa.o libxl_vnuma.o \
 			libxl_save_callout.o _libxl_save_msgs_callout.o \
-			libxl_qmp.o libxl_event.o libxl_fork.o $(LIBXL_OBJS-y)
+			libxl_qmp.o libxl_event.o libxl_fork.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 bd4daea..f9331f4 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -1611,6 +1611,8 @@ void libxl__destroy_domid(libxl__egc *egc, libxl__destroy_domid_state *dis)
 
     if (libxl__device_pci_destroy_all(gc, domid) < 0)
         LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "pci shutdown failed for domid %d", domid);
+    if (libxl__device_usb_destroy_all(gc, domid) < 0)
+        LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "usb shutdown failed for domid %d", domid);
     rc = xc_domain_pause(ctx->xch, domid);
     if (rc < 0) {
         LIBXL__LOG_ERRNOVAL(ctx, LIBXL__LOG_ERROR, rc, "xc_domain_pause failed for %d", domid);
@@ -6760,6 +6762,10 @@ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid,
 
     MERGE(pci, pcidevs, COMPARE_PCI, {});
 
+    MERGE(usbctrl, usbctrls, COMPARE_USBCTRL, {});
+
+    MERGE(usb, usbs, 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 0a7913b..d192300 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -123,6 +123,23 @@
 #define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1
 
 /*
+ * LIBXL_HAVE_PVUSB indicates the functions for doing hot-plug of
+ * USB devices through pvusb.
+ *
+ * With this functionality, 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 either 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 either 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.
+ *
+ */
+#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
@@ -1269,6 +1286,54 @@ 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 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)
+                                LIBXL_EXTERNAL_CALLERS_ONLY;
+
+/* USB Devices */
+int libxl_device_usb_add(libxl_ctx *ctx, uint32_t domid, libxl_device_usb *usb,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+
+int libxl_device_usb_remove(libxl_ctx *ctx, uint32_t domid, libxl_device_usb *usb,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+
+libxl_device_usb *
+libxl_device_usb_list(libxl_ctx *ctx, uint32_t domid, int *num);
+
+libxl_device_usb *
+libxl_device_usb_list_per_usbctrl(libxl_ctx *ctx, uint32_t domid,
+                                  int usbctrl, int *num);
+
+void libxl_device_usb_list_free(libxl_device_usb* list, int nr);
+
+int libxl_device_usb_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,
+                             libxl_usbinfo *usbinfo)
+                             LIBXL_EXTERNAL_CALLERS_ONLY;
 /* 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_internal.h b/tools/libxl/libxl_internal.h
index ef7aa1d..89a9f07 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -2439,6 +2439,18 @@ _hidden void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid,
                                    libxl_device_vtpm *vtpm,
                                    libxl__ao_device *aodev);
 
+/* AO operation to connect a PVUSB controller.
+ * Adding a PVUSB controller will add a 'vusb' device entry in xenstore,
+ * and will wait for device connection.
+ */
+_hidden void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
+                               libxl_device_usbctrl *usbctrl,
+                               libxl__ao_device *aodev);
+_hidden int libxl__device_usb_add(libxl__gc *gc, uint32_t domid,
+                                  libxl_device_usb *usb,
+                                  bool update_json);
+_hidden int libxl__device_usb_destroy_all(libxl__gc *gc, uint32_t domid);
+
 /* Internal function to connect a vkb device */
 _hidden int libxl__device_vkb_add(libxl__gc *gc, uint32_t domid,
                                   libxl_device_vkb *vkb);
@@ -3646,7 +3658,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) (!strcmp((a)->busid, (b)->busid))
+#define COMPARE_USBCTRL(a, b) ((a)->devid == (b)->devid)
+ 
 /* DEVICE_ADD
  *
  * Add a device in libxl_domain_config structure
diff --git a/tools/libxl/libxl_osdeps.h b/tools/libxl/libxl_osdeps.h
index 08eaf0c..55caf71 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,12 +42,21 @@
 #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"
 #include <libutil.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..a6e1aa1
--- /dev/null
+++ b/tools/libxl/libxl_pvusb.c
@@ -0,0 +1,1249 @@
+/*
+ * 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"
+
+#define USBBACK_INFO_PATH "/libxl/usbback"
+
+#define USBHUB_CLASS_CODE 0x09
+
+static int libxl__device_usbctrl_setdefault(libxl__gc *gc, uint32_t domid,
+                                            libxl_device_usbctrl *usbctrl)
+{
+    int rc;
+
+    if (!usbctrl->version)
+        usbctrl->version = 2;
+
+    if (!usbctrl->ports)
+        usbctrl->ports = 8;
+
+    if (usbctrl->protocol == LIBXL_USB_PROTOCOL_AUTO)
+        usbctrl->protocol = LIBXL_USB_PROTOCOL_PV;
+
+    rc = libxl__resolve_domid(gc, usbctrl->backend_domname,
+                              &usbctrl->backend_domid);
+    return rc;
+}
+
+static 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;
+}
+
+void libxl__device_usbctrl_add(libxl__egc *egc, uint32_t domid,
+                               libxl_device_usbctrl *usbctrl,
+                               libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    flexarray_t *front;
+    flexarray_t *back;
+    libxl__device *device;
+    xs_transaction_t t = XBT_NULL;
+    char *path;
+    int i, rc;
+    libxl_domain_config d_config;
+    libxl_device_usbctrl usbctrl_saved;
+    libxl__domain_userdata_lock *lock = NULL;
+
+    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;
+        }
+    }
+
+    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", "1");
+    flexarray_append_pair(back, "type",
+                    (char *)libxl_usb_protocol_to_string(usbctrl->protocol));
+    flexarray_append_pair(back, "usb-ver", GCSPRINTF("%d", usbctrl->version));
+    flexarray_append_pair(back, "num-ports", GCSPRINTF("%d", usbctrl->ports));
+    flexarray_append_pair(front, "backend-id",
+                          GCSPRINTF("%d", usbctrl->backend_domid));
+    flexarray_append_pair(front, "state", "1");
+
+    if (aodev->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 (aodev->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);
+
+        path = GCSPRINTF("%s/port", libxl__device_backend_path(gc, device));
+        libxl__xs_mkdir(gc, t, path, NULL, 0);
+        for (i = 0; i < usbctrl->ports; i++) {
+            rc = libxl__xs_write_checked(gc, t,
+                                         GCSPRINTF("%s/%d", path, i + 1), "");
+            if (rc) goto out;
+        }
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc < 0) goto out;
+    }
+
+    aodev->dev = device;
+    aodev->action = LIBXL__DEVICE_ACTION_ADD;
+    libxl__wait_device_connection(egc, aodev);
+
+    rc = 0;
+
+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);
+    aodev->rc = rc;
+    if (rc) aodev->callback(egc, aodev);
+    return;
+}
+
+static int libxl_device_usbctrl_add_common(libxl_ctx *ctx,
+                                           uint32_t domid,
+                                           libxl_device_usbctrl *usbctrl,
+                                           const libxl_asyncop_how *ao_how,
+                                           bool update_json)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__ao_device *aodev;
+
+    GCNEW(aodev);
+    libxl__prepare_ao_device(ao, aodev);
+    aodev->action = LIBXL__DEVICE_ACTION_ADD;
+    aodev->callback = device_addrm_aocomplete;
+    aodev->update_json = update_json;
+    libxl__device_usbctrl_add(egc, domid, usbctrl, aodev);
+
+    return AO_INPROGRESS;
+}
+
+int libxl_device_usbctrl_add(libxl_ctx *ctx, uint32_t domid,
+                             libxl_device_usbctrl *usbctrl,
+                             const libxl_asyncop_how *ao_how)
+{
+    return libxl_device_usbctrl_add_common(ctx, domid, usbctrl, ao_how, 1);
+}
+
+static int
+libxl__device_usb_list_per_usbctrl(libxl__gc *gc, uint32_t domid, int usbctrl,
+                                   libxl_device_usb **usbs, int *num);
+
+static int
+do_usb_remove(libxl__gc *gc, uint32_t domid, libxl_device_usb *usb);
+
+static int
+libxl_device_usbctrl_remove_common(libxl_ctx *ctx, uint32_t domid,
+                                   libxl_device_usbctrl *usbctrl,
+                                   const libxl_asyncop_how *ao_how,
+                                   int force)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    libxl__device *device;
+    libxl__ao_device *aodev;
+    libxl_device_usbctrl *usbctrls = NULL;
+    libxl_device_usbctrl *usbctrl_find = NULL;
+    int numctrl = 0;
+    libxl_device_usb *usbs = NULL;
+    int numusb = 0;
+    int i, rc;
+
+    usbctrls = libxl_device_usbctrl_list(ctx, domid, &numctrl);
+    if (!numctrl) {
+        LOG(ERROR, "No USB controller exists in this domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    for (i = 0; i < numctrl; i++) {
+        if (usbctrl->devid == usbctrls[i].devid) {
+            usbctrl_find = usbctrls + i;
+            break;
+        }
+    }
+
+    if (!usbctrl_find) {
+        LOG(ERROR, "USB controller %d is not attached to this domain",
+            usbctrl->devid);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    GCNEW(device);
+    libxl__device_from_usbctrl(gc, domid, usbctrl_find, device);
+
+    /* Remove usb devices first */
+    rc  = libxl__device_usb_list_per_usbctrl(gc, domid, usbctrl->devid,
+                                             &usbs, &numusb);
+    if (rc) goto out;
+
+    for (i = 0; i < numusb; i++) {
+        if (do_usb_remove(gc, domid, &usbs[i])) {
+            LOG(ERROR, "do_usb_remove failed");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    /* Remove usbctrl */
+    GCNEW(aodev);
+    libxl__prepare_ao_device(ao, aodev);
+    aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
+    aodev->dev = device;
+    aodev->callback = device_addrm_aocomplete;
+    aodev->force = force;
+    libxl__initiate_device_remove(egc, aodev);
+
+out:
+    libxl_device_usbctrl_list_free(usbctrls, numctrl);
+    libxl_device_usb_list_free(usbs, numusb);
+    if (rc) return AO_ABORT(rc);
+    return AO_INPROGRESS;
+}
+
+int libxl_device_usbctrl_remove(libxl_ctx *ctx, uint32_t domid,
+                                libxl_device_usbctrl *usbctrl,
+                                const libxl_asyncop_how *ao_how)
+{
+    return libxl_device_usbctrl_remove_common(ctx, domid, usbctrl, ao_how, 0);
+}
+
+int libxl_device_usbctrl_destroy(libxl_ctx *ctx, uint32_t domid,
+                                 libxl_device_usbctrl *usbctrl,
+                                 const libxl_asyncop_how *ao_how)
+{
+    return libxl_device_usbctrl_remove_common(ctx, domid, usbctrl, ao_how, 1);
+}
+
+libxl_device_usbctrl *
+libxl_device_usbctrl_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usbctrl *usbctrls = NULL;
+    char *fe_path = NULL;
+    char **dir = NULL;
+    unsigned int ndirs = 0;
+
+    *num = 0;
+
+    fe_path = GCSPRINTF("%s/device/vusb",
+                        libxl__xs_get_dompath(gc, domid));
+    dir = libxl__xs_directory(gc, XBT_NULL, fe_path, &ndirs);
+
+    if (dir && ndirs) {
+        usbctrls = malloc(sizeof(*usbctrls) * ndirs);
+        libxl_device_usbctrl* usbctrl;
+        libxl_device_usbctrl* end = usbctrls + ndirs;
+        for (usbctrl = usbctrls; usbctrl < end; usbctrl++, dir++, (*num)++) {
+            char *tmp;
+            const char *be_path = libxl__xs_read(gc, XBT_NULL,
+                                    GCSPRINTF("%s/%s/backend", fe_path, *dir));
+
+            libxl_device_usbctrl_init(usbctrl);
+            usbctrl->devid = atoi(*dir);
+
+            tmp = libxl__xs_read(gc, XBT_NULL,
+                                 GCSPRINTF("%s/%s/backend-id", fe_path, *dir));
+            if (!tmp) goto outerr;
+            usbctrl->backend_domid = atoi(tmp);
+
+            tmp = libxl__xs_read(gc, XBT_NULL,
+                                 GCSPRINTF("%s/usb-ver", be_path));
+            if (!tmp) goto outerr;
+            usbctrl->version = atoi(tmp);
+
+            tmp = libxl__xs_read(gc, XBT_NULL,
+                                 GCSPRINTF("%s/num-ports", be_path));
+            if (!tmp) goto outerr;
+            usbctrl->ports = atoi(tmp);
+
+            tmp = libxl__xs_read(gc, XBT_NULL,
+                                 GCSPRINTF("%s/type", be_path));
+            if (!tmp ||
+                libxl_usb_protocol_from_string(tmp, &usbctrl->protocol))
+                goto outerr;
+        }
+    }
+
+    goto out;
+
+outerr:
+    LOG(ERROR, "Unable to list USB Controllers");
+    libxl_device_usbctrl_list_free(usbctrls, *num);
+    *num = 0;
+    usbctrls = NULL;
+
+out:
+    GC_FREE;
+    return usbctrls;
+}
+
+int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
+                                libxl_device_usbctrl *usbctrl,
+                                libxl_usbctrlinfo *usbctrlinfo)
+{
+    GC_INIT(ctx);
+    char *dompath, *usbctrlpath;
+    char *val;
+    int rc = 0;
+
+    usbctrlinfo->devid = usbctrl->devid;
+    usbctrlinfo->ports = usbctrl->ports;
+    usbctrlinfo->version = usbctrl->version;
+    usbctrlinfo->protocol = usbctrl->protocol;
+
+    dompath = libxl__xs_get_dompath(gc, domid);
+    usbctrlpath = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrlinfo->devid);
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/backend", usbctrlpath));
+    usbctrlinfo->backend = libxl__strdup(NOGC, val);
+    if (!usbctrlinfo->backend) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/backend-id", usbctrlpath));
+    usbctrlinfo->backend_id = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/state", usbctrlpath));
+    usbctrlinfo->state = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/event-channel", usbctrlpath));
+    usbctrlinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/urb-ring-ref", usbctrlpath));
+    usbctrlinfo->ref_urb = val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/conn-ring-ref", usbctrlpath));
+    usbctrlinfo->ref_conn= val ? strtoul(val, NULL, 10) : -1;
+
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/frontend", usbctrlinfo->backend));
+    usbctrlinfo->frontend = libxl__strdup(NOGC, val);
+
+    val = libxl__xs_read(gc, XBT_NULL,
+                         GCSPRINTF("%s/frontend-id", usbctrlinfo->backend));
+    usbctrlinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1;
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+static char *usb_busaddr_to_busid(libxl__gc *gc, int bus, int addr)
+{
+    libxl_ctx *ctx = CTX;
+    struct dirent *de;
+    DIR *dir;
+    char *busid = NULL;
+
+    assert((bus > 0) && (addr > 0));
+
+    if (!(dir = opendir(SYSFS_USB_DEV)))
+        return NULL;
+
+    while((de = readdir(dir))) {
+        char *filename;
+        void *buf;
+        int busnum = -1;
+        int devnum = -1;
+
+        if (!de->d_name)
+            continue;
+
+        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", de->d_name);
+        if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+            sscanf(buf, "%x", &devnum);
+
+        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", de->d_name);
+        if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+            sscanf(buf, "%x", &busnum);
+
+        if (bus == busnum && addr == devnum) {
+            busid = libxl__strdup(NOGC, de->d_name);
+            break;
+        }
+    }
+
+    closedir(dir);
+    return busid;
+}
+
+static void usb_busaddr_from_busid(libxl__gc *gc, char *busid,
+                                   int *bus, int *addr)
+{
+    libxl_ctx *ctx = CTX;
+    char *filename;
+    void *buf;
+
+    assert(busid);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", bus);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", addr);
+}
+
+static int
+libxl__device_usb_assigned_list(libxl__gc *gc,
+                                libxl_device_usb **list, int *num)
+{
+    char **domlist;
+    unsigned int nd = 0, i, j;
+    char *be_path;
+    libxl_device_usb *usb;
+
+    *list = NULL;
+    *num = 0;
+
+    domlist = libxl__xs_directory(gc, XBT_NULL, "/local/domain", &nd);
+    be_path = GCSPRINTF("/local/domain/%d/backend/vusb", LIBXL_TOOLSTACK_DOMID);
+    for (i = 0; i < nd; i++) {
+        char *path, *num_ports, **ctrl_list;
+        unsigned int nc = 0;
+        path = GCSPRINTF("%s/%s", be_path, domlist[i]);
+        ctrl_list = libxl__xs_directory(gc, XBT_NULL, path , &nc);
+
+        for (j = 0; j < nc; j++) {
+            path = GCSPRINTF("%s/%s/%s/num-ports", be_path,
+                             domlist[i], ctrl_list[j]);
+            num_ports = libxl__xs_read(gc, XBT_NULL, path);
+            if (num_ports) {
+                int nport = atoi(num_ports), k;
+                char *devpath, *busid;
+
+                for (k = 0; k < nport; k++) {
+                    devpath = GCSPRINTF("%s/%s/%s/port/%u", be_path,
+                                        domlist[i], ctrl_list[j], k + 1);
+                    busid = libxl__xs_read(gc, XBT_NULL, devpath);
+                    /* If there are USB device attached, add it to list */
+                    if (busid && strcmp(busid, "")) {
+                        GCREALLOC_ARRAY(*list, *num + 1);
+                        usb = *list + *num;
+                        usb->ctrl = atoi(ctrl_list[j]);
+                        usb->port = k + 1;
+                        usb->busid = libxl__strdup(gc, busid);
+                        usb_busaddr_from_busid(gc, usb->busid,
+                                               &usb->hostbus, &usb->hostaddr);
+                        (*num)++;
+                    }
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+static bool is_usb_in_array(libxl_device_usb *usbs, int num,
+                            libxl_device_usb *usb)
+{
+    int i;
+
+    for (i = 0; i < num; i++) {
+        if (COMPARE_USB(&usbs[i], usb))
+            return true;
+    }
+
+    return false;
+}
+
+/* check if USB device is already assigned to a domain */
+static bool is_usb_assigned(libxl__gc *gc, libxl_device_usb *usb)
+{
+    libxl_device_usb *usbs;
+    int rc, num;
+
+    rc = libxl__device_usb_assigned_list(gc, &usbs, &num);
+    if (rc) {
+        LOG(ERROR, "Fail to get assigned usb list");
+        return true;
+    }
+
+    return is_usb_in_array(usbs, num, usb);
+}
+
+/* check if USB device type is assignable */
+static bool is_usb_assignable(libxl__gc *gc, libxl_device_usb *usb)
+{
+    libxl_ctx *ctx = CTX;
+    int classcode;
+    char *filename;
+    void *buf = NULL;
+
+    assert(usb->busid);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", usb->busid);
+    if (libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        return false;
+
+    sscanf(buf, "%x", &classcode);
+    return classcode != USBHUB_CLASS_CODE;
+}
+
+/* get usb devices under certain usb controller */
+static int
+libxl__device_usb_list_per_usbctrl(libxl__gc *gc, uint32_t domid, int usbctrl,
+                                   libxl_device_usb **usbs, int *num)
+{
+    char *be_path, *num_devs;
+    int n, i;
+
+    *usbs = NULL;
+    *num = 0;
+
+    be_path = GCSPRINTF("%s/backend/vusb/%d/%d",
+                        libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                        domid, usbctrl);
+    num_devs = libxl__xs_read(gc, XBT_NULL,
+                              GCSPRINTF("%s/num-ports", be_path));
+    if (!num_devs)
+        return 0;
+
+    n = atoi(num_devs);
+    *usbs = libxl__calloc(NOGC, n, sizeof(libxl_device_usb));
+
+    for (i = 0; i < n; i++) {
+        char *busid;
+        libxl_device_usb *usb = NULL;
+
+        busid = libxl__xs_read(gc, XBT_NULL,
+                               GCSPRINTF("%s/port/%d", be_path, i + 1));
+        if (busid && strcmp(busid, "")) {
+            usb = *usbs + *num;
+            usb->ctrl = usbctrl;
+            usb->port = i + 1;
+            usb->busid = libxl__strdup(NOGC, busid);
+            usb_busaddr_from_busid(gc, usb->busid,
+                                   &usb->hostbus, &usb->hostaddr);
+            (*num)++;
+        }
+    }
+
+    return 0;
+}
+
+/* get all usb devices of the domain */
+static libxl_device_usb *
+libxl__device_usb_list(libxl__gc *gc, uint32_t domid, int *num)
+{
+    char **usbctrls;
+    unsigned int nd = 0, i, j;
+    char *be_path;
+    int rc;
+    libxl_device_usb *usbs = NULL;
+
+    *num = 0;
+
+    be_path = GCSPRINTF("/local/domain/%d/backend/vusb/%d",
+                        LIBXL_TOOLSTACK_DOMID, domid);
+    usbctrls = libxl__xs_directory(gc, XBT_NULL, be_path, &nd);
+
+    for (i = 0; i < nd; i++) {
+        int nc = 0;
+        libxl_device_usb *tmp = NULL;
+        rc = libxl__device_usb_list_per_usbctrl(gc, domid,
+                                                atoi(usbctrls[i]), &tmp, &nc);
+        if (!nc) continue;
+
+        usbs = libxl__realloc(NOGC, usbs, sizeof(*usbs) * (*num + nc));
+        for (j = 0; j < nc; j++) {
+            usbs[*num] = tmp[j];
+            usbs[*num].busid = libxl__strdup(NOGC, tmp[j].busid);
+            (*num)++;
+        }
+        libxl_device_usb_list_free(tmp, nc);
+    }
+    return usbs;
+}
+
+libxl_device_usb *
+libxl_device_usb_list(libxl_ctx *ctx, uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usb *usbs = NULL;
+
+    usbs = libxl__device_usb_list(gc, domid, num);
+
+    GC_FREE;
+    return usbs;
+}
+
+libxl_device_usb *
+libxl_device_usb_list_per_usbctrl(libxl_ctx *ctx, uint32_t domid,
+                                  int usbctrl, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usb *usbs = NULL;
+
+    libxl__device_usb_list_per_usbctrl(gc, domid, usbctrl, &usbs, num);
+
+    GC_FREE;
+    return usbs;
+}
+
+/* find first unused controller:port and give that to usb device */
+static int
+libxl__device_usb_set_default_usbctrl(libxl__gc *gc, uint32_t domid,
+                                      libxl_device_usb *usb)
+{
+    libxl_ctx *ctx = CTX;
+    libxl_device_usbctrl *usbctrls = NULL;
+    int numctrl = 0;
+    int i, j, rc = -1;
+    char *be_path, *tmp;
+
+    usbctrls = libxl_device_usbctrl_list(ctx, domid, &numctrl);
+    if (!numctrl)
+        goto out;
+
+    for (i = 0; i < numctrl; i++) {
+        for (j = 0; j < usbctrls[i].ports; j++) {
+            be_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, be_path);
+            if (tmp && !strcmp(tmp, "")) {
+                usb->ctrl = usbctrls[i].devid;
+                usb->port = j + 1;
+                rc = 0;
+                break;
+            }
+        }
+    }
+
+out:
+    libxl_device_usbctrl_list_free(usbctrls, numctrl);
+    return rc;
+}
+
+static int libxl__device_usb_setdefault(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_usb *usb,
+                                        bool update_json)
+{
+    char *be_path, *tmp;
+
+    if (usb->ctrl == -1) {
+        int ret = libxl__device_usb_set_default_usbctrl(gc, domid, usb);
+        /* If no existing ctrl to host this usb device, setup a new one */
+        if (ret) {
+            libxl_device_usbctrl usbctrl;
+            libxl_device_usbctrl_init(&usbctrl);
+            if (libxl_device_usbctrl_add_common(CTX, domid, &usbctrl,
+                                                0, update_json)) {
+                LOG(ERROR, "Failed to create usb controller");
+                return ERROR_INVAL;
+            }
+            usb->ctrl = usbctrl.devid;
+            usb->port = 1;
+            libxl_device_usbctrl_dispose(&usbctrl);
+        }
+    }
+
+    be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d",
+                        libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                        domid, usb->ctrl, usb->port);
+    tmp = libxl__xs_read(gc, XBT_NULL, be_path);
+    if (!tmp || strcmp(tmp, "")) {
+        LOG(ERROR, "The controller port isn't available");
+        return ERROR_INVAL;
+    }
+
+    return 0;
+}
+
+static int libxl__device_usb_add_xenstore(libxl__gc *gc, uint32_t domid,
+                                          libxl_device_usb *usb,
+                                          bool update_json)
+{
+    char *be_path;
+    int rc;
+    xs_transaction_t t = XBT_NULL;
+    libxl_domain_config d_config;
+    libxl_device_usb usb_saved;
+    libxl__domain_userdata_lock *lock = NULL;
+
+    libxl_domain_config_init(&d_config);
+    libxl_device_usb_init(&usb_saved);
+    libxl_device_usb_copy(CTX, &usb_saved, usb);
+
+    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(usb, usbs, domid, &usb_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, usb->ctrl, usb->port);
+
+        LOG(DEBUG, "Adding new usb device to xenstore");
+        if (libxl__xs_write_checked(gc, t, be_path, usb->busid)) {
+            rc = ERROR_FAIL;
+            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_usb_dispose(&usb_saved);
+    libxl_domain_config_dispose(&d_config);
+    return rc;
+}
+
+static int libxl__device_usb_remove_xenstore(libxl__gc *gc, uint32_t domid,
+                                             libxl_device_usb *usb)
+{
+    char *be_path;
+
+    be_path = GCSPRINTF("%s/backend/vusb/%d/%d/port/%d",
+                        libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                        domid, usb->ctrl, usb->port);
+    LOG(DEBUG, "Removing USB device from xenstore");
+    if (libxl__xs_write_checked(gc,XBT_NULL, be_path, ""))
+        return ERROR_FAIL;
+
+    return 0;
+}
+
+/* bind/unbind usb device interface */
+static int unbind_usb_intf(libxl__gc *gc, char *intf, char **drvpath)
+{
+    char *path, *spath, *dp = NULL;
+    int fd = -1;
+    int rc = 0;
+    struct stat st;
+
+    spath = GCSPRINTF(SYSFS_USB_DEV"/%s/driver", intf);
+    if (!lstat(spath, &st)) {
+        /* Find the canonical path to the driver. */
+        dp = libxl__zalloc(gc, PATH_MAX);
+        dp = realpath(spath, dp);
+
+        path = GCSPRINTF("%s/unbind", spath);
+        fd = open(path, O_WRONLY);
+        if (fd < 0)
+            return ERROR_FAIL;
+        rc = write(fd, intf, strlen(intf));
+        close(fd);
+        if (rc < 0)
+            return ERROR_FAIL;
+    }
+
+    if (drvpath)
+        *drvpath = dp;
+
+    return 0;
+}
+
+static int bind_usb_intf(libxl__gc *gc, char *intf, char *drvpath)
+{
+    char *path;
+    struct stat st;
+    int fd, rc = 0;
+
+    path = GCSPRINTF("%s/%s", drvpath, intf);
+    rc = lstat(path, &st);
+    /* already bind, return */
+    if (rc == 0)
+        return 0;
+
+    path = GCSPRINTF("%s/bind", drvpath);
+    fd = open(path, O_WRONLY);
+    if (fd < 0)
+        return ERROR_FAIL;
+
+    rc = write(fd, intf, strlen(intf));
+    close(fd);
+    if (rc < 0)
+        return ERROR_FAIL;
+
+    return 0;
+}
+
+/* Is usb interface bound to usbback? */
+static int usb_intf_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 usb_get_all_interfaces(libxl__gc *gc, libxl_device_usb *usb,
+                                  char ***intfs, int *num)
+{
+    DIR *dir;
+    struct dirent *entry;
+    char *buf;
+    int rc = 0;
+
+    *intfs = NULL;
+    *num = 0;
+
+    buf = GCSPRINTF("%s:", usb->busid);
+
+    if (!(dir = opendir(SYSFS_USB_DEV))) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    while ((entry = readdir(dir)) != NULL) {
+        if (!strncmp(entry->d_name, buf, strlen(buf))) {
+            GCREALLOC_ARRAY(*intfs, *num + 1);
+            if (*intfs == NULL) {
+                rc = ERROR_FAIL;
+                goto out;
+            }
+            (*intfs)[*num] = libxl__strdup(gc, entry->d_name);
+            (*num)++;
+        }
+    }
+
+    closedir(dir);
+
+out:
+    return rc;
+}
+
+/* Cann't write '.' or ':' into Xenstore as key. So, change '.' to '_',
+ * change ':' to '-'.
+ */
+static char *usb_interface_xenstore_encode(char *busid)
+{
+    char *str = strdup(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 the usb device, then check each interface, unbind from usbback
+ * driver and rebind to original driver
+ */
+static int usbback_dev_unassign(libxl__gc *gc, libxl_device_usb *usb)
+{
+    char **intfs = NULL;
+    char *path;
+    int num = 0, i;
+    int rc = 0;
+    char *usb_encode = NULL;
+
+    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0)
+        return ERROR_FAIL;
+
+    usb_encode = usb_interface_xenstore_encode(usb->busid);
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+        char *drvpath = NULL;
+
+        if (usb_intf_is_assigned(gc, intf) > 0) {
+            /* unbind interface from usbback driver */
+            if (unbind_usb_intf(gc, intf, NULL) < 0) {
+                rc = ERROR_FAIL;
+                goto out;
+            }
+        }
+
+        /* bind interface to its originial driver */
+        drvpath = libxl__xs_read(gc, XBT_NULL,
+                  GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path",
+                  usb_encode, usb_interface_xenstore_encode(intf)));
+        if (drvpath && bind_usb_intf(gc, intf, drvpath))
+            LOGE(WARN, "Couldn't bind %s to %s", intf, drvpath);
+    }
+
+    /* finally, remove xs driver path */
+    path = GCSPRINTF(USBBACK_INFO_PATH"/%s", usb_encode);
+    libxl__xs_rm_checked(gc, XBT_NULL, path);
+
+out:
+    free(usb_encode);
+    return rc;
+}
+
+/* bind usb device to "usbback" driver, if there are many interfaces
+ * under the usb device, check each interface, unbind from original
+ * driver and bind to usbback driver.
+ */
+static int usbback_dev_assign(libxl__gc *gc, libxl_device_usb *usb)
+{
+    char **intfs = NULL;
+    int num = 0, i;
+    int rc = 0;
+    char *usb_encode = NULL;
+
+    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0)
+        return ERROR_FAIL;
+
+    usb_encode = usb_interface_xenstore_encode(usb->busid);
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+        char *path = NULL;
+        char *drvpath = NULL;
+
+        /* already assigned to usbback */
+        if (usb_intf_is_assigned(gc, intf) > 0)
+            continue;
+
+        /* unbind interface from original driver */
+        if (unbind_usb_intf(gc, intf, &drvpath) < 0) {
+            rc = ERROR_FAIL;
+            goto out_rebind;
+        }
+
+        if (drvpath) {
+            /* write driver path to xenstore for later rebinding */
+            path = GCSPRINTF(USBBACK_INFO_PATH"/%s/%s/driver_path",
+                             usb_encode, usb_interface_xenstore_encode(intf));
+            if (libxl__xs_write_checked(gc, XBT_NULL, path, drvpath) < 0) {
+                LOG(WARN, "Write of %s to node %s failed", drvpath, path);
+            }
+        }
+
+        /* bind interface to usbback */
+        if (bind_usb_intf(gc, intf, SYSFS_USBBACK_DRIVER) < 0) {
+            LOGE(ERROR, "Couldn't bind %s to %s", intf, SYSFS_USBBACK_DRIVER);
+            rc = ERROR_FAIL;
+            goto out_rebind;
+        }
+    }
+
+    goto out;
+
+out_rebind:
+    /* some interfaces might be bound to usbback, unbind it then and
+     * rebind to its original driver
+     */
+    usbback_dev_unassign(gc, usb);
+out:
+    free(usb_encode);
+    return rc;
+}
+
+int libxl__device_usb_add(libxl__gc *gc, uint32_t domid,
+                          libxl_device_usb *usb,
+                          bool update_json)
+{
+    int rc;
+
+    assert(usb->busid || (usb->hostbus > 0 && usb->hostaddr > 0));
+
+    if (!usb->busid) {
+        usb->busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+        if (!usb->busid) {
+            LOG(ERROR, "USB device doesn't exist in sysfs");
+            return ERROR_INVAL;
+        }
+    }
+
+    if (!is_usb_assignable(gc, usb)) {
+        LOG(ERROR, "USB device is not assignable.");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* check usb device is already assigned */
+    if (is_usb_assigned(gc, usb)) {
+        LOG(ERROR, "USB device is already attached to a domain.");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = libxl__device_usb_setdefault(gc, domid, usb, update_json);
+    if (rc) goto out;
+
+    rc = libxl__device_usb_add_xenstore(gc, domid, usb, update_json);
+    if (rc) goto out;
+
+    rc = usbback_dev_assign(gc, usb);
+    if (rc)
+        libxl__device_usb_remove_xenstore(gc, domid, usb);
+
+out:
+    return rc;
+}
+
+int libxl_device_usb_add(libxl_ctx *ctx, uint32_t domid,
+                         libxl_device_usb *usb,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    rc = libxl__device_usb_add(gc, domid, usb, 1);
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+static int do_usb_remove(libxl__gc *gc, uint32_t domid,
+                         libxl_device_usb *usb)
+{
+    if (libxl__device_usb_remove_xenstore(gc, domid, usb))
+        return -1;
+
+    usbback_dev_unassign(gc, usb);
+
+    return 0;
+}
+
+static int libxl__device_usb_remove(libxl__gc *gc, uint32_t domid,
+                                    libxl_device_usb *usb)
+{
+    libxl_device_usb *usbs = NULL;
+    libxl_device_usb *usb_find = NULL;
+    int i, num = 0, rc;
+
+    assert(usb->busid || (usb->hostbus > 0 && usb->hostaddr > 0));
+
+    if (!usb->busid) {
+        usb->busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+        if (!usb->busid) {
+            LOG(ERROR, "USB device doesn't exist in sysfs");
+            return ERROR_INVAL;
+        }
+    }
+
+    usbs = libxl__device_usb_list(gc, domid, &num);
+    if (!usbs) {
+       LOG(ERROR, "No USB device attached to this domain");
+       return ERROR_FAIL;
+    }
+
+    for (i = 0; i < num; i++) {
+        if (COMPARE_USB(usb, &usbs[i])) {
+            usb_find = usbs + i;
+            break;
+        }
+    }
+
+    /* doesn't find the usb device in domain's usb device list*/
+    if (!usb_find) {
+        LOG(ERROR, "USB device %x.%x is not attached to this domain",
+            usb->hostbus, usb->hostaddr);
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    rc = do_usb_remove(gc, domid, usb_find);
+
+out:
+    libxl_device_usb_list_free(usbs, num);
+    return rc;
+}
+
+int libxl_device_usb_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_usb *usb,
+                            const libxl_asyncop_how *ao_how)
+
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    rc = libxl__device_usb_remove(gc, domid, usb);
+
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+int libxl__device_usb_destroy_all(libxl__gc *gc, uint32_t domid)
+{
+    libxl_ctx *ctx = CTX;
+    libxl_device_usbctrl *usbctrls = NULL;
+    int i, num = 0, rc = 0;
+
+    usbctrls = libxl_device_usbctrl_list(ctx, domid, &num);
+    if (!usbctrls)
+        return 0;
+
+    for (i = 0; i < num; i++) {
+        /* Force remove on shutdown since, on HVM, qemu will not always
+         * respond to SCI interrupt because the guest kernel has shut
+         * down the devices by the time we even get here!
+         */
+        if (libxl_device_usbctrl_remove_common(ctx, domid,
+                                               usbctrls + i, 0, 1) < 0) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+out:
+    libxl_device_usbctrl_list_free(usbctrls, num);
+    return rc;
+}
+
+int libxl_device_usb_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,
+                             libxl_usbinfo *usbinfo)
+{
+    GC_INIT(ctx);
+    char *filename;
+    void *buf = NULL;
+
+    usbinfo->ctrl = usb->ctrl;
+    usbinfo->port = usb->port;
+
+    if (usb->busid)
+        usbinfo->busid = libxl__strdup(NOGC, usb->busid);
+    else if (usb->hostbus > 0 && usb->hostaddr > 0)
+        usbinfo->busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+
+    assert(usbinfo->busid);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", usbinfo->busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->devnum);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", usbinfo->busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->busnum);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idVendor", usbinfo->busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->idVendor);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idProduct", usbinfo->busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->idProduct);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/manufacturer", usbinfo->busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) {
+        /* replace \n to \0 */
+        usbinfo->manuf = libxl__strdup(NOGC, buf);
+        if (usbinfo->manuf && strlen(usbinfo->manuf) > 0 &&
+            usbinfo->manuf[strlen(usbinfo->manuf) - 1] == '\n')
+            usbinfo->manuf[strlen(usbinfo->manuf) - 1] = '\0';
+    }
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/product", usbinfo->busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) {
+       /* replace \n to \0 */
+       usbinfo->prod = libxl__strdup(NOGC, buf);
+       if (usbinfo->prod && strlen(usbinfo->prod) > 0 &&
+           usbinfo->prod[strlen(usbinfo->prod) - 1] == '\n')
+           usbinfo->prod[strlen(usbinfo->prod) - 1] = '\0';
+    }
+
+    GC_FREE;
+    return 0;
+}
+/*
+ * 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 23f27d4..4561e1b 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -541,6 +541,29 @@ libxl_device_pci = Struct("device_pci", [
     ("seize", bool),
     ])
 
+libxl_usb_protocol = Enumeration("usb_protocol", [
+    (0, "AUTO"),
+    (1, "PV"),
+    (2, "QEMU"),
+    ])
+
+libxl_device_usbctrl = Struct("device_usbctrl", [
+    ("protocol", libxl_usb_protocol),
+    ("devid", libxl_devid),
+    ("version", integer),
+    ("ports", integer),
+    ("backend_domid", libxl_domid),
+    ("backend_domname", string),
+   ])
+
+libxl_device_usb = Struct("device_usb", [
+    ("ctrl", libxl_devid),
+    ("port", integer),
+    ("busid", string),
+    ("hostbus",   integer),
+    ("hostaddr",  integer),
+    ])
+
 libxl_device_dtdev = Struct("device_dtdev", [
     ("path", string),
     ])
@@ -572,6 +595,8 @@ libxl_domain_config = Struct("domain_config", [
     ("nics", Array(libxl_device_nic, "num_nics")),
     ("pcidevs", Array(libxl_device_pci, "num_pcidevs")),
     ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")),
+    ("usbctrls", Array(libxl_device_usbctrl, "num_usbctrls")),
+    ("usbs", Array(libxl_device_usb, "num_usbs")),
     ("vfbs", Array(libxl_device_vfb, "num_vfbs")),
     ("vkbs", Array(libxl_device_vkb, "num_vkbs")),
     ("vtpms", Array(libxl_device_vtpm, "num_vtpms")),
@@ -620,6 +645,33 @@ libxl_vtpminfo = Struct("vtpminfo", [
     ("uuid", libxl_uuid),
     ], dir=DIR_OUT)
 
+libxl_usbctrlinfo = Struct("usbctrlinfo", [
+    ("protocol", libxl_usb_protocol),
+    ("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_usbinfo = Struct("usbinfo", [
+    ("ctrl", libxl_devid),
+    ("port", integer),
+    ("busid", string),
+    ("busnum", integer),
+    ("devnum", integer),
+    ("idVendor", integer),
+    ("idProduct", integer),
+    ("prod", string),
+    ("manuf", string),
+    ], 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 d0d56c2..aca4bb9 100644
--- a/tools/libxl/libxl_utils.c
+++ b/tools/libxl/libxl_utils.c
@@ -1182,6 +1182,22 @@ 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_usb_list_free(libxl_device_usb* list, int nr)
+{
+   int i;
+   for (i = 0; i < nr; i++)
+      libxl_device_usb_dispose(&list[i]);
+   free(list);
+}
+
 /*
  * Local variables:
  * mode: C
-- 
2.1.4

  parent reply	other threads:[~2015-06-10  3:20 UTC|newest]

Thread overview: 88+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-10  3:20 [PATCH V4 0/7] xen pvusb toolstack work Chunyan Liu
2015-06-10  3:20 ` [PATCH V4 1/7] libxl: export some functions for pvusb use Chunyan Liu
2015-06-11 16:08   ` Ian Jackson
2015-06-11 16:28     ` Wei Liu
2015-06-12 15:14       ` Ian Jackson
2015-06-10  3:20 ` [PATCH V4 2/7] libxl_read_file_contents: add new entry to read sysfs file Chunyan Liu
2015-06-11 16:16   ` Ian Jackson
2015-06-12  7:00     ` Chun Yan Liu
2015-06-12 15:11       ` Ian Jackson
2015-06-10  3:20 ` Chunyan Liu [this message]
2015-06-11 15:00   ` [PATCH V4 3/7] libxl: add pvusb API Juergen Gross
2015-06-11 16:07     ` Ian Jackson
2015-06-11 16:42   ` Ian Jackson
2015-06-12  7:39     ` Chun Yan Liu
2015-06-12  8:06       ` Chun Yan Liu
2015-06-12 11:22       ` Ian Jackson
2015-06-15 14:17   ` George Dunlap
2015-06-15 14:25     ` Jürgen Groß
2015-06-15 14:34       ` George Dunlap
2015-06-15 18:26         ` Juergen Gross
2015-06-16 10:30           ` George Dunlap
2015-06-16 10:51             ` Juergen Gross
2015-06-16 11:11               ` George Dunlap
2015-06-16 11:19                 ` Juergen Gross
2015-06-16 11:23                   ` George Dunlap
2015-06-16 11:44                     ` Ian Jackson
2015-06-17 11:24                       ` Ian Campbell
2015-06-18 11:50                         ` George Dunlap
2015-06-18 12:08                         ` George Dunlap
2015-06-18 13:03                           ` Juergen Gross
2015-06-22 13:29                           ` Proposed plan for libxl USB interface (was Re: [PATCH V4 3/7] libxl: add pvusb API) George Dunlap
2015-06-22 14:14                             ` Juergen Gross
2015-06-22 14:22                             ` Ian Jackson
2015-06-23  2:42                             ` Chun Yan Liu
2015-06-23  2:43                             ` Chun Yan Liu
2015-06-23  2:44                             ` Chun Yan Liu
2015-06-16 10:41     ` [PATCH V4 3/7] libxl: add pvusb API Ian Jackson
2015-06-16 10:56       ` Jürgen Groß
2015-06-16 11:03         ` George Dunlap
2015-06-16 11:10         ` Ian Jackson
2015-06-16 11:25           ` Juergen Gross
2015-06-16 11:45             ` George Dunlap
2015-06-16 12:02               ` Ian Jackson
2015-06-16 13:19                 ` George Dunlap
2015-06-16 13:32                   ` Juergen Gross
2015-06-16 13:37                   ` [PATCH V4 3/7] libxl: add pvusb API [and 1 more messages] Ian Jackson
2015-06-16 14:41                     ` George Dunlap
2015-06-16 15:58                       ` Sander Eikelenboom
2015-06-16 15:59                       ` Ian Jackson
2015-06-16 16:34                         ` George Dunlap
2015-06-17  3:59                           ` Juergen Gross
2015-06-17 10:27                             ` George Dunlap
2015-06-18  6:24                               ` Chun Yan Liu
2015-06-16 11:45             ` [PATCH V4 3/7] libxl: add pvusb API Ian Jackson
2015-06-16 13:06               ` Juergen Gross
2015-06-16 13:09                 ` George Dunlap
2015-06-16 13:23                   ` Juergen Gross
2015-06-16 13:29                     ` George Dunlap
2015-06-16 13:49                       ` Juergen Gross
2015-06-16 14:06                         ` George Dunlap
2015-06-16 14:20                           ` Juergen Gross
2015-06-16 14:37                             ` George Dunlap
2015-06-17 11:34                             ` Ian Campbell
2015-06-17 11:40                               ` Juergen Gross
2015-06-18  6:20                               ` Chun Yan Liu
2015-06-18  7:02                                 ` Juergen Gross
2015-06-18  8:50                                   ` Ian Campbell
2015-06-18 13:02                                     ` Juergen Gross
2015-06-16 15:38             ` George Dunlap
2015-06-16 11:01       ` George Dunlap
2015-06-16 11:12         ` Ian Jackson
2015-06-16 11:21           ` George Dunlap
2015-06-16 16:32             ` Ian Jackson
2015-06-16 16:39               ` George Dunlap
2015-06-16 16:51                 ` Ross Philipson
2015-06-17  4:03                   ` Jürgen Groß
2015-06-17 13:48                     ` Ross Philipson
2015-06-15 17:47   ` George Dunlap
2015-06-23 10:18     ` Chun Yan Liu
2015-06-23 11:29       ` George Dunlap
2015-06-24  2:26         ` Chun Yan Liu
2015-06-10  3:20 ` [PATCH V4 4/7] libxl: add libxl_device_usb_assignable_list API Chunyan Liu
2015-06-10  3:20 ` [PATCH V4 5/7] xl: add pvusb commands Chunyan Liu
2015-06-12  7:37   ` Juergen Gross
2015-06-12  8:03     ` Chun Yan Liu
2015-06-12  8:22       ` Juergen Gross
2015-06-10  3:20 ` [PATCH V4 6/7] xl: add usb-assignable-list command Chunyan Liu
2015-06-10  3:20 ` [PATCH V4 7/7] domcreate: support pvusb in configuration file Chunyan Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1433906441-3280-4-git-send-email-cyliu@suse.com \
    --to=cyliu@suse.com \
    --cc=Ian.Jackson@eu.citrix.com \
    --cc=caobosimon@gmail.com \
    --cc=george.dunlap@eu.citrix.com \
    --cc=ian.campbell@citrix.com \
    --cc=wei.liu2@citrix.com \
    --cc=xen-devel@lists.xen.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.