xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V5 0/7] xen pvusb toolstack work
@ 2015-06-25 10:07 Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 1/7] libxl: export some functions for pvusb use Chunyan Liu
                   ` (7 more replies)
  0 siblings, 8 replies; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig

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 v4:
* use DEFINE_DEVICE_ADD and DEFINE_DEVICES_ADD to handle usbctrl adding
  and usb adding, define extended macro DEFINE_DEVICE_REMOVE_EXT to handle
  usbctrl remove.
* Change interfaces:
  libxl_device_usb only includes bus.addr, removing busid.
  'xl usb-detach' uses <ctrl,port> to specify usb device instead of bus.addr.
  Adjusting all related codes.
* Other changes addring all other comments in v4.

V3 is here:
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_read_file_contents: add new entry to read sysfs file
  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                |   75 ++
 docs/man/xl.pod.1                    |   40 +
 tools/libxl/Makefile                 |    2 +-
 tools/libxl/libxl.c                  |   57 +-
 tools/libxl/libxl.h                  |   67 ++
 tools/libxl/libxl_create.c           |   73 +-
 tools/libxl/libxl_device.c           |    8 +
 tools/libxl/libxl_internal.h         |   33 +-
 tools/libxl/libxl_osdeps.h           |   13 +
 tools/libxl/libxl_pvusb.c            | 1358 ++++++++++++++++++++++++++++++++++
 tools/libxl/libxl_types.idl          |   50 ++
 tools/libxl/libxl_types_internal.idl |    1 +
 tools/libxl/libxl_utils.c            |   53 +-
 tools/libxl/libxl_utils.h            |    5 +
 tools/libxl/xl.h                     |    6 +
 tools/libxl/xl_cmdimpl.c             |  401 +++++++++-
 tools/libxl/xl_cmdtable.c            |   29 +
 17 files changed, 2255 insertions(+), 16 deletions(-)
 create mode 100644 tools/libxl/libxl_pvusb.c

-- 
2.1.4

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

* [PATCH V5 1/7] libxl: export some functions for pvusb use
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
@ 2015-06-25 10:07 ` Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file Chunyan Liu
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 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>
---
Changes:
  * Not export device_addrm_aocomplete now. The only place that
    will call device_addrm_aocomplete in pvusb is
    libxl_device_usbctrl_remove/destroy, we'll define them in
    libxl.c too with an extended macro.

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

diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index d86ea62..6570476 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -1996,7 +1996,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;
@@ -2015,7 +2015,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,
+int libxl__resolve_domid(libxl__gc *gc, const char *name,
                                 uint32_t *domid)
 {
     if (!name)
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index e96d6b5..cdbe28b 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1083,6 +1083,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] 30+ messages in thread

* [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 1/7] libxl: export some functions for pvusb use Chunyan Liu
@ 2015-06-25 10:07 ` Chunyan Liu
  2015-06-25 11:09   ` Ian Jackson
  2015-06-25 10:07 ` [PATCH V5 3/7] libxl: add pvusb API Chunyan Liu
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig

Sysfs file has size=4096 but actual file content is less than that.
Current libxl_read_file_contents will treat it as error when file size
and actual file content differs, so reading sysfs file content with
this function always fails.

Add a new entry 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:
  - make libxl_read_file_contents as internal.
  - handle tolerant_shriking_file as Ian suggested

 tools/libxl/libxl_internal.h |  2 ++
 tools/libxl/libxl_utils.c    | 37 +++++++++++++++++++++++++++++--------
 2 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index cdbe28b..0965e08 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -3707,6 +3707,8 @@ static inline void libxl__update_config_vtpm(libxl__gc *gc,
 void libxl__bitmap_copy_best_effort(libxl__gc *gc, libxl_bitmap *dptr,
                                     const libxl_bitmap *sptr);
 #endif
+_hidden int libxl_read_sysfs_file_contents(libxl_ctx *ctx, const char *filename,
+                                   void **data_r, int *datalen_r);
 
 /*
  * Local variables:
diff --git a/tools/libxl/libxl_utils.c b/tools/libxl/libxl_utils.c
index f6be2d7..dc5465e 100644
--- a/tools/libxl/libxl_utils.c
+++ b/tools/libxl/libxl_utils.c
@@ -322,8 +322,10 @@ out:
     return rc;
 }
 
-int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
-                             void **data_r, int *datalen_r) {
+static int libxl_read_file_contents_core(libxl_ctx *ctx, const char *filename,
+                                         void **data_r, int *datalen_r,
+                                         bool tolerate_shrinking_file)
+{
     GC_INIT(ctx);
     FILE *f = 0;
     uint8_t *data = 0;
@@ -364,14 +366,20 @@ int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
 
         rs = fread(data, 1, datalen, f);
         if (rs != datalen) {
-            if (ferror(f))
+            if (ferror(f)) {
                 LOGE(ERROR, "failed to read %s", filename);
-            else if (feof(f))
-                LOG(ERROR, "%s changed size while we were reading it",
-		    filename);
-            else
+                goto xe;
+            } else if (feof(f)) {
+                if (rs < datalen && tolerate_shrinking_file) {
+                    datalen = rs;
+                } else {
+                    LOG(ERROR, "%s changed size while we were reading it",
+                        filename);
+                    goto xe;
+                }
+            } else {
                 abort();
-            goto xe;
+            }
         }
     }
 
@@ -396,6 +404,19 @@ int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
     return e;
 }
 
+int libxl_read_file_contents(libxl_ctx *ctx, const char *filename,
+                             void **data_r, int *datalen_r)
+{
+    return libxl_read_file_contents_core(ctx, filename, data_r, datalen_r, 0);
+}
+
+int libxl_read_sysfs_file_contents(libxl_ctx *ctx, const char *filename,
+                                   void **data_r, int *datalen_r)
+{
+    return libxl_read_file_contents_core(ctx, filename, data_r, datalen_r, 1);
+}
+
+
 #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] 30+ messages in thread

* [PATCH V5 3/7] libxl: add pvusb API
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 1/7] libxl: export some functions for pvusb use Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file Chunyan Liu
@ 2015-06-25 10:07 ` Chunyan Liu
  2015-06-25 11:15   ` Ian Jackson
                     ` (2 more replies)
  2015-06-25 10:07 ` [PATCH V5 4/7] libxl: add libxl_device_usb_assignable_list API Chunyan Liu
                   ` (4 subsequent siblings)
  7 siblings, 3 replies; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 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:
  - Use macros DEFINE_DEVICE_ADD and DEFINE_DEVICES_ADD for adding
    usbctrl and usb, update all related codes.
  - Use an extend macro DEFINE_DEVICE_REMOVE_EXT for removimg usbctrl,
    update all related codes.
  - Remove busid from libxl_device_usb definition, keep bus.addr only,
    update all related codes.
  - Remove documentation since it's mostly about design, move to
    cover-letter. Some parts are moved to code comments.
  - Address some other comments

 tools/libxl/Makefile                 |    2 +-
 tools/libxl/libxl.c                  |   53 ++
 tools/libxl/libxl.h                  |   64 ++
 tools/libxl/libxl_device.c           |    4 +
 tools/libxl/libxl_internal.h         |   20 +-
 tools/libxl/libxl_osdeps.h           |   13 +
 tools/libxl/libxl_pvusb.c            | 1305 ++++++++++++++++++++++++++++++++++
 tools/libxl/libxl_types.idl          |   50 ++
 tools/libxl/libxl_types_internal.idl |    1 +
 tools/libxl/libxl_utils.c            |   16 +
 tools/libxl/libxl_utils.h            |    5 +
 11 files changed, 1531 insertions(+), 2 deletions(-)
 create mode 100644 tools/libxl/libxl_pvusb.c

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 6570476..6542127 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -4233,11 +4233,54 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
 
 /******************************************************************************/
 
+/* Macro for defining device remove/destroy functions for usbctrl */
+/* Following functions are defined:
+ * libxl_device_usbctrl_remove
+ * libxl_device_usbctrl_destroy
+ */
+
+#define DEFINE_DEVICE_REMOVE_EXT(type, removedestroy, f)                \
+    int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \
+        uint32_t domid, libxl_device_##type *type,                      \
+        const libxl_asyncop_how *ao_how)                                \
+    {                                                                   \
+        AO_CREATE(ctx, domid, ao_how);                                  \
+        libxl__device *device;                                          \
+        libxl__ao_device *aodev;                                        \
+        int rc;                                                         \
+                                                                        \
+        GCNEW(device);                                                  \
+        rc = libxl__device_from_##type(gc, domid, type, device);        \
+        if (rc != 0) goto out;                                          \
+                                                                        \
+        GCNEW(aodev);                                                   \
+        libxl__prepare_ao_device(ao, aodev);                            \
+        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;                    \
+        aodev->dev = device;                                            \
+        aodev->callback = device_addrm_aocomplete;                      \
+        aodev->force = f;                                               \
+        libxl__initiate_device_##type##_remove(egc, aodev);             \
+                                                                        \
+    out:                                                                \
+        if (rc) return AO_ABORT(rc);                                    \
+        return AO_INPROGRESS;                                           \
+    }
+
+
+DEFINE_DEVICE_REMOVE_EXT(usbctrl, remove, 0)
+DEFINE_DEVICE_REMOVE_EXT(usbctrl, destroy, 1)
+
+#undef DEFINE_DEVICE_REMOVE_EXT
+
+/******************************************************************************/
+
 /* Macro for defining device addition functions in a compact way */
 /* The following functions are defined:
  * libxl_device_disk_add
  * libxl_device_nic_add
  * libxl_device_vtpm_add
+ * libxl_device_usbctrl_add
+ * libxl_device_usb_add
  */
 
 #define DEFINE_DEVICE_ADD(type)                                         \
@@ -4269,6 +4312,12 @@ DEFINE_DEVICE_ADD(nic)
 /* vtpm */
 DEFINE_DEVICE_ADD(vtpm)
 
+/* usbctrl */
+DEFINE_DEVICE_ADD(usbctrl)
+
+/* usb */
+DEFINE_DEVICE_ADD(usb)
+
 #undef DEFINE_DEVICE_ADD
 
 /******************************************************************************/
@@ -6770,6 +6819,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..6669835 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,53 @@ 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);
+
+/* 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,
+                                  libxl_devid 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);
+
 /* 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 93bb41e..9869a21 100644
--- a/tools/libxl/libxl_device.c
+++ b/tools/libxl/libxl_device.c
@@ -676,6 +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;
+                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) {
+                    libxl__initiate_device_usbctrl_remove(egc, aodev);
+                    continue;
+                }
                 libxl__initiate_device_remove(egc, aodev);
             }
         }
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 0965e08..6a8d4ee 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -2438,6 +2438,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_usb_add(libxl__egc *egc, uint32_t domid,
+                                   libxl_device_usb *usb,
+                                   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);
@@ -2470,6 +2478,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.
@@ -3645,7 +3660,10 @@ 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)->hostbus == (b)->hostbus &&  \
+                           (a)->hostaddr == (b)->hostaddr)
+#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..d8cacd4
--- /dev/null
+++ b/tools/libxl/libxl_pvusb.c
@@ -0,0 +1,1305 @@
+/*
+ * 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
+
+/* Utility to read backend xenstore keys */
+#define READ_BACKEND(tgc, subpath)                                    \
+            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath, be_path))
+
+/* Utility to read frontend xenstore keys */
+#define READ_FRONTEND(tgc, subpath)                                   \
+            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath, fe_path))
+
+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;
+}
+
+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", "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(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", "1");
+
+    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.
+ */
+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;
+        }
+    }
+
+    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);
+
+    rc = 0;
+
+out:
+    aodev->rc = rc;
+    if (rc) aodev->callback(egc, aodev);
+    return;
+}
+
+static int
+libxl__device_usb_list_per_usbctrl(libxl__gc *gc, uint32_t domid,
+                                   libxl_devid usbctrl,
+                                   libxl_device_usb **usbs, int *num);
+
+static int
+libxl__device_usb_remove(libxl__gc *gc, uint32_t domid, libxl_device_usb *usb);
+
+int libxl_devid_to_device_usbctrl(libxl_ctx *ctx,
+                                  uint32_t domid,
+                                  int devid,
+                                  libxl_device_usbctrl *usbctrl)
+{
+    GC_INIT(ctx);
+    libxl_device_usbctrl *usbctrls;
+    int nb = 0;
+    int i, rc = -1;
+
+    usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb);
+    if (!nb) goto out;
+
+    libxl_device_usbctrl_init(usbctrl);
+    for (i = 0; i < nb; i++) {
+        if (devid == usbctrls[i].devid) {
+            *usbctrl = usbctrls[i];
+            rc = 0;
+            break;
+        }
+    }
+
+    libxl_device_usbctrl_list_free(usbctrls, nb);
+
+out:
+    GC_FREE;
+    return rc;
+}
+
+/* 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
+ */
+void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,
+                                           libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    libxl_device_usb *usbs = NULL;
+    int numusb = 0;
+    int i, rc;
+    uint32_t domid = ao->domid;
+    int usbctrl_devid = aodev->dev->devid;
+
+    /* 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 (libxl__device_usb_remove(gc, domid, &usbs[i])) {
+            LOG(ERROR, "libxl__device_usb_remove failed");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+    }
+
+    libxl_device_usb_list_free(usbs, numusb);
+
+    /* Remove usbctrl */
+    return libxl__initiate_device_remove(egc, aodev);
+
+out:
+    libxl_device_usb_list_free(usbs, numusb);
+    aodev->rc = rc;
+    if (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 **dir = NULL;
+    unsigned int ndirs = 0;
+
+    *num = 0;
+
+    path = GCSPRINTF("%s/device/vusb",
+                     libxl__xs_get_dompath(gc, domid));
+    dir = libxl__xs_directory(gc, XBT_NULL, path, &ndirs);
+
+    if (dir && ndirs) {
+        usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * ndirs);
+        libxl_device_usbctrl* usbctrl;
+        libxl_device_usbctrl* end = usbctrls + ndirs;
+        for (usbctrl = usbctrls; usbctrl < end; usbctrl++, dir++, (*num)++) {
+            const char *tmp, *be_path;
+            const char *fe_path = GCSPRINTF("%s/%s", path, *dir);
+
+            libxl_device_usbctrl_init(usbctrl);
+            usbctrl->devid = atoi(*dir);
+
+            be_path = READ_FRONTEND(gc, "backend");
+            if (!be_path) goto outerr;
+
+            tmp = READ_FRONTEND(gc, "backend-id");
+            if (!tmp) goto outerr;
+            usbctrl->backend_domid = atoi(tmp);
+
+            tmp = READ_BACKEND(gc, "usb-ver");
+            if (!tmp) goto outerr;
+            usbctrl->version = atoi(tmp);
+
+            tmp = READ_BACKEND(gc, "num-ports");
+            if (!tmp) goto outerr;
+            usbctrl->ports = atoi(tmp);
+
+            tmp = READ_BACKEND(gc, "type");
+            if (!tmp) goto outerr;
+            libxl_usb_protocol_from_string(tmp, &usbctrl->protocol);
+        }
+    }
+
+    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;
+    const char *fe_path, *be_path, *tmp;
+    int rc = 0;
+
+    usbctrlinfo->devid = usbctrl->devid;
+
+    dompath = libxl__xs_get_dompath(gc, domid);
+    fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrlinfo->devid);
+    be_path = READ_FRONTEND(gc, "backend");
+    if (!be_path) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+    usbctrlinfo->backend = libxl__strdup(NOGC, be_path);
+
+    tmp = READ_FRONTEND(gc, "backend-id");
+    usbctrlinfo->backend_id = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_FRONTEND(gc, "state");
+    usbctrlinfo->state = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_FRONTEND(gc, "event-channel");
+    usbctrlinfo->evtch = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_FRONTEND(gc, "urb-ring-ref");
+    usbctrlinfo->ref_urb = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_FRONTEND(gc, "conn-ring-ref");
+    usbctrlinfo->ref_conn= tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_BACKEND(gc, "frontend");
+    usbctrlinfo->frontend = libxl__strdup(NOGC, tmp);
+
+    tmp = READ_BACKEND(gc, "frontend-id");
+    usbctrlinfo->frontend_id = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_BACKEND(gc, "num-ports");
+    usbctrlinfo->ports = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_BACKEND(gc, "usb-ver");
+    usbctrlinfo->version = tmp ? strtoul(tmp, NULL, 10) : -1;
+
+    tmp = READ_BACKEND(gc, "type");
+    libxl_usb_protocol_from_string(tmp, &usbctrlinfo->protocol);
+
+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/busnum", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", bus);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", 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_busaddr_from_busid(gc, 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;
+    char *busid = NULL;
+
+    assert(usb->hostbus > 0 && usb->hostaddr > 0);
+    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", 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,
+                                   libxl_devid 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_busaddr_from_busid(gc, 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];
+            (*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,
+                                  libxl_devid 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;
+}
+
+/* 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_usb_setdefault(libxl__gc *gc, uint32_t domid,
+                                        libxl_device_usb *usb,
+                                        bool update_json)
+{
+    int rc = -1;
+    char *be_path, *tmp;
+
+    if (usb->ctrl == -1) {
+        if (usb->port) {
+            LOG(ERROR, "USB controller must be specified if you specify port ID");
+            return ERROR_INVAL;
+        }
+
+        rc = libxl__device_usb_set_default_usbctrl(gc, domid, usb);
+        /* 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) {
+                    goto out;
+                }
+            }
+
+            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,
+                                                    update_json);
+            if (rc) goto out;
+
+            usb->ctrl = usbctrl->devid;
+            usb->port = 1;
+        }
+    } else if (!usb->port) {
+        /* Valid port starts from 1. Choose port for us. */
+        int i, ports;
+
+        be_path = GCSPRINTF("%s/backend/vusb/%d/%d",
+                            libxl__xs_get_dompath(gc, LIBXL_TOOLSTACK_DOMID),
+                            domid, usb->ctrl);
+        tmp = READ_BACKEND(gc, "num-ports");
+        ports = tmp ? atoi(tmp) : 0;
+        for (i = 0; i < ports; i++) {
+            tmp = libxl__xs_read(gc, XBT_NULL,
+                                 GCSPRINTF("%s/port/%d", be_path, i + 1));
+            if (tmp && !strcmp(tmp, "")) {
+                usb->port = i + 1;
+                break;
+            }
+        }
+
+        if (!usb->port) {
+            LOG(ERROR, "No available port under specified controller");
+            goto out;
+        }
+    } else {
+        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");
+            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_usb_add_xenstore(libxl__gc *gc, uint32_t domid,
+                                          libxl_device_usb *usb,
+                                          bool update_json)
+{
+    char *be_path;
+    char *busid;
+    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);
+
+    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->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(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, 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_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;
+    char *busid;
+    int rc = 0;
+
+    *intfs = NULL;
+    *num = 0;
+
+    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+    if (!busid) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    buf = GCSPRINTF("%s:", 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;
+}
+
+/* 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(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 USB device, check each interface,
+ * unbind from "usbback" driver and rebind to its 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 *busid;
+    char *usb_encode = NULL;
+
+    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0)
+        return ERROR_FAIL;
+
+    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+    usb_encode = usb_interface_xenstore_encode(busid);
+
+    for (i = 0; i < num; i++) {
+        char *intf = intfs[i];
+        char *drvpath = NULL;
+
+        /* check if the USB interface is already bound to "usbbcak" */
+        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 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 *busid;
+    char *usb_encode = NULL;
+
+    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0)
+        return ERROR_FAIL;
+
+    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+    usb_encode = usb_interface_xenstore_encode(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;
+}
+
+/* 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.
+ */
+void libxl__device_usb_add(libxl__egc *egc, uint32_t domid,
+                           libxl_device_usb *usb,
+                           libxl__ao_device *aodev)
+{
+    STATE_AO_GC(aodev->ao);
+    int rc = -1;
+    char *busid = NULL;
+
+    assert(usb->hostbus > 0 && usb->hostaddr > 0);
+
+    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+    if (!busid) {
+        LOG(ERROR, "USB device doesn't exist in sysfs");
+        goto out;
+    }
+
+    if (!is_usb_assignable(gc, usb)) {
+        LOG(ERROR, "USB device is not assignable.");
+        goto out;
+    }
+
+    /* check usb device is already assigned */
+    if (is_usb_assigned(gc, usb)) {
+        LOG(ERROR, "USB device is already attached to a domain.");
+        goto out;
+    }
+
+    rc = libxl__device_usb_setdefault(gc, domid, usb, aodev->update_json);
+    if (rc) goto out;
+
+    rc = libxl__device_usb_add_xenstore(gc, domid, usb, aodev->update_json);
+    if (rc) goto out;
+
+    rc = usbback_dev_assign(gc, usb);
+    if (rc) {
+        libxl__device_usb_remove_xenstore(gc, domid, usb);
+        goto out;
+    }
+
+    libxl__ao_complete(egc, ao, 0);
+    rc = 0;
+
+out:
+    aodev->rc = rc;
+    if (rc) aodev->callback(egc, aodev);
+    return;
+}
+
+int libxl_ctrlport_to_device_usb(libxl_ctx *ctx,
+                                 uint32_t domid,
+                                 int ctrl,
+                                 int port,
+                                 libxl_device_usb *usb)
+{
+    GC_INIT(ctx);
+    char *dompath, *be_path, *busid;
+    int rc = ERROR_FAIL;
+
+    dompath = libxl__xs_get_dompath(gc, domid);
+    if (!dompath)
+        goto out;
+
+    be_path = libxl__xs_read(gc, XBT_NULL,
+                  GCSPRINTF("%s/device/vusb/%d/backend", dompath, ctrl));
+    if (!be_path)
+        goto out;
+
+    busid = libxl__xs_read(gc, XBT_NULL,
+                           GCSPRINTF("%s/port/%d", be_path, port));
+    if (busid && strcmp(busid, "")) {
+        usb->ctrl = ctrl;
+        usb->port = port;
+        usb_busaddr_from_busid(gc, busid, &usb->hostbus, &usb->hostaddr);
+        rc = 0;
+    }
+
+out:
+    GC_FREE;
+    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_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;
+}
+
+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_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,
+                             libxl_usbinfo *usbinfo)
+{
+    GC_INIT(ctx);
+    char *filename;
+    char *busid;
+    void *buf = NULL;
+    int buflen, rc;
+
+    usbinfo->ctrl = usb->ctrl;
+    usbinfo->port = usb->port;
+
+    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
+    if (!busid) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->devnum);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->busnum);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idVendor", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->idVendor);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idProduct", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
+        sscanf(buf, "%x", &usbinfo->idProduct);
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/manufacturer", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) &&
+        buflen > 0) {
+        /* replace \n to \0 */
+        if (((char *)buf)[buflen - 1] == '\n')
+            ((char *)buf)[buflen - 1] = '\0';
+        usbinfo->manuf = libxl__strdup(NOGC, buf);
+   }
+
+    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/product", busid);
+    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) &&
+        buflen > 0) {
+        /* replace \n to \0 */
+        if (((char *)buf)[buflen - 1] == '\n')
+            ((char *)buf)[buflen - 1] = '\0';
+        usbinfo->prod = libxl__strdup(NOGC, buf);
+    }
+
+    rc = 0;
+
+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 23f27d4..5278d03 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -541,6 +541,28 @@ 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),
+    ("hostbus",   integer),
+    ("hostaddr",  integer),
+    ])
+
 libxl_device_dtdev = Struct("device_dtdev", [
     ("path", string),
     ])
@@ -572,6 +594,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 +644,32 @@ 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),
+    ("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 dc5465e..a627662 100644
--- a/tools/libxl/libxl_utils.c
+++ b/tools/libxl/libxl_utils.c
@@ -1187,6 +1187,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
diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h
index 1c1761d..321df9d 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_usb(libxl_ctx *ctx, uint32_t domid,
+                                 int ctrl, int port,
+                                 libxl_device_usb *usb);
 
 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] 30+ messages in thread

* [PATCH V5 4/7] libxl: add libxl_device_usb_assignable_list API
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (2 preceding siblings ...)
  2015-06-25 10:07 ` [PATCH V5 3/7] libxl: add pvusb API Chunyan Liu
@ 2015-06-25 10:07 ` Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 5/7] xl: add pvusb commands Chunyan Liu
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 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.

 tools/libxl/libxl.h       |  3 +++
 tools/libxl/libxl_pvusb.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+)

diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 6669835..a52748b 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -1313,6 +1313,9 @@ int libxl_device_usbctrl_getinfo(libxl_ctx *ctx, uint32_t domid,
                                 libxl_usbctrlinfo *usbctrlinfo);
 
 /* USB Devices */
+libxl_device_usb *
+libxl_device_usb_assignable_list(libxl_ctx *ctx, int *num);
+
 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;
diff --git a/tools/libxl/libxl_pvusb.c b/tools/libxl/libxl_pvusb.c
index d8cacd4..215ed05 100644
--- a/tools/libxl/libxl_pvusb.c
+++ b/tools/libxl/libxl_pvusb.c
@@ -551,6 +551,59 @@ static bool is_usb_assignable(libxl__gc *gc, libxl_device_usb *usb)
     return classcode != USBHUB_CLASS_CODE;
 }
 
+libxl_device_usb *
+libxl_device_usb_assignable_list(libxl_ctx *ctx, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usb *usbs = NULL;
+    libxl_device_usb *assigned;
+    int num_assigned;
+    struct dirent *de;
+    DIR *dir;
+
+    *num = 0;
+
+    if (libxl__device_usb_assigned_list(gc, &assigned, &num_assigned) < 0)
+        goto out;
+
+    if (!(dir = opendir(SYSFS_USB_DEV)))
+        goto out;
+
+    while ((de = readdir(dir))) {
+        libxl_device_usb *usb;
+        int bus = -1, addr = -1;
+
+        if (!de->d_name)
+            continue;
+
+        usb_busaddr_from_busid(gc, de->d_name, &bus, &addr);
+        if (bus < 1 || addr < 1)
+            continue;
+
+        GCNEW(usb);
+        usb->hostbus = bus;
+        usb->hostaddr = addr;
+
+        if (!is_usb_assignable(gc, usb))
+            continue;
+
+        if (is_usb_in_array(assigned, num_assigned, usb))
+            continue;
+
+        usbs = libxl__realloc(NOGC, usbs, sizeof(*usbs) * (*num + 1));
+        libxl_device_usb_init(usbs + *num);
+        usbs[*num].hostbus = bus;
+        usbs[*num].hostaddr = addr;
+        (*num)++;
+    }
+
+    closedir(dir);
+
+out:
+    GC_FREE;
+    return usbs;
+}
+
 /* get usb devices under certain usb controller */
 static int
 libxl__device_usb_list_per_usbctrl(libxl__gc *gc, uint32_t domid,
-- 
2.1.4

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

* [PATCH V5 5/7] xl: add pvusb commands
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (3 preceding siblings ...)
  2015-06-25 10:07 ` [PATCH V5 4/7] libxl: add libxl_device_usb_assignable_list API Chunyan Liu
@ 2015-06-25 10:07 ` Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 6/7] xl: add usb-assignable-list command Chunyan Liu
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig, Simon Cao

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

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

 #xl usb-ctrl-attach test_vm version=1 num_ports=8

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

 #xl usb-attach test_vm 1.6
 will find the first usable controller:port, and attach usb
 device whose bus address is 1.6 (busnum is 1, devnum is 6)
 to it. One could also specify which <controller> and which <port>.

 #xl usb-detach test_vm 0 1
 will remove usb device at controller '0' (devid), port '1'.

 #xl usb-ctrl-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:
  - change usb-detach interface from using bus.addr to using
    <ctrl,port> to specify the USB device.

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

diff --git a/docs/man/xl.pod.1 b/docs/man/xl.pod.1
index 4eb929d..83b7680 100644
--- a/docs/man/xl.pod.1
+++ b/docs/man/xl.pod.1
@@ -1387,6 +1387,46 @@ List pass-through pci devices for a domain.
 
 =back
 
+=head1 USB PASS-THROUGH
+
+=over 4
+
+=item B<usb-ctrl-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 support 'pv'.
+B<version=val> is the usb controller version, could be 1 (USB1.1) or 2 (USB2.0).
+B<ports=number> is the total ports of the usb controller.
+By default, it will create a USB2.0 controller with 8 ports.
+
+=item B<usb-ctrl-detach> I<domain-id> I<devid>
+
+Destroy a USB controller from the specified domain.
+B<devid> is devid of the USB controller.
+
+If B<-f> is specified, B<xl> is going to forcefully remove the device even
+without guest's collaboration.
+
+=item B<usb-attach> I<domain-id> I<bus.addr> [I<controller=devid> [I<port=number>]]
+
+Hot-plug a new pass-through USB device to the specified domain.
+B<bus.addr> is the busnum.devnum 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, it will create one.
+
+=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 5bc138c..2d57ee3 100644
--- a/tools/libxl/xl.h
+++ b/tools/libxl/xl.h
@@ -86,6 +86,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_usbattach(int argc, char **argv);
+int main_usbdetach(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 c858068..82a92a1 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -3219,6 +3219,270 @@ int main_cd_insert(int argc, char **argv)
     return 0;
 }
 
+static void usbinfo_print(libxl_device_usb *usb)
+{
+    libxl_usbinfo usbinfo;
+
+    if (usb == NULL)
+         return;
+
+    libxl_usbinfo_init(&usbinfo);
+    if (!libxl_device_usb_getinfo(ctx, usb, &usbinfo)) {
+        printf(" Bus %03x Device %03x: ID %04x:%04x %s %s\n",
+                usbinfo.busnum, usbinfo.devnum,
+                usbinfo.idVendor, usbinfo.idProduct,
+                usbinfo.manuf ?: "", usbinfo.prod ?: "");
+    }
+    libxl_usbinfo_dispose(&usbinfo);
+}
+
+int main_usbctrl_attach(int argc, char **argv)
+{
+    uint32_t domid;
+    int opt, rc = 1;
+    char *oparg;
+    libxl_device_usbctrl usbctrl;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usb-ctrl-attach", 1) {
+        /* No options */
+    }
+
+    domid = find_domain(argv[optind++]);
+
+    libxl_device_usbctrl_init(&usbctrl);
+
+    while (argc > optind) {
+        if (MATCH_OPTION("type", argv[optind], oparg)) {
+            if (!strcmp(oparg, "pv")) {
+                usbctrl.protocol = LIBXL_USB_PROTOCOL_PV;
+            } else {
+                fprintf(stderr, "unsupported type `%s'\n", oparg);
+                goto out;
+            }
+        } else if (MATCH_OPTION("version", argv[optind], oparg)) {
+            usbctrl.version = atoi(oparg);
+            if (usbctrl.version != 1 && usbctrl.version != 2) {
+                fprintf(stderr, "unsupported version `%s'\n", oparg);
+                goto out;
+            }
+        } else if (MATCH_OPTION("ports", argv[optind], oparg)) {
+            usbctrl.ports = atoi(oparg);
+            if (usbctrl.ports < 1 || usbctrl.ports > 31) {
+                fprintf(stderr, "unsupported ports `%s'\n", oparg);
+                goto out;
+            }
+        } else {
+            fprintf(stderr, "unrecognized argument `%s'\n", argv[optind]);
+            goto out;
+        }
+        optind++;
+    }
+
+    if (dryrun_only) {
+       char* json = libxl_device_usbctrl_to_json(ctx, &usbctrl);
+       printf("usb controller: %s\n", json);
+       free(json);
+       libxl_device_usbctrl_dispose(&usbctrl);
+       if (ferror(stdout) || fflush(stdout)) {
+           perror("stdout");
+           exit(-1);
+       }
+       return 0;
+    }
+
+    if (libxl_device_usbctrl_add(ctx, domid, &usbctrl, 0)) {
+        fprintf(stderr, "libxl_device_usbctrl_add failed.\n");
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    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, "usb-ctrl-detach", 2) {
+        /* No options */
+    }
+
+    domid = find_domain(argv[optind]);
+    devid = atoi(argv[optind+1]);
+    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");
+
+    libxl_device_usbctrl_dispose(&usbctrl);
+    return rc;
+
+}
+
+int main_usbattach(int argc, char **argv)
+{
+    uint32_t domid;
+    char *devname, *p;
+    int opt, rc = 1;
+    char *oparg;
+    libxl_device_usb usb;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usb-attach", 2) {
+        /* No options */
+    }
+
+    libxl_device_usb_init(&usb);
+
+    domid = find_domain(argv[optind++]);
+    devname = argv[optind++];
+    p = strchr(devname, '.');
+    if (p) {
+        usb.hostbus = strtoul(devname, NULL, 0);
+        usb.hostaddr = strtoul(p + 1, NULL, 0);
+    }
+
+    if (usb.hostbus < 1 || usb.hostaddr < 1) {
+        fprintf(stderr, "Invalid usb device.\n");
+        goto out;
+    }
+
+    while (argc > optind) {
+        if (MATCH_OPTION("controller", argv[optind], oparg)) {
+            usb.ctrl = atoi(oparg);
+        } else if (MATCH_OPTION("port", argv[optind], oparg)) {
+            usb.port = atoi(oparg);
+        } else {
+            fprintf(stderr, "unrecognized argument `%s'\n", argv[optind]);
+            goto out;
+        }
+        optind++;
+    }
+
+    if (dryrun_only) {
+        char *json = libxl_device_usb_to_json(ctx, &usb);
+        printf("usb: %s\n", json);
+        free(json);
+        libxl_device_usb_dispose(&usb);
+        if (ferror(stdout) || fflush(stdout)) {
+            perror("stdout");
+            exit(-1);
+        }
+        return 0;
+    }
+
+    if (libxl_device_usb_add(ctx, domid, &usb, 0)) {
+        fprintf(stderr, "libxl_device_usb_add failed.\n");
+        goto out;
+    }
+
+    rc = 0;
+
+out:
+    libxl_device_usb_dispose(&usb);
+    return rc;
+}
+
+int main_usbdetach(int argc, char **argv)
+{
+    uint32_t domid;
+    int ctrl, port;
+    int opt, rc = 1;
+    libxl_device_usb usb;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usb-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");
+        goto out;
+    }
+
+    libxl_device_usb_init(&usb);
+    if (libxl_ctrlport_to_device_usb(ctx, domid, ctrl, port, &usb)) {
+        fprintf(stderr, "Unknown device at controller %d port %d.\n",
+                ctrl, port);
+        goto out;
+    }
+
+    rc = libxl_device_usb_remove(ctx, domid, &usb, 0);
+    if (rc)
+        fprintf(stderr, "libxl_device_usb_remove failed.\n");
+
+out:
+    libxl_device_usb_dispose(&usb);
+    return rc;
+}
+
+int main_usblist(int argc, char **argv)
+{
+    uint32_t domid;
+    libxl_device_usbctrl *usbctrls;
+    libxl_usbctrlinfo usbctrlinfo;
+    libxl_device_usb *usbs;
+    int numctrl, numusb, 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_usb_protocol_to_string(usbctrlinfo.protocol),
+                    usbctrlinfo.backend_id, usbctrlinfo.state,
+                    usbctrlinfo.version, usbctrlinfo.ports,
+                    usbctrlinfo.backend);
+
+            usbs = libxl_device_usb_list_per_usbctrl(ctx, domid,
+                                                     usbctrlinfo.devid, &numusb);
+            for (j = 0; j < numusb; j++) {
+                printf("  Port %d:", usbs[j].port);
+                usbinfo_print(&usbs[j]);
+            }
+
+            libxl_device_usb_list_free(usbs, numusb);
+        }
+
+        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 7f4759b..0627077 100644
--- a/tools/libxl/xl_cmdtable.c
+++ b/tools/libxl/xl_cmdtable.c
@@ -545,6 +545,31 @@ struct cmd_spec cmd_table[] = {
       "\"local_mem_bandwidth\":     Show local memory bandwidth(KB/s)\n",
     },
 #endif
+    { "usb-ctrl-attach",
+      &main_usbctrl_attach, 1, 1,
+      "Create a virtual USB controller for a domain",
+      "<Domain> [type=pv] [version=<version>] [ports=<number>]",
+    },
+    { "usb-ctrl-detach",
+      &main_usbctrl_detach, 0, 1,
+      "Remove the virtual USB controller specified by <DevId> for a domain",
+      "<Domain> <DevId>",
+    },
+    { "usb-attach",
+      &main_usbattach, 1, 1,
+      "Attach a USB device to a domain",
+      "<Domain> <bus.addr> [controller=<DevId> [port=<port>]]",
+    },
+    { "usb-detach",
+      &main_usbdetach, 0, 1,
+      "Detach a USB device from a domain",
+      "<Domain> <controller> <port>",
+    },
+    { "usb-list",
+      &main_usblist, 0, 0,
+      "List information about USB devices for a domain",
+      "<Domain>",
+    },
 };
 
 int cmdtable_len = sizeof(cmd_table)/sizeof(struct cmd_spec);
-- 
2.1.4

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

* [PATCH V5 6/7] xl: add usb-assignable-list command
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (4 preceding siblings ...)
  2015-06-25 10:07 ` [PATCH V5 5/7] xl: add pvusb commands Chunyan Liu
@ 2015-06-25 10:07 ` Chunyan Liu
  2015-06-25 10:07 ` [PATCH V5 7/7] domcreate: support pvusb in configuration file Chunyan Liu
  2015-07-13 10:09 ` [PATCH V5 0/7] xen pvusb toolstack work Juergen Gross
  7 siblings, 0 replies; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 UTC (permalink / raw)
  To: xen-devel
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, Ian.Jackson,
	Chunyan Liu, jfehlig

Add xl usb-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_usb_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  | 25 +++++++++++++++++++++++++
 tools/libxl/xl_cmdtable.c |  4 ++++
 3 files changed, 30 insertions(+)

diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h
index 2d57ee3..f37a99f 100644
--- a/tools/libxl/xl.h
+++ b/tools/libxl/xl.h
@@ -86,6 +86,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_usbassignable_list(int argc, char **argv);
 int main_usbctrl_attach(int argc, char **argv);
 int main_usbctrl_detach(int argc, char **argv);
 int main_usbattach(int argc, char **argv);
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index 82a92a1..005d1c2 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -3236,6 +3236,31 @@ static void usbinfo_print(libxl_device_usb *usb)
     libxl_usbinfo_dispose(&usbinfo);
 }
 
+static void usb_assignable_list(void)
+{
+    libxl_device_usb *usbs;
+    int num, i;
+
+    usbs = libxl_device_usb_assignable_list(ctx, &num);
+
+    for (i = 0; i < num; i++)
+        usbinfo_print(&usbs[i]);
+
+    libxl_device_usb_list_free(usbs, num);
+}
+
+int main_usbassignable_list(int argc, char **argv)
+{
+    int opt;
+
+    SWITCH_FOREACH_OPT(opt, "", NULL, "usb-assignable-list", 0) {
+        /* No options */
+    }
+
+    usb_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 8f1c93d..d5eb8df 100644
--- a/tools/libxl/xl_cmdtable.c
+++ b/tools/libxl/xl_cmdtable.c
@@ -570,6 +570,10 @@ struct cmd_spec cmd_table[] = {
       "List information about USB devices for a domain",
       "<Domain>",
     },
+    { "usb-assignable-list",
+      &main_usbassignable_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] 30+ messages in thread

* [PATCH V5 7/7] domcreate: support pvusb in configuration file
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (5 preceding siblings ...)
  2015-06-25 10:07 ` [PATCH V5 6/7] xl: add usb-assignable-list command Chunyan Liu
@ 2015-06-25 10:07 ` Chunyan Liu
  2015-07-13 10:08   ` Juergen Gross
  2015-07-13 10:09 ` [PATCH V5 0/7] xen pvusb toolstack work Juergen Gross
  7 siblings, 1 reply; 30+ messages in thread
From: Chunyan Liu @ 2015-06-25 10:07 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=['2.1,controller=0,port=1', ]

Signed-off-by: Chunyan Liu <cyliu@suse.com>
Signed-off-by: Simon Cao <caobosimon@gmail.com>
---
Changes:
  - add DEFINE_DEVICES_ADD(usbctrl) and DEFINE_DEVICES_ADD(usb),
    adjust codes to use that.
  - use "usbdev" to specify pvusb device. Keep "usb" as its original usage.
  - extract codes to parse_usbctrl_config and parse_usb_config as George
    suggests

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

diff --git a/docs/man/xl.cfg.pod.5 b/docs/man/xl.cfg.pod.5
index a3e0e2e..0af1802 100644
--- a/docs/man/xl.cfg.pod.5
+++ b/docs/man/xl.cfg.pod.5
@@ -655,6 +655,81 @@ assigned slave device.
 
 =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 protocol to implement USB controller, could be "pv" (indicates
+PVUSB) or "qemu" (indicates QEMU emulated). Currently only "pv" is supported.
+
+=item B<version=VERSION>
+
+Specifies version of the USB controller, could be 1 (USB1.1) or 2 (USB2.0).
+Default is 2 (USB2.0).
+
+=item B<ports=PORTS>
+
+Specifies port number of the USB controller. Default is 8.
+
+Each USB controller will have an index starting from 0. On the same
+controller, each port will have an index starting from 1.
+
+E.g.
+usbctrl=["version=1,ports=4", "version=2,ports=8",]
+The first controller has:
+controller index = 0, and port 1,2,3,4.
+The second controller has:
+controller index = 1, and port 1,2,3,4,5,6,7,8.
+
+=back
+
+=back
+
+=item B<usbdev=[ "USB_SPEC_STRING", "USB_SPEC_STRING", ... ]>
+
+Specifies the host USB devices to passthrough to this guest. Each
+B<USB_SPEC_STRING> has the form C<bus.addr,KEY=VALUE,KEY=VALUE,...> where:
+
+=over 4
+
+=item B<bus.addr>
+
+Identifies the busnum.devnum of the USB device from the host perspective.
+This is the same scheme as used in the output of C<lsusb> for the device in
+question.
+
+=item B<KEY=VALUE>
+
+Possible B<KEY>s are:
+
+=over 4
+
+=item B<controller=CONTROLLER>
+
+Specifies USB controller index, to which controller the USB device is attached.
+
+=item B<port=PORT>
+
+Specifies USB port index, to which port the USB device is attached. B<port=PORT>
+is valid only when B<controller=CONTROLLER> is specified. Without
+B<controller=CONTROLLER>, it will find the first available USB controller:port
+and use it. If there is no controller at all, it will create one.
+
+=back
+
+=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 86384d2..dd797b2 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -771,6 +771,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_usbs(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,
@@ -1418,13 +1422,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:
@@ -1432,6 +1436,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_usbs;
+        libxl__add_usbctrls(egc, ao, domid, d_config, &dcs->multidev);
+        libxl__multidev_prepared(egc, &dcs->multidev, 0);
+        return;
+    }
+
+    domcreate_attach_usbs(egc, multidev, 0);
+    return;
+
+error_out:
+    assert(ret);
+    domcreate_complete(egc, dcs, ret);
+}
+
+
+static void domcreate_attach_usbs(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_usbs > 0) {
+        /* Attach usbctrls */
+        libxl__multidev_begin(ao, &dcs->multidev);
+        dcs->multidev.callback = domcreate_attach_pci;
+        libxl__add_usbs(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)
 {
@@ -1445,7 +1512,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 9869a21..bee0ccb 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(usb)
 
 #undef DEFINE_DEVICES_ADD
 
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 6a8d4ee..36c26b8 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -3078,6 +3078,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_usbs(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 005d1c2..f96c3cb 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -1160,6 +1160,79 @@ static void parse_vnuma_config(const XLU_Config *config,
     free(vcpu_range_parsed);
 }
 
+static void parse_usbctrl_config(libxl_device_usbctrl *usbctrl,
+                                 const char * buf)
+{
+    char *buf2 = strdup(buf);
+    char *p, *p2;
+
+    p = strtok(buf2, ",");
+    if (!p)
+        goto out;
+    do {
+        while (*p == ' ')
+            p++;
+        if ((p2 = strchr(p, '=')) == NULL)
+            break;
+        *p2 = '\0';
+        if (!strcmp(p, "type")) {
+            if (!strcmp(p2 + 1, "pv")) {
+                usbctrl->protocol = LIBXL_USB_PROTOCOL_PV;
+            } else {
+                fprintf(stderr,
+                        "Unsupported USB controller type '%s'\n",
+                        p2 + 1);
+                exit(1);
+            }
+        } else if (!strcmp(p, "version")){
+            usbctrl->version = atoi(p2 + 1);
+        } else if (!strcmp(p, "ports")){
+            usbctrl->ports = atoi(p2 + 1);
+        } else {
+            fprintf(stderr, "Unknown string `%s' in usb spec\n", p);
+            exit(1);
+        }
+    } while ((p = strtok(NULL, ",")) != NULL);
+
+out:
+    free(buf2);
+}
+
+static void parse_usb_config(libxl_device_usb *usb, const char *buf)
+{
+    char *buf2 = strdup(buf);
+    char *p, *p2;
+
+    p = strtok(buf2, ",");
+    if (!p)
+        goto out;
+    do {
+        while(*p == ' ')
+            ++p;
+        if ((p2 = strchr(p, '=')) == NULL) {
+            char *busaddr = p;
+            p = strchr(busaddr, '.');
+            if (p) {
+                usb->hostbus = strtoul(busaddr, NULL, 0);
+                usb->hostaddr = strtoul(p + 1, NULL, 0);
+            }
+            continue;
+        }
+        *p2 = '\0';
+        if (!strcmp(p, "controller")) {
+            usb->ctrl = atoi(p2 + 1);
+        } else if (!strcmp(p, "port")) {
+            usb->port = atoi(p2 + 1);
+        } else {
+            fprintf(stderr, "Unknown string `%s' in usb spec\n", p);
+            exit(1);
+        }
+    } while ((p = strtok(NULL, ",")) != NULL);
+
+out:
+    free(buf2);
+}
+
 static void parse_config_data(const char *config_source,
                               const char *config_data,
                               int config_len,
@@ -1168,7 +1241,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, *usbs;
     XLU_ConfigList *channels, *ioports, *irqs, *iomem, *viridian, *dtdevs;
     int num_ioports, num_irqs, num_iomem, num_cpus, num_viridian;
     int pci_power_mgmt = 0;
@@ -1961,6 +2035,42 @@ 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;
+
+            d_config->usbctrls =
+                (libxl_device_usbctrl *) realloc(d_config->usbctrls,
+                sizeof (libxl_device_usbctrl) * (d_config->num_usbctrls + 1));
+            usbctrl = d_config->usbctrls + d_config->num_usbctrls;
+            libxl_device_usbctrl_init(usbctrl);
+
+            parse_usbctrl_config(usbctrl, buf);
+
+            d_config->num_usbctrls++;
+        }
+    }
+
+    if (!xlu_cfg_get_list (config, "usbdev", &usbs, 0, 0) ) {
+        d_config->num_usbs = 0;
+        d_config->usbs = NULL;
+        while ((buf = xlu_cfg_get_listitem (usbs, d_config->num_usbs)) != NULL) {
+            libxl_device_usb *usb;
+
+            d_config->usbs = (libxl_device_usb *) realloc(d_config->usbs,
+                    sizeof (libxl_device_usb) * (d_config->num_usbs+1));
+            usb = d_config->usbs + d_config->num_usbs;
+            libxl_device_usb_init(usb);
+
+            parse_usb_config(usb, buf);
+
+            d_config->num_usbs++;
+        }
+    }
+
     switch (xlu_cfg_get_list(config, "cpuid", &cpuids, 0, 1)) {
     case 0:
         {
-- 
2.1.4

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-25 10:07 ` [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file Chunyan Liu
@ 2015-06-25 11:09   ` Ian Jackson
  2015-06-26  5:47     ` Chun Yan Liu
  2015-06-26  5:48     ` Chun Yan Liu
  0 siblings, 2 replies; 30+ messages in thread
From: Ian Jackson @ 2015-06-25 11:09 UTC (permalink / raw)
  To: Chunyan Liu
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, xen-devel, jfehlig

Chunyan Liu writes ("[PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file"):
> Sysfs file has size=4096 but actual file content is less than that.
> Current libxl_read_file_contents will treat it as error when file size
> and actual file content differs, so reading sysfs file content with
> this function always fails.
> 
> Add a new entry libxl_read_sysfs_file_contents to handle sysfs file
> specially. It would be used in later pvusb work.

I think this still fails to detect a situation where the file is
unexpectedly longer than the requested size ?

As we wrote earlier:

> > > Is there any risk that the file is actually bigger than advertised, 
> > > rather than smaller ? 
> > 
> > For sysfs file, couldn't be bigger.
> 
> Then you should detect the condition that the file is bigger, and call
> it an error.

Thanks,
Ian.

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-06-25 10:07 ` [PATCH V5 3/7] libxl: add pvusb API Chunyan Liu
@ 2015-06-25 11:15   ` Ian Jackson
  2015-07-07  1:25   ` Chun Yan Liu
  2015-07-16 15:01   ` Juergen Gross
  2 siblings, 0 replies; 30+ messages in thread
From: Ian Jackson @ 2015-06-25 11:15 UTC (permalink / raw)
  To: Chunyan Liu
  Cc: jgross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	jfehlig, Simon Cao

Chunyan Liu writes ("[PATCH V5 3/7] libxl: add pvusb API"):
> 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>

I have reviewed the new API introduced in this patch and it looks
sensible to me.

I did notice this wrap damage:

> +/* 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)


I haven't reviewed the implementation (yet).

Ian.

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-25 11:09   ` Ian Jackson
@ 2015-06-26  5:47     ` Chun Yan Liu
  2015-06-26  5:48     ` Chun Yan Liu
  1 sibling, 0 replies; 30+ messages in thread
From: Chun Yan Liu @ 2015-06-26  5:47 UTC (permalink / raw)
  To: Ian Jackson
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig



>>> On 6/25/2015 at 07:09 PM, in message
<21899.57676.368102.982820@mariner.uk.xensource.com>, Ian Jackson
<Ian.Jackson@eu.citrix.com> wrote: 
> Chunyan Liu writes ("[PATCH V5 2/7] libxl_read_file_contents: add new entry  
> to read sysfs file"): 
> > Sysfs file has size=4096 but actual file content is less than that. 
> > Current libxl_read_file_contents will treat it as error when file size 
> > and actual file content differs, so reading sysfs file content with 
> > this function always fails. 
> >  
> > Add a new entry libxl_read_sysfs_file_contents to handle sysfs file 
> > specially. It would be used in later pvusb work. 
>  
> I think this still fails to detect a situation where the file is 
> unexpectedly longer than the requested size ? 


+            } else if (feof(f)) {
+                if (rs < datalen && tolerate_shrinking_file) {
+                    datalen = rs;
+                } else {

If the file is bigger than the requested size, it will fall to this branch and report error.
Do you mean I should report another error message separately?

- Chunyan

+                    LOG(ERROR, "%s changed size while we were reading it",
+                        filename);
+                    goto xe;
+                }
+            } else {

>  
> As we wrote earlier: 
>  
> > > > Is there any risk that the file is actually bigger than advertised,  
> > > > rather than smaller ?  
> > >  
> > > For sysfs file, couldn't be bigger. 
> >  
> > Then you should detect the condition that the file is bigger, and call 
> > it an error. 
>  
> Thanks, 
> Ian. 
>  
>  

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-25 11:09   ` Ian Jackson
  2015-06-26  5:47     ` Chun Yan Liu
@ 2015-06-26  5:48     ` Chun Yan Liu
  2015-06-26 13:05       ` Ian Jackson
  1 sibling, 1 reply; 30+ messages in thread
From: Chun Yan Liu @ 2015-06-26  5:48 UTC (permalink / raw)
  To: Ian Jackson
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig



>>> On 6/25/2015 at 07:09 PM, in message
<21899.57676.368102.982820@mariner.uk.xensource.com>, Ian Jackson
<Ian.Jackson@eu.citrix.com> wrote: 
> Chunyan Liu writes ("[PATCH V5 2/7] libxl_read_file_contents: add new entry  
> to read sysfs file"): 
> > Sysfs file has size=4096 but actual file content is less than that. 
> > Current libxl_read_file_contents will treat it as error when file size 
> > and actual file content differs, so reading sysfs file content with 
> > this function always fails. 
> >  
> > Add a new entry libxl_read_sysfs_file_contents to handle sysfs file 
> > specially. It would be used in later pvusb work. 
>  
> I think this still fails to detect a situation where the file is 
> unexpectedly longer than the requested size ? 


+            } else if (feof(f)) {
+                if (rs < datalen && tolerate_shrinking_file) {
+                    datalen = rs;
+                } else {

If the file is bigger than the requested size, it will fall to this branch and report error.
Do you mean I should report another error message separately?

- Chunyan

+                    LOG(ERROR, "%s changed size while we were reading it",
+                        filename);
+                    goto xe;
+                }
+            } else {

>  
> As we wrote earlier: 
>  
> > > > Is there any risk that the file is actually bigger than advertised,  
> > > > rather than smaller ?  
> > >  
> > > For sysfs file, couldn't be bigger. 
> >  
> > Then you should detect the condition that the file is bigger, and call 
> > it an error. 
>  
> Thanks, 
> Ian. 
>  
>  

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-26  5:48     ` Chun Yan Liu
@ 2015-06-26 13:05       ` Ian Jackson
  2015-06-29 10:15         ` Chun Yan Liu
  0 siblings, 1 reply; 30+ messages in thread
From: Ian Jackson @ 2015-06-26 13:05 UTC (permalink / raw)
  To: Chun Yan Liu
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig

Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file"):
> >>> On 6/25/2015 at 07:09 PM, in message
> <21899.57676.368102.982820@mariner.uk.xensource.com>, Ian Jackson
> <Ian.Jackson@eu.citrix.com> wrote: 
> > Chunyan Liu writes ("[PATCH V5 2/7] libxl_read_file_contents: add new entry> > > Add a new entry libxl_read_sysfs_file_contents to handle sysfs file 
> > > specially. It would be used in later pvusb work. 
> >  
> > I think this still fails to detect a situation where the file is 
> > unexpectedly longer than the requested size ? 
> 
> +            } else if (feof(f)) {
> +                if (rs < datalen && tolerate_shrinking_file) {
> +                    datalen = rs;
> +                } else {
> 
> If the file is bigger than the requested size, it will fall to this
> branch and report error.

I don't think this is true.  I just applied your patch to my copy of
staging to see what the code looks like and saw this:

    if (stab.st_size && data_r) {
        data = malloc(datalen);
        if (!data) goto xe;

        rs = fread(data, 1, datalen, f);
        if (rs != datalen) {
            if (ferror(f)) {
                LOGE(ERROR, "failed to read %s", filename);
                goto xe;
            } else if (feof(f)) {
                if (rs < datalen && tolerate_shrinking_file) {
                    datalen = rs;
                } else {
                    LOG(ERROR, "%s changed size while we were reading it",
                        filename);
                    goto xe;
                }
            } else {
                abort();
            }
        }
    }

So I think in the case of a sysfs file which is >4096 bytes:

 - stat will succeed and give st_size == 4096
 - fread(,,4096,) will read the first 4096 bytes and return 4096
 - rs == datalen, so we don't take the `errors and special
     cases' branch
 - we return success having read 4096 bytes

But please feel free to explain why I'm wrong.

Thanks,
Ian.

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-26 13:05       ` Ian Jackson
@ 2015-06-29 10:15         ` Chun Yan Liu
  2015-06-29 10:52           ` Ian Jackson
  0 siblings, 1 reply; 30+ messages in thread
From: Chun Yan Liu @ 2015-06-29 10:15 UTC (permalink / raw)
  To: Ian Jackson
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig



>>> On 6/26/2015 at 09:05 PM, in message
<21901.20009.85407.581628@mariner.uk.xensource.com>, Ian Jackson
<Ian.Jackson@eu.citrix.com> wrote: 
> Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new  
> entry to read sysfs file"): 
> > >>> On 6/25/2015 at 07:09 PM, in message 
> > <21899.57676.368102.982820@mariner.uk.xensource.com>, Ian Jackson 
> > <Ian.Jackson@eu.citrix.com> wrote:  
> > > Chunyan Liu writes ("[PATCH V5 2/7] libxl_read_file_contents: add new  
> entry> > > Add a new entry libxl_read_sysfs_file_contents to handle sysfs file  
> > > > specially. It would be used in later pvusb work.  
> > >   
> > > I think this still fails to detect a situation where the file is  
> > > unexpectedly longer than the requested size ?  
> >  
> > +            } else if (feof(f)) { 
> > +                if (rs < datalen && tolerate_shrinking_file) { 
> > +                    datalen = rs; 
> > +                } else { 
> >  
> > If the file is bigger than the requested size, it will fall to this 
> > branch and report error. 
>  
> I don't think this is true.  I just applied your patch to my copy of 
> staging to see what the code looks like and saw this: 
>  
>     if (stab.st_size && data_r) { 
>         data = malloc(datalen); 
>         if (!data) goto xe; 
>  
>         rs = fread(data, 1, datalen, f); 
>         if (rs != datalen) { 
>             if (ferror(f)) { 
>                 LOGE(ERROR, "failed to read %s", filename); 
>                 goto xe; 
>             } else if (feof(f)) { 
>                 if (rs < datalen && tolerate_shrinking_file) { 
>                     datalen = rs; 
>                 } else { 
>                     LOG(ERROR, "%s changed size while we were reading it", 
>                         filename); 
>                     goto xe; 
>                 } 
>             } else { 
>                 abort(); 
>             } 
>         } 
>     } 
>  
> So I think in the case of a sysfs file which is >4096 bytes: 
>  
>  - stat will succeed and give st_size == 4096 
>  - fread(,,4096,) will read the first 4096 bytes and return 4096 
>  - rs == datalen, so we don't take the `errors and special 
>      cases' branch 
>  - we return success having read 4096 bytes 
>  
> But please feel free to explain why I'm wrong. 

You are right. I'm confused about the original logic. The original code
doesn't consider this case (size bigger than requested) at all. So, we need
to add a check if (rs == datalen && read end of file), if not, means bigger
than requested, report error.

- Chunyan

>  
> Thanks, 
> Ian. 
>  
>  

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-29 10:15         ` Chun Yan Liu
@ 2015-06-29 10:52           ` Ian Jackson
  2015-06-30  1:43             ` Chun Yan Liu
  0 siblings, 1 reply; 30+ messages in thread
From: Ian Jackson @ 2015-06-29 10:52 UTC (permalink / raw)
  To: Chun Yan Liu
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig

Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file"):
> On 6/26/2015 at 09:05 PM, in message
> <21901.20009.85407.581628@mariner.uk.xensource.com>, Ian Jackson
> <Ian.Jackson@eu.citrix.com> wrote: 
> > But please feel free to explain why I'm wrong. 
> 
> You are right. I'm confused about the original logic. The original code
> doesn't consider this case (size bigger than requested) at all.

Indeed.  The original code assumes that files don't change size, and
only detects them shrinking because that causes a short read which it
has deal with somehow.

But if we are intending to use this with sysfs files, where the
reported size is known to be wrong, it seems to me that we should be
more proactive.

> So, we need to add a check if (rs == datalen && read end of file),
> if not, means bigger than requested, report error.

To detect a growing file it will be necessary to actually attempt to
read at least one byte more than expected.  This is probably done most
conveniently by making the buffer one byte bigger, too.

Ian.

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-29 10:52           ` Ian Jackson
@ 2015-06-30  1:43             ` Chun Yan Liu
  2015-06-30  9:08               ` Ian Jackson
  0 siblings, 1 reply; 30+ messages in thread
From: Chun Yan Liu @ 2015-06-30  1:43 UTC (permalink / raw)
  To: Ian Jackson
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig



>>> On 6/29/2015 at 06:52 PM, in message
<21905.9050.452111.208124@mariner.uk.xensource.com>, Ian Jackson
<Ian.Jackson@eu.citrix.com> wrote: 
> Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new  
> entry to read sysfs file"): 
> > On 6/26/2015 at 09:05 PM, in message 
> > <21901.20009.85407.581628@mariner.uk.xensource.com>, Ian Jackson 
> > <Ian.Jackson@eu.citrix.com> wrote:  
> > > But please feel free to explain why I'm wrong.  
> >  
> > You are right. I'm confused about the original logic. The original code 
> > doesn't consider this case (size bigger than requested) at all. 
>  
> Indeed.  The original code assumes that files don't change size, and 
> only detects them shrinking because that causes a short read which it 
> has deal with somehow. 
>  
> But if we are intending to use this with sysfs files, where the 
> reported size is known to be wrong, it seems to me that we should be 
> more proactive.

If only for sysfs files, the bigger size problem should never happens, since sysfs
system is not like normal file system, user won't be able to change the size.

Reference from following link:
http://www.makelinux.net/books/lkd2/ch17lev1sec8

[ The sysfs filesystem is an in-memory virtual filesystem that provides a view
of the kobject object hierarchy. It enables users to view the device topology of
their system as a simple filesystem. Using attributes, kobjects can export files
that allow kernel variables to be read from and optionally written to.
......

The show() method is invoked on read. It must copy the value of the attribute
given by attr into the buffer provided by buffer. The buffer is PAGE_SIZE bytes
in length; on x86, PAGE_SIZE is 4096 bytes. The function should return the size
in bytes of data actually written into buffer on success or a negative error
code on failure.

The store() method is invoked on write. It must read the size bytes from buffer
into the variable represented by the attribute attr. The size of the buffer is
always PAGE_SIZE or smaller. The function should return the size in bytes of
data read from buffer on success or a negative error code on failure.]

- Chunyan

>  
> > So, we need to add a check if (rs == datalen && read end of file), 
> > if not, means bigger than requested, report error. 
>  
> To detect a growing file it will be necessary to actually attempt to 
> read at least one byte more than expected.  This is probably done most 
> conveniently by making the buffer one byte bigger, too. 
>  
> Ian. 
>  
>  

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-30  1:43             ` Chun Yan Liu
@ 2015-06-30  9:08               ` Ian Jackson
  2015-06-30  9:32                 ` Chun Yan Liu
  0 siblings, 1 reply; 30+ messages in thread
From: Ian Jackson @ 2015-06-30  9:08 UTC (permalink / raw)
  To: Chun Yan Liu
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig

Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file"):
> >>> On 6/29/2015 at 06:52 PM, in message
> <21905.9050.452111.208124@mariner.uk.xensource.com>, Ian Jackson
> <Ian.Jackson@eu.citrix.com> wrote: 
> > Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new > > But if we are intending to use this with sysfs files, where the 
> > reported size is known to be wrong, it seems to me that we should be 
> > more proactive.
> 
> If only for sysfs files, the bigger size problem should never
> happens, since sysfs system is not like normal file system, user
> won't be able to change the size.
> 
> Reference from following link:
> http://www.makelinux.net/books/lkd2/ch17lev1sec8

I don't think that can be relied on as a guide to the future API.
Maybe sysfs will grow support for bigger files in the future.  (Also,
that is actually a description of the kernel internals, not of the
syscall API).

Thanks,
Ian.

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-30  9:08               ` Ian Jackson
@ 2015-06-30  9:32                 ` Chun Yan Liu
  2015-06-30  9:42                   ` Ian Jackson
  0 siblings, 1 reply; 30+ messages in thread
From: Chun Yan Liu @ 2015-06-30  9:32 UTC (permalink / raw)
  To: Ian Jackson
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig



>>> On 6/30/2015 at 05:08 PM, in message
<21906.23698.778991.627734@mariner.uk.xensource.com>, Ian Jackson
<Ian.Jackson@eu.citrix.com> wrote: 
> Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new  
> entry to read sysfs file"): 
> > >>> On 6/29/2015 at 06:52 PM, in message 
> > <21905.9050.452111.208124@mariner.uk.xensource.com>, Ian Jackson 
> > <Ian.Jackson@eu.citrix.com> wrote:  
> > > Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new  
> > > But if we are intending to use this with sysfs files, where the  
> > > reported size is known to be wrong, it seems to me that we should be  
> > > more proactive. 
> >  
> > If only for sysfs files, the bigger size problem should never 
> > happens, since sysfs system is not like normal file system, user 
> > won't be able to change the size. 
> >  
> > Reference from following link: 
> > http://www.makelinux.net/books/lkd2/ch17lev1sec8 
>  
> I don't think that can be relied on as a guide to the future API. 
> Maybe sysfs will grow support for bigger files in the future.  (Also, 
> that is actually a description of the kernel internals, not of the 
> syscall API). 

Yes, that's about kernel internals. But syscall API will finally call
kernel implementation. From those description, we knows why
fstat always return size 4096 (on x86_64, although actual file
content length is less). And it's not possible the file is changed
into a bigger size during we are reading it. About whether it'll
be changed in future, really don't know.

As to adding a check, it's certainly OK. I can update.

Thanks,
Chunyan
 
>  
> Thanks, 
> Ian. 
>  
>  

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

* Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file
  2015-06-30  9:32                 ` Chun Yan Liu
@ 2015-06-30  9:42                   ` Ian Jackson
  0 siblings, 0 replies; 30+ messages in thread
From: Ian Jackson @ 2015-06-30  9:42 UTC (permalink / raw)
  To: Chun Yan Liu
  Cc: Juergen Gross, wei.liu2, ian.campbell, george.dunlap, xen-devel,
	Jim Fehlig

Chun Yan Liu writes ("Re: [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file"):
> >>> On 6/30/2015 at 05:08 PM, in message
> <21906.23698.778991.627734@mariner.uk.xensource.com>, Ian Jackson
> <Ian.Jackson@eu.citrix.com> wrote: 
> > Maybe sysfs will grow support for bigger files in the future.  (Also, 
> > that is actually a description of the kernel internals, not of the 
> > syscall API). 
> 
> Yes, that's about kernel internals. But syscall API will finally call
> kernel implementation. From those description, we knows why
> fstat always return size 4096 (on x86_64, although actual file
> content length is less). And it's not possible the file is changed
> into a bigger size during we are reading it. About whether it'll
> be changed in future, really don't know.

Right.  My point was that an internals document is a poor guide to
future behaviour.  So,

> As to adding a check, it's certainly OK. I can update.

Yes, please.

Thanks,
Ian.

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-06-25 10:07 ` [PATCH V5 3/7] libxl: add pvusb API Chunyan Liu
  2015-06-25 11:15   ` Ian Jackson
@ 2015-07-07  1:25   ` Chun Yan Liu
  2015-07-07  9:57     ` George Dunlap
  2015-07-16 15:01   ` Juergen Gross
  2 siblings, 1 reply; 30+ messages in thread
From: Chun Yan Liu @ 2015-07-07  1:25 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 the implementation? If there is anything improper, I can update.

Thanks,
Chunyan

>>> On 6/25/2015 at 06:07 PM, in message
<1435226838-3067-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: 
>   - Use macros DEFINE_DEVICE_ADD and DEFINE_DEVICES_ADD for adding 
>     usbctrl and usb, update all related codes. 
>   - Use an extend macro DEFINE_DEVICE_REMOVE_EXT for removimg usbctrl
>     update all related codes. 
>   - Remove busid from libxl_device_usb definition, keep bus.addr only, 
>     update all related codes. 
>   - Remove documentation since it's mostly about design, move to 
>     cover-letter. Some parts are moved to code comments. 
>   - Address some other comments 
>  
>  tools/libxl/Makefile                 |    2 +- 
>  tools/libxl/libxl.c                  |   53 ++ 
>  tools/libxl/libxl.h                  |   64 ++ 
>  tools/libxl/libxl_device.c           |    4 + 
>  tools/libxl/libxl_internal.h         |   20 +- 
>  tools/libxl/libxl_osdeps.h           |   13 + 
>  tools/libxl/libxl_pvusb.c            | 1305  
> ++++++++++++++++++++++++++++++++++ 
>  tools/libxl/libxl_types.idl          |   50 ++ 
>  tools/libxl/libxl_types_internal.idl |    1 + 
>  tools/libxl/libxl_utils.c            |   16 + 
>  tools/libxl/libxl_utils.h            |    5 + 
>  11 files changed, 1531 insertions(+), 2 deletions(-) 
>  create mode 100644 tools/libxl/libxl_pvusb.c 
>  
> 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 6570476..6542127 100644 
> --- a/tools/libxl/libxl.c 
> +++ b/tools/libxl/libxl.c 
> @@ -4233,11 +4233,54 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) 
>   
>   
> /**************************************************************************** 
> **/ 
>   
> +/* Macro for defining device remove/destroy functions for usbctrl */ 
> +/* Following functions are defined: 
> + * libxl_device_usbctrl_remove 
> + * libxl_device_usbctrl_destroy 
> + */ 
> + 
> +#define DEFINE_DEVICE_REMOVE_EXT(type, removedestroy, f)                \ 
> +    int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \ 
> +        uint32_t domid, libxl_device_##type *type,                      \ 
> +        const libxl_asyncop_how *ao_how)                                \ 
> +    {                                                                   \ 
> +        AO_CREATE(ctx, domid, ao_how);                                  \ 
> +        libxl__device *device;                                          \ 
> +        libxl__ao_device *aodev;                                        \ 
> +        int rc;                                                         \ 
> +                                                                        \ 
> +        GCNEW(device);                                                  \ 
> +        rc = libxl__device_from_##type(gc, domid, type, device);        \ 
> +        if (rc != 0) goto out;                                          \ 
> +                                                                        \ 
> +        GCNEW(aodev);                                                   \ 
> +        libxl__prepare_ao_device(ao, aodev);                            \ 
> +        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;                    \ 
> +        aodev->dev = device;                                            \ 
> +        aodev->callback = device_addrm_aocomplete;                      \ 
> +        aodev->force = f;                                               \ 
> +        libxl__initiate_device_##type##_remove(egc, aodev);             \ 
> +                                                                        \ 
> +    out:                                                                \ 
> +        if (rc) return AO_ABORT(rc);                                    \ 
> +        return AO_INPROGRESS;                                           \ 
> +    } 
> + 
> + 
> +DEFINE_DEVICE_REMOVE_EXT(usbctrl, remove, 0) 
> +DEFINE_DEVICE_REMOVE_EXT(usbctrl, destroy, 1) 
> + 
> +#undef DEFINE_DEVICE_REMOVE_EXT 
> + 
> +/************************************************************************** 
> ****/ 
> + 
>  /* Macro for defining device addition functions in a compact way */ 
>  /* The following functions are defined: 
>   * libxl_device_disk_add 
>   * libxl_device_nic_add 
>   * libxl_device_vtpm_add 
> + * libxl_device_usbctrl_add 
> + * libxl_device_usb_add 
>   */ 
>   
>  #define DEFINE_DEVICE_ADD(type)                                         \ 
> @@ -4269,6 +4312,12 @@ DEFINE_DEVICE_ADD(nic) 
>  /* vtpm */ 
>  DEFINE_DEVICE_ADD(vtpm) 
>   
> +/* usbctrl */ 
> +DEFINE_DEVICE_ADD(usbctrl) 
> + 
> +/* usb */ 
> +DEFINE_DEVICE_ADD(usb) 
> + 
>  #undef DEFINE_DEVICE_ADD 
>   
>   
> /**************************************************************************** 
> **/ 
> @@ -6770,6 +6819,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..6669835 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,53 @@ 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); 
> + 
> +/* 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, 
> +                                  libxl_devid 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); 
> + 
>  /* 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 93bb41e..9869a21 100644 
> --- a/tools/libxl/libxl_device.c 
> +++ b/tools/libxl/libxl_device.c 
> @@ -676,6 +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; 
> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) { 
> +                    libxl__initiate_device_usbctrl_remove(egc, aodev); 
> +                    continue; 
> +                } 
>                  libxl__initiate_device_remove(egc, aodev); 
>              } 
>          } 
> diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h 
> index 0965e08..6a8d4ee 100644 
> --- a/tools/libxl/libxl_internal.h 
> +++ b/tools/libxl/libxl_internal.h 
> @@ -2438,6 +2438,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_usb_add(libxl__egc *egc, uint32_t domid, 
> +                                   libxl_device_usb *usb, 
> +                                   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); 
> @@ -2470,6 +2478,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. 
> @@ -3645,7 +3660,10 @@ 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)->hostbus == (b)->hostbus &&  \ 
> +                           (a)->hostaddr == (b)->hostaddr) 
> +#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..d8cacd4 
> --- /dev/null 
> +++ b/tools/libxl/libxl_pvusb.c 
> @@ -0,0 +1,1305 @@ 
> +/* 
> + * 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 
> + 
> +/* Utility to read backend xenstore keys */ 
> +#define READ_BACKEND(tgc, subpath)                                    \ 
> +            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath,  
> be_path)) 
> + 
> +/* Utility to read frontend xenstore keys */ 
> +#define READ_FRONTEND(tgc, subpath)                                   \ 
> +            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath,  
> fe_path)) 
> + 
> +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; 
> +} 
> + 
> +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", "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(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", "1"); 
> + 
> +    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. 
> + */ 
> +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; 
> +        } 
> +    } 
> + 
> +    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); 
> + 
> +    rc = 0; 
> + 
> +out: 
> +    aodev->rc = rc; 
> +    if (rc) aodev->callback(egc, aodev); 
> +    return; 
> +} 
> + 
> +static int 
> +libxl__device_usb_list_per_usbctrl(libxl__gc *gc, uint32_t domid, 
> +                                   libxl_devid usbctrl, 
> +                                   libxl_device_usb **usbs, int *num); 
> + 
> +static int 
> +libxl__device_usb_remove(libxl__gc *gc, uint32_t domid, libxl_device_usb  
> *usb); 
> + 
> +int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, 
> +                                  uint32_t domid, 
> +                                  int devid, 
> +                                  libxl_device_usbctrl *usbctrl) 
> +{ 
> +    GC_INIT(ctx); 
> +    libxl_device_usbctrl *usbctrls; 
> +    int nb = 0; 
> +    int i, rc = -1; 
> + 
> +    usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb); 
> +    if (!nb) goto out; 
> + 
> +    libxl_device_usbctrl_init(usbctrl); 
> +    for (i = 0; i < nb; i++) { 
> +        if (devid == usbctrls[i].devid) { 
> +            *usbctrl = usbctrls[i]; 
> +            rc = 0; 
> +            break; 
> +        } 
> +    } 
> + 
> +    libxl_device_usbctrl_list_free(usbctrls, nb); 
> + 
> +out: 
> +    GC_FREE; 
> +    return rc; 
> +} 
> + 
> +/* 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 
> + */ 
> +void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, 
> +                                           libxl__ao_device *aodev) 
> +{ 
> +    STATE_AO_GC(aodev->ao); 
> +    libxl_device_usb *usbs = NULL; 
> +    int numusb = 0; 
> +    int i, rc; 
> +    uint32_t domid = ao->domid; 
> +    int usbctrl_devid = aodev->dev->devid; 
> + 
> +    /* 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 (libxl__device_usb_remove(gc, domid, &usbs[i])) { 
> +            LOG(ERROR, "libxl__device_usb_remove failed"); 
> +            rc = ERROR_FAIL; 
> +            goto out; 
> +        } 
> +    } 
> + 
> +    libxl_device_usb_list_free(usbs, numusb); 
> + 
> +    /* Remove usbctrl */ 
> +    return libxl__initiate_device_remove(egc, aodev); 
> + 
> +out: 
> +    libxl_device_usb_list_free(usbs, numusb); 
> +    aodev->rc = rc; 
> +    if (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 **dir = NULL; 
> +    unsigned int ndirs = 0; 
> + 
> +    *num = 0; 
> + 
> +    path = GCSPRINTF("%s/device/vusb", 
> +                     libxl__xs_get_dompath(gc, domid)); 
> +    dir = libxl__xs_directory(gc, XBT_NULL, path, &ndirs); 
> + 
> +    if (dir && ndirs) { 
> +        usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * ndirs); 
> +        libxl_device_usbctrl* usbctrl; 
> +        libxl_device_usbctrl* end = usbctrls + ndirs; 
> +        for (usbctrl = usbctrls; usbctrl < end; usbctrl++, dir++, (*num)++)  
> { 
> +            const char *tmp, *be_path; 
> +            const char *fe_path = GCSPRINTF("%s/%s", path, *dir); 
> + 
> +            libxl_device_usbctrl_init(usbctrl); 
> +            usbctrl->devid = atoi(*dir); 
> + 
> +            be_path = READ_FRONTEND(gc, "backend"); 
> +            if (!be_path) goto outerr; 
> + 
> +            tmp = READ_FRONTEND(gc, "backend-id"); 
> +            if (!tmp) goto outerr; 
> +            usbctrl->backend_domid = atoi(tmp); 
> + 
> +            tmp = READ_BACKEND(gc, "usb-ver"); 
> +            if (!tmp) goto outerr; 
> +            usbctrl->version = atoi(tmp); 
> + 
> +            tmp = READ_BACKEND(gc, "num-ports"); 
> +            if (!tmp) goto outerr; 
> +            usbctrl->ports = atoi(tmp); 
> + 
> +            tmp = READ_BACKEND(gc, "type"); 
> +            if (!tmp) goto outerr; 
> +            libxl_usb_protocol_from_string(tmp, &usbctrl->protocol); 
> +        } 
> +    } 
> + 
> +    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; 
> +    const char *fe_path, *be_path, *tmp; 
> +    int rc = 0; 
> + 
> +    usbctrlinfo->devid = usbctrl->devid; 
> + 
> +    dompath = libxl__xs_get_dompath(gc, domid); 
> +    fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrlinfo->devid); 
> +    be_path = READ_FRONTEND(gc, "backend"); 
> +    if (!be_path) { 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> +    usbctrlinfo->backend = libxl__strdup(NOGC, be_path); 
> + 
> +    tmp = READ_FRONTEND(gc, "backend-id"); 
> +    usbctrlinfo->backend_id = tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_FRONTEND(gc, "state"); 
> +    usbctrlinfo->state = tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_FRONTEND(gc, "event-channel"); 
> +    usbctrlinfo->evtch = tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_FRONTEND(gc, "urb-ring-ref"); 
> +    usbctrlinfo->ref_urb = tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_FRONTEND(gc, "conn-ring-ref"); 
> +    usbctrlinfo->ref_conn= tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_BACKEND(gc, "frontend"); 
> +    usbctrlinfo->frontend = libxl__strdup(NOGC, tmp); 
> + 
> +    tmp = READ_BACKEND(gc, "frontend-id"); 
> +    usbctrlinfo->frontend_id = tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_BACKEND(gc, "num-ports"); 
> +    usbctrlinfo->ports = tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_BACKEND(gc, "usb-ver"); 
> +    usbctrlinfo->version = tmp ? strtoul(tmp, NULL, 10) : -1; 
> + 
> +    tmp = READ_BACKEND(gc, "type"); 
> +    libxl_usb_protocol_from_string(tmp, &usbctrlinfo->protocol); 
> + 
> +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/busnum", busid); 
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
> +        sscanf(buf, "%x", bus); 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", 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_busaddr_from_busid(gc, 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; 
> +    char *busid = NULL; 
> + 
> +    assert(usb->hostbus > 0 && usb->hostaddr > 0); 
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", 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, 
> +                                   libxl_devid 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_busaddr_from_busid(gc, 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]; 
> +            (*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, 
> +                                  libxl_devid 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; 
> +} 
> + 
> +/* 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_usb_setdefault(libxl__gc *gc, uint32_t domid, 
> +                                        libxl_device_usb *usb, 
> +                                        bool update_json) 
> +{ 
> +    int rc = -1; 
> +    char *be_path, *tmp; 
> + 
> +    if (usb->ctrl == -1) { 
> +        if (usb->port) { 
> +            LOG(ERROR, "USB controller must be specified if you specify  
> port ID"); 
> +            return ERROR_INVAL; 
> +        } 
> + 
> +        rc = libxl__device_usb_set_default_usbctrl(gc, domid, usb); 
> +        /* 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) { 
> +                    goto out; 
> +                } 
> +            } 
> + 
> +            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, 
> +                                                    update_json); 
> +            if (rc) goto out; 
> + 
> +            usb->ctrl = usbctrl->devid; 
> +            usb->port = 1; 
> +        } 
> +    } else if (!usb->port) { 
> +        /* Valid port starts from 1. Choose port for us. */ 
> +        int i, ports; 
> + 
> +        be_path = GCSPRINTF("%s/backend/vusb/%d/%d", 
> +                            libxl__xs_get_dompath(gc,  
> LIBXL_TOOLSTACK_DOMID), 
> +                            domid, usb->ctrl); 
> +        tmp = READ_BACKEND(gc, "num-ports"); 
> +        ports = tmp ? atoi(tmp) : 0; 
> +        for (i = 0; i < ports; i++) { 
> +            tmp = libxl__xs_read(gc, XBT_NULL, 
> +                                 GCSPRINTF("%s/port/%d", be_path, i + 1)); 
> +            if (tmp && !strcmp(tmp, "")) { 
> +                usb->port = i + 1; 
> +                break; 
> +            } 
> +        } 
> + 
> +        if (!usb->port) { 
> +            LOG(ERROR, "No available port under specified controller"); 
> +            goto out; 
> +        } 
> +    } else { 
> +        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"); 
> +            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_usb_add_xenstore(libxl__gc *gc, uint32_t domid, 
> +                                          libxl_device_usb *usb, 
> +                                          bool update_json) 
> +{ 
> +    char *be_path; 
> +    char *busid; 
> +    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); 
> + 
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->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(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, 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_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; 
> +    char *busid; 
> +    int rc = 0; 
> + 
> +    *intfs = NULL; 
> +    *num = 0; 
> + 
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
> +    if (!busid) { 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    buf = GCSPRINTF("%s:", 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; 
> +} 
> + 
> +/* 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(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 USB device, check each interface, 
> + * unbind from "usbback" driver and rebind to its 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 *busid; 
> +    char *usb_encode = NULL; 
> + 
> +    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0) 
> +        return ERROR_FAIL; 
> + 
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
> +    usb_encode = usb_interface_xenstore_encode(busid); 
> + 
> +    for (i = 0; i < num; i++) { 
> +        char *intf = intfs[i]; 
> +        char *drvpath = NULL; 
> + 
> +        /* check if the USB interface is already bound to "usbbcak" */ 
> +        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 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 *busid; 
> +    char *usb_encode = NULL; 
> + 
> +    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0) 
> +        return ERROR_FAIL; 
> + 
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
> +    usb_encode = usb_interface_xenstore_encode(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; 
> +} 
> + 
> +/* 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. 
> + */ 
> +void libxl__device_usb_add(libxl__egc *egc, uint32_t domid, 
> +                           libxl_device_usb *usb, 
> +                           libxl__ao_device *aodev) 
> +{ 
> +    STATE_AO_GC(aodev->ao); 
> +    int rc = -1; 
> +    char *busid = NULL; 
> + 
> +    assert(usb->hostbus > 0 && usb->hostaddr > 0); 
> + 
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
> +    if (!busid) { 
> +        LOG(ERROR, "USB device doesn't exist in sysfs"); 
> +        goto out; 
> +    } 
> + 
> +    if (!is_usb_assignable(gc, usb)) { 
> +        LOG(ERROR, "USB device is not assignable."); 
> +        goto out; 
> +    } 
> + 
> +    /* check usb device is already assigned */ 
> +    if (is_usb_assigned(gc, usb)) { 
> +        LOG(ERROR, "USB device is already attached to a domain."); 
> +        goto out; 
> +    } 
> + 
> +    rc = libxl__device_usb_setdefault(gc, domid, usb, aodev->update_json); 
> +    if (rc) goto out; 
> + 
> +    rc = libxl__device_usb_add_xenstore(gc, domid, usb, aodev->update_json); 
> +    if (rc) goto out; 
> + 
> +    rc = usbback_dev_assign(gc, usb); 
> +    if (rc) { 
> +        libxl__device_usb_remove_xenstore(gc, domid, usb); 
> +        goto out; 
> +    } 
> + 
> +    libxl__ao_complete(egc, ao, 0); 
> +    rc = 0; 
> + 
> +out: 
> +    aodev->rc = rc; 
> +    if (rc) aodev->callback(egc, aodev); 
> +    return; 
> +} 
> + 
> +int libxl_ctrlport_to_device_usb(libxl_ctx *ctx, 
> +                                 uint32_t domid, 
> +                                 int ctrl, 
> +                                 int port, 
> +                                 libxl_device_usb *usb) 
> +{ 
> +    GC_INIT(ctx); 
> +    char *dompath, *be_path, *busid; 
> +    int rc = ERROR_FAIL; 
> + 
> +    dompath = libxl__xs_get_dompath(gc, domid); 
> +    if (!dompath) 
> +        goto out; 
> + 
> +    be_path = libxl__xs_read(gc, XBT_NULL, 
> +                  GCSPRINTF("%s/device/vusb/%d/backend", dompath, ctrl)); 
> +    if (!be_path) 
> +        goto out; 
> + 
> +    busid = libxl__xs_read(gc, XBT_NULL, 
> +                           GCSPRINTF("%s/port/%d", be_path, port)); 
> +    if (busid && strcmp(busid, "")) { 
> +        usb->ctrl = ctrl; 
> +        usb->port = port; 
> +        usb_busaddr_from_busid(gc, busid, &usb->hostbus, &usb->hostaddr); 
> +        rc = 0; 
> +    } 
> + 
> +out: 
> +    GC_FREE; 
> +    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_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; 
> +} 
> + 
> +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_getinfo(libxl_ctx *ctx, libxl_device_usb *usb, 
> +                             libxl_usbinfo *usbinfo) 
> +{ 
> +    GC_INIT(ctx); 
> +    char *filename; 
> +    char *busid; 
> +    void *buf = NULL; 
> +    int buflen, rc; 
> + 
> +    usbinfo->ctrl = usb->ctrl; 
> +    usbinfo->port = usb->port; 
> + 
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
> +    if (!busid) { 
> +        rc = ERROR_FAIL; 
> +        goto out; 
> +    } 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid); 
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
> +        sscanf(buf, "%x", &usbinfo->devnum); 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid); 
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
> +        sscanf(buf, "%x", &usbinfo->busnum); 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idVendor", busid); 
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
> +        sscanf(buf, "%x", &usbinfo->idVendor); 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idProduct", busid); 
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
> +        sscanf(buf, "%x", &usbinfo->idProduct); 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/manufacturer", busid); 
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) && 
> +        buflen > 0) { 
> +        /* replace \n to \0 */ 
> +        if (((char *)buf)[buflen - 1] == '\n') 
> +            ((char *)buf)[buflen - 1] = '\0'; 
> +        usbinfo->manuf = libxl__strdup(NOGC, buf); 
> +   } 
> + 
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/product", busid); 
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) && 
> +        buflen > 0) { 
> +        /* replace \n to \0 */ 
> +        if (((char *)buf)[buflen - 1] == '\n') 
> +            ((char *)buf)[buflen - 1] = '\0'; 
> +        usbinfo->prod = libxl__strdup(NOGC, buf); 
> +    } 
> + 
> +    rc = 0; 
> + 
> +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 23f27d4..5278d03 100644 
> --- a/tools/libxl/libxl_types.idl 
> +++ b/tools/libxl/libxl_types.idl 
> @@ -541,6 +541,28 @@ 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), 
> +    ("hostbus",   integer), 
> +    ("hostaddr",  integer), 
> +    ]) 
> + 
>  libxl_device_dtdev = Struct("device_dtdev", [ 
>      ("path", string), 
>      ]) 
> @@ -572,6 +594,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 +644,32 @@ 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), 
> +    ("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 dc5465e..a627662 100644 
> --- a/tools/libxl/libxl_utils.c 
> +++ b/tools/libxl/libxl_utils.c 
> @@ -1187,6 +1187,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 
> diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h 
> index 1c1761d..321df9d 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_usb(libxl_ctx *ctx, uint32_t domid, 
> +                                 int ctrl, int port, 
> +                                 libxl_device_usb *usb); 
>   
>  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] 30+ messages in thread

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-07-07  1:25   ` Chun Yan Liu
@ 2015-07-07  9:57     ` George Dunlap
  2015-08-06  3:11       ` Chun Yan Liu
  0 siblings, 1 reply; 30+ messages in thread
From: George Dunlap @ 2015-07-07  9:57 UTC (permalink / raw)
  To: Chun Yan Liu, xen-devel
  Cc: Juergen Gross, wei.liu2, ian.campbell, Ian.Jackson, Jim Fehlig,
	Simon Cao

On 07/07/2015 02:25 AM, Chun Yan Liu wrote:
> Any comments on the implementation? If there is anything improper, I can update.

Since we decided to wait until the next release, I've been prioritizing
reviewing patch series which might make it into 4.6.  I'll come back to
it once the freeze starts.

Believe me, I'd much rather be working on the USB stuff. :-)

 -George

> 
> Thanks,
> Chunyan
> 
>>>> On 6/25/2015 at 06:07 PM, in message
> <1435226838-3067-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: 
>>   - Use macros DEFINE_DEVICE_ADD and DEFINE_DEVICES_ADD for adding 
>>     usbctrl and usb, update all related codes. 
>>   - Use an extend macro DEFINE_DEVICE_REMOVE_EXT for removimg usbctrl
>>     update all related codes. 
>>   - Remove busid from libxl_device_usb definition, keep bus.addr only, 
>>     update all related codes. 
>>   - Remove documentation since it's mostly about design, move to 
>>     cover-letter. Some parts are moved to code comments. 
>>   - Address some other comments 
>>  
>>  tools/libxl/Makefile                 |    2 +- 
>>  tools/libxl/libxl.c                  |   53 ++ 
>>  tools/libxl/libxl.h                  |   64 ++ 
>>  tools/libxl/libxl_device.c           |    4 + 
>>  tools/libxl/libxl_internal.h         |   20 +- 
>>  tools/libxl/libxl_osdeps.h           |   13 + 
>>  tools/libxl/libxl_pvusb.c            | 1305  
>> ++++++++++++++++++++++++++++++++++ 
>>  tools/libxl/libxl_types.idl          |   50 ++ 
>>  tools/libxl/libxl_types_internal.idl |    1 + 
>>  tools/libxl/libxl_utils.c            |   16 + 
>>  tools/libxl/libxl_utils.h            |    5 + 
>>  11 files changed, 1531 insertions(+), 2 deletions(-) 
>>  create mode 100644 tools/libxl/libxl_pvusb.c 
>>  
>> 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 6570476..6542127 100644 
>> --- a/tools/libxl/libxl.c 
>> +++ b/tools/libxl/libxl.c 
>> @@ -4233,11 +4233,54 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) 
>>   
>>   
>> /**************************************************************************** 
>> **/ 
>>   
>> +/* Macro for defining device remove/destroy functions for usbctrl */ 
>> +/* Following functions are defined: 
>> + * libxl_device_usbctrl_remove 
>> + * libxl_device_usbctrl_destroy 
>> + */ 
>> + 
>> +#define DEFINE_DEVICE_REMOVE_EXT(type, removedestroy, f)                \ 
>> +    int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \ 
>> +        uint32_t domid, libxl_device_##type *type,                      \ 
>> +        const libxl_asyncop_how *ao_how)                                \ 
>> +    {                                                                   \ 
>> +        AO_CREATE(ctx, domid, ao_how);                                  \ 
>> +        libxl__device *device;                                          \ 
>> +        libxl__ao_device *aodev;                                        \ 
>> +        int rc;                                                         \ 
>> +                                                                        \ 
>> +        GCNEW(device);                                                  \ 
>> +        rc = libxl__device_from_##type(gc, domid, type, device);        \ 
>> +        if (rc != 0) goto out;                                          \ 
>> +                                                                        \ 
>> +        GCNEW(aodev);                                                   \ 
>> +        libxl__prepare_ao_device(ao, aodev);                            \ 
>> +        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;                    \ 
>> +        aodev->dev = device;                                            \ 
>> +        aodev->callback = device_addrm_aocomplete;                      \ 
>> +        aodev->force = f;                                               \ 
>> +        libxl__initiate_device_##type##_remove(egc, aodev);             \ 
>> +                                                                        \ 
>> +    out:                                                                \ 
>> +        if (rc) return AO_ABORT(rc);                                    \ 
>> +        return AO_INPROGRESS;                                           \ 
>> +    } 
>> + 
>> + 
>> +DEFINE_DEVICE_REMOVE_EXT(usbctrl, remove, 0) 
>> +DEFINE_DEVICE_REMOVE_EXT(usbctrl, destroy, 1) 
>> + 
>> +#undef DEFINE_DEVICE_REMOVE_EXT 
>> + 
>> +/************************************************************************** 
>> ****/ 
>> + 
>>  /* Macro for defining device addition functions in a compact way */ 
>>  /* The following functions are defined: 
>>   * libxl_device_disk_add 
>>   * libxl_device_nic_add 
>>   * libxl_device_vtpm_add 
>> + * libxl_device_usbctrl_add 
>> + * libxl_device_usb_add 
>>   */ 
>>   
>>  #define DEFINE_DEVICE_ADD(type)                                         \ 
>> @@ -4269,6 +4312,12 @@ DEFINE_DEVICE_ADD(nic) 
>>  /* vtpm */ 
>>  DEFINE_DEVICE_ADD(vtpm) 
>>   
>> +/* usbctrl */ 
>> +DEFINE_DEVICE_ADD(usbctrl) 
>> + 
>> +/* usb */ 
>> +DEFINE_DEVICE_ADD(usb) 
>> + 
>>  #undef DEFINE_DEVICE_ADD 
>>   
>>   
>> /**************************************************************************** 
>> **/ 
>> @@ -6770,6 +6819,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..6669835 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,53 @@ 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); 
>> + 
>> +/* 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, 
>> +                                  libxl_devid 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); 
>> + 
>>  /* 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 93bb41e..9869a21 100644 
>> --- a/tools/libxl/libxl_device.c 
>> +++ b/tools/libxl/libxl_device.c 
>> @@ -676,6 +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; 
>> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) { 
>> +                    libxl__initiate_device_usbctrl_remove(egc, aodev); 
>> +                    continue; 
>> +                } 
>>                  libxl__initiate_device_remove(egc, aodev); 
>>              } 
>>          } 
>> diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h 
>> index 0965e08..6a8d4ee 100644 
>> --- a/tools/libxl/libxl_internal.h 
>> +++ b/tools/libxl/libxl_internal.h 
>> @@ -2438,6 +2438,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_usb_add(libxl__egc *egc, uint32_t domid, 
>> +                                   libxl_device_usb *usb, 
>> +                                   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); 
>> @@ -2470,6 +2478,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. 
>> @@ -3645,7 +3660,10 @@ 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)->hostbus == (b)->hostbus &&  \ 
>> +                           (a)->hostaddr == (b)->hostaddr) 
>> +#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..d8cacd4 
>> --- /dev/null 
>> +++ b/tools/libxl/libxl_pvusb.c 
>> @@ -0,0 +1,1305 @@ 
>> +/* 
>> + * 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 
>> + 
>> +/* Utility to read backend xenstore keys */ 
>> +#define READ_BACKEND(tgc, subpath)                                    \ 
>> +            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath,  
>> be_path)) 
>> + 
>> +/* Utility to read frontend xenstore keys */ 
>> +#define READ_FRONTEND(tgc, subpath)                                   \ 
>> +            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath,  
>> fe_path)) 
>> + 
>> +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; 
>> +} 
>> + 
>> +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", "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(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", "1"); 
>> + 
>> +    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. 
>> + */ 
>> +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; 
>> +        } 
>> +    } 
>> + 
>> +    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); 
>> + 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    aodev->rc = rc; 
>> +    if (rc) aodev->callback(egc, aodev); 
>> +    return; 
>> +} 
>> + 
>> +static int 
>> +libxl__device_usb_list_per_usbctrl(libxl__gc *gc, uint32_t domid, 
>> +                                   libxl_devid usbctrl, 
>> +                                   libxl_device_usb **usbs, int *num); 
>> + 
>> +static int 
>> +libxl__device_usb_remove(libxl__gc *gc, uint32_t domid, libxl_device_usb  
>> *usb); 
>> + 
>> +int libxl_devid_to_device_usbctrl(libxl_ctx *ctx, 
>> +                                  uint32_t domid, 
>> +                                  int devid, 
>> +                                  libxl_device_usbctrl *usbctrl) 
>> +{ 
>> +    GC_INIT(ctx); 
>> +    libxl_device_usbctrl *usbctrls; 
>> +    int nb = 0; 
>> +    int i, rc = -1; 
>> + 
>> +    usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb); 
>> +    if (!nb) goto out; 
>> + 
>> +    libxl_device_usbctrl_init(usbctrl); 
>> +    for (i = 0; i < nb; i++) { 
>> +        if (devid == usbctrls[i].devid) { 
>> +            *usbctrl = usbctrls[i]; 
>> +            rc = 0; 
>> +            break; 
>> +        } 
>> +    } 
>> + 
>> +    libxl_device_usbctrl_list_free(usbctrls, nb); 
>> + 
>> +out: 
>> +    GC_FREE; 
>> +    return rc; 
>> +} 
>> + 
>> +/* 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 
>> + */ 
>> +void libxl__initiate_device_usbctrl_remove(libxl__egc *egc, 
>> +                                           libxl__ao_device *aodev) 
>> +{ 
>> +    STATE_AO_GC(aodev->ao); 
>> +    libxl_device_usb *usbs = NULL; 
>> +    int numusb = 0; 
>> +    int i, rc; 
>> +    uint32_t domid = ao->domid; 
>> +    int usbctrl_devid = aodev->dev->devid; 
>> + 
>> +    /* 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 (libxl__device_usb_remove(gc, domid, &usbs[i])) { 
>> +            LOG(ERROR, "libxl__device_usb_remove failed"); 
>> +            rc = ERROR_FAIL; 
>> +            goto out; 
>> +        } 
>> +    } 
>> + 
>> +    libxl_device_usb_list_free(usbs, numusb); 
>> + 
>> +    /* Remove usbctrl */ 
>> +    return libxl__initiate_device_remove(egc, aodev); 
>> + 
>> +out: 
>> +    libxl_device_usb_list_free(usbs, numusb); 
>> +    aodev->rc = rc; 
>> +    if (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 **dir = NULL; 
>> +    unsigned int ndirs = 0; 
>> + 
>> +    *num = 0; 
>> + 
>> +    path = GCSPRINTF("%s/device/vusb", 
>> +                     libxl__xs_get_dompath(gc, domid)); 
>> +    dir = libxl__xs_directory(gc, XBT_NULL, path, &ndirs); 
>> + 
>> +    if (dir && ndirs) { 
>> +        usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * ndirs); 
>> +        libxl_device_usbctrl* usbctrl; 
>> +        libxl_device_usbctrl* end = usbctrls + ndirs; 
>> +        for (usbctrl = usbctrls; usbctrl < end; usbctrl++, dir++, (*num)++)  
>> { 
>> +            const char *tmp, *be_path; 
>> +            const char *fe_path = GCSPRINTF("%s/%s", path, *dir); 
>> + 
>> +            libxl_device_usbctrl_init(usbctrl); 
>> +            usbctrl->devid = atoi(*dir); 
>> + 
>> +            be_path = READ_FRONTEND(gc, "backend"); 
>> +            if (!be_path) goto outerr; 
>> + 
>> +            tmp = READ_FRONTEND(gc, "backend-id"); 
>> +            if (!tmp) goto outerr; 
>> +            usbctrl->backend_domid = atoi(tmp); 
>> + 
>> +            tmp = READ_BACKEND(gc, "usb-ver"); 
>> +            if (!tmp) goto outerr; 
>> +            usbctrl->version = atoi(tmp); 
>> + 
>> +            tmp = READ_BACKEND(gc, "num-ports"); 
>> +            if (!tmp) goto outerr; 
>> +            usbctrl->ports = atoi(tmp); 
>> + 
>> +            tmp = READ_BACKEND(gc, "type"); 
>> +            if (!tmp) goto outerr; 
>> +            libxl_usb_protocol_from_string(tmp, &usbctrl->protocol); 
>> +        } 
>> +    } 
>> + 
>> +    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; 
>> +    const char *fe_path, *be_path, *tmp; 
>> +    int rc = 0; 
>> + 
>> +    usbctrlinfo->devid = usbctrl->devid; 
>> + 
>> +    dompath = libxl__xs_get_dompath(gc, domid); 
>> +    fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrlinfo->devid); 
>> +    be_path = READ_FRONTEND(gc, "backend"); 
>> +    if (!be_path) { 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> +    usbctrlinfo->backend = libxl__strdup(NOGC, be_path); 
>> + 
>> +    tmp = READ_FRONTEND(gc, "backend-id"); 
>> +    usbctrlinfo->backend_id = tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_FRONTEND(gc, "state"); 
>> +    usbctrlinfo->state = tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_FRONTEND(gc, "event-channel"); 
>> +    usbctrlinfo->evtch = tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_FRONTEND(gc, "urb-ring-ref"); 
>> +    usbctrlinfo->ref_urb = tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_FRONTEND(gc, "conn-ring-ref"); 
>> +    usbctrlinfo->ref_conn= tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_BACKEND(gc, "frontend"); 
>> +    usbctrlinfo->frontend = libxl__strdup(NOGC, tmp); 
>> + 
>> +    tmp = READ_BACKEND(gc, "frontend-id"); 
>> +    usbctrlinfo->frontend_id = tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_BACKEND(gc, "num-ports"); 
>> +    usbctrlinfo->ports = tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_BACKEND(gc, "usb-ver"); 
>> +    usbctrlinfo->version = tmp ? strtoul(tmp, NULL, 10) : -1; 
>> + 
>> +    tmp = READ_BACKEND(gc, "type"); 
>> +    libxl_usb_protocol_from_string(tmp, &usbctrlinfo->protocol); 
>> + 
>> +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/busnum", busid); 
>> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
>> +        sscanf(buf, "%x", bus); 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", 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_busaddr_from_busid(gc, 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; 
>> +    char *busid = NULL; 
>> + 
>> +    assert(usb->hostbus > 0 && usb->hostaddr > 0); 
>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", 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, 
>> +                                   libxl_devid 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_busaddr_from_busid(gc, 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]; 
>> +            (*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, 
>> +                                  libxl_devid 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; 
>> +} 
>> + 
>> +/* 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_usb_setdefault(libxl__gc *gc, uint32_t domid, 
>> +                                        libxl_device_usb *usb, 
>> +                                        bool update_json) 
>> +{ 
>> +    int rc = -1; 
>> +    char *be_path, *tmp; 
>> + 
>> +    if (usb->ctrl == -1) { 
>> +        if (usb->port) { 
>> +            LOG(ERROR, "USB controller must be specified if you specify  
>> port ID"); 
>> +            return ERROR_INVAL; 
>> +        } 
>> + 
>> +        rc = libxl__device_usb_set_default_usbctrl(gc, domid, usb); 
>> +        /* 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) { 
>> +                    goto out; 
>> +                } 
>> +            } 
>> + 
>> +            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl, 
>> +                                                    update_json); 
>> +            if (rc) goto out; 
>> + 
>> +            usb->ctrl = usbctrl->devid; 
>> +            usb->port = 1; 
>> +        } 
>> +    } else if (!usb->port) { 
>> +        /* Valid port starts from 1. Choose port for us. */ 
>> +        int i, ports; 
>> + 
>> +        be_path = GCSPRINTF("%s/backend/vusb/%d/%d", 
>> +                            libxl__xs_get_dompath(gc,  
>> LIBXL_TOOLSTACK_DOMID), 
>> +                            domid, usb->ctrl); 
>> +        tmp = READ_BACKEND(gc, "num-ports"); 
>> +        ports = tmp ? atoi(tmp) : 0; 
>> +        for (i = 0; i < ports; i++) { 
>> +            tmp = libxl__xs_read(gc, XBT_NULL, 
>> +                                 GCSPRINTF("%s/port/%d", be_path, i + 1)); 
>> +            if (tmp && !strcmp(tmp, "")) { 
>> +                usb->port = i + 1; 
>> +                break; 
>> +            } 
>> +        } 
>> + 
>> +        if (!usb->port) { 
>> +            LOG(ERROR, "No available port under specified controller"); 
>> +            goto out; 
>> +        } 
>> +    } else { 
>> +        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"); 
>> +            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_usb_add_xenstore(libxl__gc *gc, uint32_t domid, 
>> +                                          libxl_device_usb *usb, 
>> +                                          bool update_json) 
>> +{ 
>> +    char *be_path; 
>> +    char *busid; 
>> +    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); 
>> + 
>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->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(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, 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_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; 
>> +    char *busid; 
>> +    int rc = 0; 
>> + 
>> +    *intfs = NULL; 
>> +    *num = 0; 
>> + 
>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
>> +    if (!busid) { 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    buf = GCSPRINTF("%s:", 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; 
>> +} 
>> + 
>> +/* 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(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 USB device, check each interface, 
>> + * unbind from "usbback" driver and rebind to its 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 *busid; 
>> +    char *usb_encode = NULL; 
>> + 
>> +    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0) 
>> +        return ERROR_FAIL; 
>> + 
>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
>> +    usb_encode = usb_interface_xenstore_encode(busid); 
>> + 
>> +    for (i = 0; i < num; i++) { 
>> +        char *intf = intfs[i]; 
>> +        char *drvpath = NULL; 
>> + 
>> +        /* check if the USB interface is already bound to "usbbcak" */ 
>> +        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 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 *busid; 
>> +    char *usb_encode = NULL; 
>> + 
>> +    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0) 
>> +        return ERROR_FAIL; 
>> + 
>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
>> +    usb_encode = usb_interface_xenstore_encode(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; 
>> +} 
>> + 
>> +/* 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. 
>> + */ 
>> +void libxl__device_usb_add(libxl__egc *egc, uint32_t domid, 
>> +                           libxl_device_usb *usb, 
>> +                           libxl__ao_device *aodev) 
>> +{ 
>> +    STATE_AO_GC(aodev->ao); 
>> +    int rc = -1; 
>> +    char *busid = NULL; 
>> + 
>> +    assert(usb->hostbus > 0 && usb->hostaddr > 0); 
>> + 
>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
>> +    if (!busid) { 
>> +        LOG(ERROR, "USB device doesn't exist in sysfs"); 
>> +        goto out; 
>> +    } 
>> + 
>> +    if (!is_usb_assignable(gc, usb)) { 
>> +        LOG(ERROR, "USB device is not assignable."); 
>> +        goto out; 
>> +    } 
>> + 
>> +    /* check usb device is already assigned */ 
>> +    if (is_usb_assigned(gc, usb)) { 
>> +        LOG(ERROR, "USB device is already attached to a domain."); 
>> +        goto out; 
>> +    } 
>> + 
>> +    rc = libxl__device_usb_setdefault(gc, domid, usb, aodev->update_json); 
>> +    if (rc) goto out; 
>> + 
>> +    rc = libxl__device_usb_add_xenstore(gc, domid, usb, aodev->update_json); 
>> +    if (rc) goto out; 
>> + 
>> +    rc = usbback_dev_assign(gc, usb); 
>> +    if (rc) { 
>> +        libxl__device_usb_remove_xenstore(gc, domid, usb); 
>> +        goto out; 
>> +    } 
>> + 
>> +    libxl__ao_complete(egc, ao, 0); 
>> +    rc = 0; 
>> + 
>> +out: 
>> +    aodev->rc = rc; 
>> +    if (rc) aodev->callback(egc, aodev); 
>> +    return; 
>> +} 
>> + 
>> +int libxl_ctrlport_to_device_usb(libxl_ctx *ctx, 
>> +                                 uint32_t domid, 
>> +                                 int ctrl, 
>> +                                 int port, 
>> +                                 libxl_device_usb *usb) 
>> +{ 
>> +    GC_INIT(ctx); 
>> +    char *dompath, *be_path, *busid; 
>> +    int rc = ERROR_FAIL; 
>> + 
>> +    dompath = libxl__xs_get_dompath(gc, domid); 
>> +    if (!dompath) 
>> +        goto out; 
>> + 
>> +    be_path = libxl__xs_read(gc, XBT_NULL, 
>> +                  GCSPRINTF("%s/device/vusb/%d/backend", dompath, ctrl)); 
>> +    if (!be_path) 
>> +        goto out; 
>> + 
>> +    busid = libxl__xs_read(gc, XBT_NULL, 
>> +                           GCSPRINTF("%s/port/%d", be_path, port)); 
>> +    if (busid && strcmp(busid, "")) { 
>> +        usb->ctrl = ctrl; 
>> +        usb->port = port; 
>> +        usb_busaddr_from_busid(gc, busid, &usb->hostbus, &usb->hostaddr); 
>> +        rc = 0; 
>> +    } 
>> + 
>> +out: 
>> +    GC_FREE; 
>> +    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_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; 
>> +} 
>> + 
>> +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_getinfo(libxl_ctx *ctx, libxl_device_usb *usb, 
>> +                             libxl_usbinfo *usbinfo) 
>> +{ 
>> +    GC_INIT(ctx); 
>> +    char *filename; 
>> +    char *busid; 
>> +    void *buf = NULL; 
>> +    int buflen, rc; 
>> + 
>> +    usbinfo->ctrl = usb->ctrl; 
>> +    usbinfo->port = usb->port; 
>> + 
>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr); 
>> +    if (!busid) { 
>> +        rc = ERROR_FAIL; 
>> +        goto out; 
>> +    } 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid); 
>> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
>> +        sscanf(buf, "%x", &usbinfo->devnum); 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid); 
>> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
>> +        sscanf(buf, "%x", &usbinfo->busnum); 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idVendor", busid); 
>> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
>> +        sscanf(buf, "%x", &usbinfo->idVendor); 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idProduct", busid); 
>> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL)) 
>> +        sscanf(buf, "%x", &usbinfo->idProduct); 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/manufacturer", busid); 
>> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) && 
>> +        buflen > 0) { 
>> +        /* replace \n to \0 */ 
>> +        if (((char *)buf)[buflen - 1] == '\n') 
>> +            ((char *)buf)[buflen - 1] = '\0'; 
>> +        usbinfo->manuf = libxl__strdup(NOGC, buf); 
>> +   } 
>> + 
>> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/product", busid); 
>> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) && 
>> +        buflen > 0) { 
>> +        /* replace \n to \0 */ 
>> +        if (((char *)buf)[buflen - 1] == '\n') 
>> +            ((char *)buf)[buflen - 1] = '\0'; 
>> +        usbinfo->prod = libxl__strdup(NOGC, buf); 
>> +    } 
>> + 
>> +    rc = 0; 
>> + 
>> +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 23f27d4..5278d03 100644 
>> --- a/tools/libxl/libxl_types.idl 
>> +++ b/tools/libxl/libxl_types.idl 
>> @@ -541,6 +541,28 @@ 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), 
>> +    ("hostbus",   integer), 
>> +    ("hostaddr",  integer), 
>> +    ]) 
>> + 
>>  libxl_device_dtdev = Struct("device_dtdev", [ 
>>      ("path", string), 
>>      ]) 
>> @@ -572,6 +594,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 +644,32 @@ 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), 
>> +    ("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 dc5465e..a627662 100644 
>> --- a/tools/libxl/libxl_utils.c 
>> +++ b/tools/libxl/libxl_utils.c 
>> @@ -1187,6 +1187,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 
>> diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h 
>> index 1c1761d..321df9d 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_usb(libxl_ctx *ctx, uint32_t domid, 
>> +                                 int ctrl, int port, 
>> +                                 libxl_device_usb *usb); 
>>   
>>  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] 30+ messages in thread

* Re: [PATCH V5 7/7] domcreate: support pvusb in configuration file
  2015-06-25 10:07 ` [PATCH V5 7/7] domcreate: support pvusb in configuration file Chunyan Liu
@ 2015-07-13 10:08   ` Juergen Gross
  2015-08-06  2:49     ` Chun Yan Liu
  0 siblings, 1 reply; 30+ messages in thread
From: Juergen Gross @ 2015-07-13 10:08 UTC (permalink / raw)
  To: Chunyan Liu, xen-devel
  Cc: wei.liu2, ian.campbell, george.dunlap, Ian.Jackson, jfehlig, Simon Cao

On 06/25/2015 12:07 PM, Chunyan Liu 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=['2.1,controller=0,port=1', ]
>
> Signed-off-by: Chunyan Liu <cyliu@suse.com>
> Signed-off-by: Simon Cao <caobosimon@gmail.com>

I tested this patch and it still isn't working:

config file:

usbctrl = [ 'version=2,ports=4', ]
usbdev = [ '3.4,controller=0,port=1', ]

xl create output:

xl: libxl_event.c:1759: libxl__ao_inprogress_gc: Assertion 
`!ao->complete' failed.

I'm not sure, but comparing e.g. libxl__device_disk_add() with
libxl__device_usb_add() shows a significant difference regarding passing
of ao data.

I think you'll need a wrapper as in the disk case and pass NULL for ao
to the attach function being capable of async operation when you are
calling the attach function during domain creation.


Juergen

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

* Re: [PATCH V5 0/7] xen pvusb toolstack work
  2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
                   ` (6 preceding siblings ...)
  2015-06-25 10:07 ` [PATCH V5 7/7] domcreate: support pvusb in configuration file Chunyan Liu
@ 2015-07-13 10:09 ` Juergen Gross
  7 siblings, 0 replies; 30+ messages in thread
From: Juergen Gross @ 2015-07-13 10:09 UTC (permalink / raw)
  To: Chunyan Liu, xen-devel
  Cc: george.dunlap, Ian.Jackson, jfehlig, wei.liu2, ian.campbell

On 06/25/2015 12:07 PM, Chunyan Liu wrote:
> 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.

Patches 1-6:

Tested-by: Juergen Gross <jgross@suse.com>


Juergen

>
> Changes to v4:
> * use DEFINE_DEVICE_ADD and DEFINE_DEVICES_ADD to handle usbctrl adding
>    and usb adding, define extended macro DEFINE_DEVICE_REMOVE_EXT to handle
>    usbctrl remove.
> * Change interfaces:
>    libxl_device_usb only includes bus.addr, removing busid.
>    'xl usb-detach' uses <ctrl,port> to specify usb device instead of bus.addr.
>    Adjusting all related codes.
> * Other changes addring all other comments in v4.
>
> V3 is here:
> 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_read_file_contents: add new entry to read sysfs file
>    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                |   75 ++
>   docs/man/xl.pod.1                    |   40 +
>   tools/libxl/Makefile                 |    2 +-
>   tools/libxl/libxl.c                  |   57 +-
>   tools/libxl/libxl.h                  |   67 ++
>   tools/libxl/libxl_create.c           |   73 +-
>   tools/libxl/libxl_device.c           |    8 +
>   tools/libxl/libxl_internal.h         |   33 +-
>   tools/libxl/libxl_osdeps.h           |   13 +
>   tools/libxl/libxl_pvusb.c            | 1358 ++++++++++++++++++++++++++++++++++
>   tools/libxl/libxl_types.idl          |   50 ++
>   tools/libxl/libxl_types_internal.idl |    1 +
>   tools/libxl/libxl_utils.c            |   53 +-
>   tools/libxl/libxl_utils.h            |    5 +
>   tools/libxl/xl.h                     |    6 +
>   tools/libxl/xl_cmdimpl.c             |  401 +++++++++-
>   tools/libxl/xl_cmdtable.c            |   29 +
>   17 files changed, 2255 insertions(+), 16 deletions(-)
>   create mode 100644 tools/libxl/libxl_pvusb.c
>

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-06-25 10:07 ` [PATCH V5 3/7] libxl: add pvusb API Chunyan Liu
  2015-06-25 11:15   ` Ian Jackson
  2015-07-07  1:25   ` Chun Yan Liu
@ 2015-07-16 15:01   ` Juergen Gross
  2 siblings, 0 replies; 30+ messages in thread
From: Juergen Gross @ 2015-07-16 15:01 UTC (permalink / raw)
  To: Chunyan Liu, xen-devel
  Cc: wei.liu2, ian.campbell, george.dunlap, Ian.Jackson, jfehlig, Simon Cao

On 06/25/2015 12:07 PM, Chunyan Liu 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>

Sorry, found another error: You changed too many format specifiers
from "%d" to "%x":

...

> +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);

That's a decimal number. Use %d, please.

> +
> +        filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", de->d_name);
> +        if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +            sscanf(buf, "%x", &busnum);

Same here.

> +
> +        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/busnum", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +        sscanf(buf, "%x", bus);

And here.

> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +        sscanf(buf, "%x", addr);

And here.

> +}

...

> +
> +/* 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;
> +    char *busid = NULL;
> +
> +    assert(usb->hostbus > 0 && usb->hostaddr > 0);
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", busid);
> +    if (libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +        return false;
> +
> +    sscanf(buf, "%x", &classcode);

This one, too.

> +    return classcode != USBHUB_CLASS_CODE;
> +}

...

> +int libxl_device_usb_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,
> +                             libxl_usbinfo *usbinfo)
> +{
> +    GC_INIT(ctx);
> +    char *filename;
> +    char *busid;
> +    void *buf = NULL;
> +    int buflen, rc;
> +
> +    usbinfo->ctrl = usb->ctrl;
> +    usbinfo->port = usb->port;
> +
> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);
> +    if (!busid) {
> +        rc = ERROR_FAIL;
> +        goto out;
> +    }
> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +        sscanf(buf, "%x", &usbinfo->devnum);

Again.

> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +        sscanf(buf, "%x", &usbinfo->busnum);

And here.

> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idVendor", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +        sscanf(buf, "%x", &usbinfo->idVendor);

This one is correct!

> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idProduct", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))
> +        sscanf(buf, "%x", &usbinfo->idProduct);

Correct, too.

> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/manufacturer", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) &&
> +        buflen > 0) {
> +        /* replace \n to \0 */
> +        if (((char *)buf)[buflen - 1] == '\n')
> +            ((char *)buf)[buflen - 1] = '\0';
> +        usbinfo->manuf = libxl__strdup(NOGC, buf);
> +   }
> +
> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/product", busid);
> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) &&
> +        buflen > 0) {
> +        /* replace \n to \0 */
> +        if (((char *)buf)[buflen - 1] == '\n')
> +            ((char *)buf)[buflen - 1] = '\0';
> +        usbinfo->prod = libxl__strdup(NOGC, buf);
> +    }
> +
> +    rc = 0;
> +
> +out:
> +    GC_FREE;
> +    return rc;
> +}


Juergen

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

* Re: [PATCH V5 7/7] domcreate: support pvusb in configuration file
  2015-07-13 10:08   ` Juergen Gross
@ 2015-08-06  2:49     ` Chun Yan Liu
  0 siblings, 0 replies; 30+ messages in thread
From: Chun Yan Liu @ 2015-08-06  2:49 UTC (permalink / raw)
  To: xen-devel, Juergen Gross
  Cc: wei.liu2, ian.campbell, george.dunlap, Ian.Jackson, Jim Fehlig,
	Simon Cao



>>> On 7/13/2015 at 06:08 PM, in message <55A38E2E.60509@suse.com>, Juergen Gross
<jgross@suse.com> wrote: 
> On 06/25/2015 12:07 PM, Chunyan Liu 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=['2.1,controller=0,port=1', ] 
> > 
> > Signed-off-by: Chunyan Liu <cyliu@suse.com> 
> > Signed-off-by: Simon Cao <caobosimon@gmail.com> 
>  
> I tested this patch and it still isn't working: 
>  
> config file: 
>  
> usbctrl = [ 'version=2,ports=4', ] 
> usbdev = [ '3.4,controller=0,port=1', ] 
>  
> xl create output: 
>  
> xl: libxl_event.c:1759: libxl__ao_inprogress_gc: Assertion  
> `!ao->complete' failed. 

There is a small fix about error handling after the testing version I sent to
you, already included in the version posted here. With this version patch,
this assertion problem should not exist.

But as I ever mentioned before in V2, domain create with pvusb device in
configuration file always fails. As I remembered the test with xend toolstack
also fails in this case. I'm not sure the problem is in driver or in toolstack.

>  
> I'm not sure, but comparing e.g. libxl__device_disk_add() with 
> libxl__device_usb_add() shows a significant difference regarding passing 
> of ao data. 

usb_add and disk_add do have something different. usb_add doesn't create
new device in xenstore, it doesn't need to do wait_for_device_connection which 
waits for frontend/backend driver status change, so after adding usb device entry
to xenstore, we can call libxl__ao_complete() directly.

- Chunyan

> I think you'll need a wrapper as in the disk case and pass NULL for ao 
> to the attach function being capable of async operation when you are 
> calling the attach function during domain creation. 
>  
>  
> Juergen 
>  

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-07-07  9:57     ` George Dunlap
@ 2015-08-06  3:11       ` Chun Yan Liu
  2015-08-06 17:21         ` George Dunlap
  0 siblings, 1 reply; 30+ messages in thread
From: Chun Yan Liu @ 2015-08-06  3:11 UTC (permalink / raw)
  To: George Dunlap, xen-devel
  Cc: Juergen Gross, wei.liu2, ian.campbell, Ian.Jackson, Jim Fehlig,
	Simon Cao

As 4.6 goes to bug fixing stage, maybe we can pick up this thread? :-)

Beside to call for your precious review comments and suggestions so that we can
make progress, I also want to confirm about the previous discussed two TODO
things:
1) use UDEV name rule to specify usb device uniquely even across reboot. That
    got consensus. Next thing is exposing that name to some sysfs entry, right?
2) use libusb instead of reading sysfs by ourselves. As George mentioned, using
    libusb is not simpler than reading sysfs; and if UDEV name is stored to some
    sysfs entry for us to retrieve, then we still need reading sysfs things. Could we
    get to a final decision?
If these are settled down, I can update related code.

Thanks,
Chunyan

>>> On 7/7/2015 at 05:57 PM, in message <559BA270.4000006@eu.citrix.com>, George
Dunlap <george.dunlap@eu.citrix.com> wrote: 
> On 07/07/2015 02:25 AM, Chun Yan Liu wrote: 
> > Any comments on the implementation? If there is anything improper, I can  
> update. 
>  
> Since we decided to wait until the next release, I've been prioritizing 
> reviewing patch series which might make it into 4.6.  I'll come back to 
> it once the freeze starts. 
>  
> Believe me, I'd much rather be working on the USB stuff. :-) 
>  
>  -George 
>  
> >  
> > Thanks, 
> > Chunyan 
> >  
> >>>> On 6/25/2015 at 06:07 PM, in message 
> > <1435226838-3067-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:  
> >>   - Use macros DEFINE_DEVICE_ADD and DEFINE_DEVICES_ADD for adding  
> >>     usbctrl and usb, update all related codes.  
> >>   - Use an extend macro DEFINE_DEVICE_REMOVE_EXT for removimg usbctrl 
> >>     update all related codes.  
> >>   - Remove busid from libxl_device_usb definition, keep bus.addr only,  
> >>     update all related codes.  
> >>   - Remove documentation since it's mostly about design, move to  
> >>     cover-letter. Some parts are moved to code comments.  
> >>   - Address some other comments  
> >>   
> >>  tools/libxl/Makefile                 |    2 +-  
> >>  tools/libxl/libxl.c                  |   53 ++  
> >>  tools/libxl/libxl.h                  |   64 ++  
> >>  tools/libxl/libxl_device.c           |    4 +  
> >>  tools/libxl/libxl_internal.h         |   20 +-  
> >>  tools/libxl/libxl_osdeps.h           |   13 +  
> >>  tools/libxl/libxl_pvusb.c            | 1305   
> >> ++++++++++++++++++++++++++++++++++  
> >>  tools/libxl/libxl_types.idl          |   50 ++  
> >>  tools/libxl/libxl_types_internal.idl |    1 +  
> >>  tools/libxl/libxl_utils.c            |   16 +  
> >>  tools/libxl/libxl_utils.h            |    5 +  
> >>  11 files changed, 1531 insertions(+), 2 deletions(-)  
> >>  create mode 100644 tools/libxl/libxl_pvusb.c  
> >>   
> >> 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 6570476..6542127 100644  
> >> --- a/tools/libxl/libxl.c  
> >> +++ b/tools/libxl/libxl.c  
> >> @@ -4233,11 +4233,54 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)  
> >>    
> >>    
> >>  
> /****************************************************************************  
>  
> >> **/  
> >>    
> >> +/* Macro for defining device remove/destroy functions for usbctrl */  
> >> +/* Following functions are defined:  
> >> + * libxl_device_usbctrl_remove  
> >> + * libxl_device_usbctrl_destroy  
> >> + */  
> >> +  
> >> +#define DEFINE_DEVICE_REMOVE_EXT(type, removedestroy, f)                \  
> >> +    int libxl_device_##type##_##removedestroy(libxl_ctx *ctx,           \  
> >> +        uint32_t domid, libxl_device_##type *type,                      \  
> >> +        const libxl_asyncop_how *ao_how)                                \  
> >> +    {                                                                   \  
> >> +        AO_CREATE(ctx, domid, ao_how);                                  \  
> >> +        libxl__device *device;                                          \  
> >> +        libxl__ao_device *aodev;                                        \  
> >> +        int rc;                                                         \  
> >> +                                                                        \  
> >> +        GCNEW(device);                                                  \  
> >> +        rc = libxl__device_from_##type(gc, domid, type, device);        \  
> >> +        if (rc != 0) goto out;                                          \  
> >> +                                                                        \  
> >> +        GCNEW(aodev);                                                   \  
> >> +        libxl__prepare_ao_device(ao, aodev);                            \  
> >> +        aodev->action = LIBXL__DEVICE_ACTION_REMOVE;                    \  
> >> +        aodev->dev = device;                                            \  
> >> +        aodev->callback = device_addrm_aocomplete;                      \  
> >> +        aodev->force = f;                                               \  
> >> +        libxl__initiate_device_##type##_remove(egc, aodev);             \  
> >> +                                                                        \  
> >> +    out:                                                                \  
> >> +        if (rc) return AO_ABORT(rc);                                    \  
> >> +        return AO_INPROGRESS;                                           \  
> >> +    }  
> >> +  
> >> +  
> >> +DEFINE_DEVICE_REMOVE_EXT(usbctrl, remove, 0)  
> >> +DEFINE_DEVICE_REMOVE_EXT(usbctrl, destroy, 1)  
> >> +  
> >> +#undef DEFINE_DEVICE_REMOVE_EXT  
> >> +  
> >>  
> +/**************************************************************************  
> >> ****/  
> >> +  
> >>  /* Macro for defining device addition functions in a compact way */  
> >>  /* The following functions are defined:  
> >>   * libxl_device_disk_add  
> >>   * libxl_device_nic_add  
> >>   * libxl_device_vtpm_add  
> >> + * libxl_device_usbctrl_add  
> >> + * libxl_device_usb_add  
> >>   */  
> >>    
> >>  #define DEFINE_DEVICE_ADD(type)                                         \  
> >> @@ -4269,6 +4312,12 @@ DEFINE_DEVICE_ADD(nic)  
> >>  /* vtpm */  
> >>  DEFINE_DEVICE_ADD(vtpm)  
> >>    
> >> +/* usbctrl */  
> >> +DEFINE_DEVICE_ADD(usbctrl)  
> >> +  
> >> +/* usb */  
> >> +DEFINE_DEVICE_ADD(usb)  
> >> +  
> >>  #undef DEFINE_DEVICE_ADD  
> >>    
> >>    
> >>  
> /****************************************************************************  
>  
> >> **/  
> >> @@ -6770,6 +6819,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..6669835 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,53 @@ 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);  
> >> +  
> >> +/* 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,  
> >> +                                  libxl_devid 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);  
> >> +  
> >>  /* 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 93bb41e..9869a21 100644  
> >> --- a/tools/libxl/libxl_device.c  
> >> +++ b/tools/libxl/libxl_device.c  
> >> @@ -676,6 +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;  
> >> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) {  
> >> +                    libxl__initiate_device_usbctrl_remove(egc, aodev);  
> >> +                    continue;  
> >> +                }  
> >>                  libxl__initiate_device_remove(egc, aodev);  
> >>              }  
> >>          }  
> >> diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h  
> >> index 0965e08..6a8d4ee 100644  
> >> --- a/tools/libxl/libxl_internal.h  
> >> +++ b/tools/libxl/libxl_internal.h  
> >> @@ -2438,6 +2438,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_usb_add(libxl__egc *egc, uint32_t domid,  
> >> +                                   libxl_device_usb *usb,  
> >> +                                   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);  
> >> @@ -2470,6 +2478,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.  
> >> @@ -3645,7 +3660,10 @@ 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)->hostbus == (b)->hostbus &&  \  
> >> +                           (a)->hostaddr == (b)->hostaddr)  
> >> +#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..d8cacd4  
> >> --- /dev/null  
> >> +++ b/tools/libxl/libxl_pvusb.c  
> >> @@ -0,0 +1,1305 @@  
> >> +/*  
> >> + * 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  
> >> +  
> >> +/* Utility to read backend xenstore keys */  
> >> +#define READ_BACKEND(tgc, subpath)                                    \  
> >> +            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath,   
> >> be_path))  
> >> +  
> >> +/* Utility to read frontend xenstore keys */  
> >> +#define READ_FRONTEND(tgc, subpath)                                   \  
> >> +            libxl__xs_read(tgc, XBT_NULL, GCSPRINTF("%s/" subpath,   
> >> fe_path))  
> >> +  
> >> +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;  
> >> +}  
> >> +  
> >> +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", "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(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", "1");  
> >> +  
> >> +    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.  
> >> + */  
> >> +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;  
> >> +        }  
> >> +    }  
> >> +  
> >> +    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);  
> >> +  
> >> +    rc = 0;  
> >> +  
> >> +out:  
> >> +    aodev->rc = rc;  
> >> +    if (rc) aodev->callback(egc, aodev);  
> >> +    return;  
> >> +}  
> >> +  
> >> +static int  
> >> +libxl__device_usb_list_per_usbctrl(libxl__gc *gc, uint32_t domid,  
> >> +                                   libxl_devid usbctrl,  
> >> +                                   libxl_device_usb **usbs, int *num);  
> >> +  
> >> +static int  
> >> +libxl__device_usb_remove(libxl__gc *gc, uint32_t domid, libxl_device_usb   
> >> *usb);  
> >> +  
> >> +int libxl_devid_to_device_usbctrl(libxl_ctx *ctx,  
> >> +                                  uint32_t domid,  
> >> +                                  int devid,  
> >> +                                  libxl_device_usbctrl *usbctrl)  
> >> +{  
> >> +    GC_INIT(ctx);  
> >> +    libxl_device_usbctrl *usbctrls;  
> >> +    int nb = 0;  
> >> +    int i, rc = -1;  
> >> +  
> >> +    usbctrls = libxl_device_usbctrl_list(ctx, domid, &nb);  
> >> +    if (!nb) goto out;  
> >> +  
> >> +    libxl_device_usbctrl_init(usbctrl);  
> >> +    for (i = 0; i < nb; i++) {  
> >> +        if (devid == usbctrls[i].devid) {  
> >> +            *usbctrl = usbctrls[i];  
> >> +            rc = 0;  
> >> +            break;  
> >> +        }  
> >> +    }  
> >> +  
> >> +    libxl_device_usbctrl_list_free(usbctrls, nb);  
> >> +  
> >> +out:  
> >> +    GC_FREE;  
> >> +    return rc;  
> >> +}  
> >> +  
> >> +/* 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  
> >> + */  
> >> +void libxl__initiate_device_usbctrl_remove(libxl__egc *egc,  
> >> +                                           libxl__ao_device *aodev)  
> >> +{  
> >> +    STATE_AO_GC(aodev->ao);  
> >> +    libxl_device_usb *usbs = NULL;  
> >> +    int numusb = 0;  
> >> +    int i, rc;  
> >> +    uint32_t domid = ao->domid;  
> >> +    int usbctrl_devid = aodev->dev->devid;  
> >> +  
> >> +    /* 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 (libxl__device_usb_remove(gc, domid, &usbs[i])) {  
> >> +            LOG(ERROR, "libxl__device_usb_remove failed");  
> >> +            rc = ERROR_FAIL;  
> >> +            goto out;  
> >> +        }  
> >> +    }  
> >> +  
> >> +    libxl_device_usb_list_free(usbs, numusb);  
> >> +  
> >> +    /* Remove usbctrl */  
> >> +    return libxl__initiate_device_remove(egc, aodev);  
> >> +  
> >> +out:  
> >> +    libxl_device_usb_list_free(usbs, numusb);  
> >> +    aodev->rc = rc;  
> >> +    if (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 **dir = NULL;  
> >> +    unsigned int ndirs = 0;  
> >> +  
> >> +    *num = 0;  
> >> +  
> >> +    path = GCSPRINTF("%s/device/vusb",  
> >> +                     libxl__xs_get_dompath(gc, domid));  
> >> +    dir = libxl__xs_directory(gc, XBT_NULL, path, &ndirs);  
> >> +  
> >> +    if (dir && ndirs) {  
> >> +        usbctrls = libxl__zalloc(NOGC, sizeof(*usbctrls) * ndirs);  
> >> +        libxl_device_usbctrl* usbctrl;  
> >> +        libxl_device_usbctrl* end = usbctrls + ndirs;  
> >> +        for (usbctrl = usbctrls; usbctrl < end; usbctrl++, dir++, (*num)++)  
>   
> >> {  
> >> +            const char *tmp, *be_path;  
> >> +            const char *fe_path = GCSPRINTF("%s/%s", path, *dir);  
> >> +  
> >> +            libxl_device_usbctrl_init(usbctrl);  
> >> +            usbctrl->devid = atoi(*dir);  
> >> +  
> >> +            be_path = READ_FRONTEND(gc, "backend");  
> >> +            if (!be_path) goto outerr;  
> >> +  
> >> +            tmp = READ_FRONTEND(gc, "backend-id");  
> >> +            if (!tmp) goto outerr;  
> >> +            usbctrl->backend_domid = atoi(tmp);  
> >> +  
> >> +            tmp = READ_BACKEND(gc, "usb-ver");  
> >> +            if (!tmp) goto outerr;  
> >> +            usbctrl->version = atoi(tmp);  
> >> +  
> >> +            tmp = READ_BACKEND(gc, "num-ports");  
> >> +            if (!tmp) goto outerr;  
> >> +            usbctrl->ports = atoi(tmp);  
> >> +  
> >> +            tmp = READ_BACKEND(gc, "type");  
> >> +            if (!tmp) goto outerr;  
> >> +            libxl_usb_protocol_from_string(tmp, &usbctrl->protocol);  
> >> +        }  
> >> +    }  
> >> +  
> >> +    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;  
> >> +    const char *fe_path, *be_path, *tmp;  
> >> +    int rc = 0;  
> >> +  
> >> +    usbctrlinfo->devid = usbctrl->devid;  
> >> +  
> >> +    dompath = libxl__xs_get_dompath(gc, domid);  
> >> +    fe_path = GCSPRINTF("%s/device/vusb/%d", dompath, usbctrlinfo->devid);  
> >> +    be_path = READ_FRONTEND(gc, "backend");  
> >> +    if (!be_path) {  
> >> +        rc = ERROR_FAIL;  
> >> +        goto out;  
> >> +    }  
> >> +    usbctrlinfo->backend = libxl__strdup(NOGC, be_path);  
> >> +  
> >> +    tmp = READ_FRONTEND(gc, "backend-id");  
> >> +    usbctrlinfo->backend_id = tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_FRONTEND(gc, "state");  
> >> +    usbctrlinfo->state = tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_FRONTEND(gc, "event-channel");  
> >> +    usbctrlinfo->evtch = tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_FRONTEND(gc, "urb-ring-ref");  
> >> +    usbctrlinfo->ref_urb = tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_FRONTEND(gc, "conn-ring-ref");  
> >> +    usbctrlinfo->ref_conn= tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_BACKEND(gc, "frontend");  
> >> +    usbctrlinfo->frontend = libxl__strdup(NOGC, tmp);  
> >> +  
> >> +    tmp = READ_BACKEND(gc, "frontend-id");  
> >> +    usbctrlinfo->frontend_id = tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_BACKEND(gc, "num-ports");  
> >> +    usbctrlinfo->ports = tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_BACKEND(gc, "usb-ver");  
> >> +    usbctrlinfo->version = tmp ? strtoul(tmp, NULL, 10) : -1;  
> >> +  
> >> +    tmp = READ_BACKEND(gc, "type");  
> >> +    libxl_usb_protocol_from_string(tmp, &usbctrlinfo->protocol);  
> >> +  
> >> +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/busnum", busid);  
> >> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))  
> >> +        sscanf(buf, "%x", bus);  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", 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_busaddr_from_busid(gc, 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;  
> >> +    char *busid = NULL;  
> >> +  
> >> +    assert(usb->hostbus > 0 && usb->hostaddr > 0);  
> >> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/bDeviceClass", 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,  
> >> +                                   libxl_devid 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_busaddr_from_busid(gc, 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];  
> >> +            (*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,  
> >> +                                  libxl_devid 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;  
> >> +}  
> >> +  
> >> +/* 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_usb_setdefault(libxl__gc *gc, uint32_t domid,  
> >> +                                        libxl_device_usb *usb,  
> >> +                                        bool update_json)  
> >> +{  
> >> +    int rc = -1;  
> >> +    char *be_path, *tmp;  
> >> +  
> >> +    if (usb->ctrl == -1) {  
> >> +        if (usb->port) {  
> >> +            LOG(ERROR, "USB controller must be specified if you specify   
> >> port ID");  
> >> +            return ERROR_INVAL;  
> >> +        }  
> >> +  
> >> +        rc = libxl__device_usb_set_default_usbctrl(gc, domid, usb);  
> >> +        /* 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) {  
> >> +                    goto out;  
> >> +                }  
> >> +            }  
> >> +  
> >> +            rc = libxl__device_usbctrl_add_xenstore(gc, domid, usbctrl,  
> >> +                                                    update_json);  
> >> +            if (rc) goto out;  
> >> +  
> >> +            usb->ctrl = usbctrl->devid;  
> >> +            usb->port = 1;  
> >> +        }  
> >> +    } else if (!usb->port) {  
> >> +        /* Valid port starts from 1. Choose port for us. */  
> >> +        int i, ports;  
> >> +  
> >> +        be_path = GCSPRINTF("%s/backend/vusb/%d/%d",  
> >> +                            libxl__xs_get_dompath(gc,   
> >> LIBXL_TOOLSTACK_DOMID),  
> >> +                            domid, usb->ctrl);  
> >> +        tmp = READ_BACKEND(gc, "num-ports");  
> >> +        ports = tmp ? atoi(tmp) : 0;  
> >> +        for (i = 0; i < ports; i++) {  
> >> +            tmp = libxl__xs_read(gc, XBT_NULL,  
> >> +                                 GCSPRINTF("%s/port/%d", be_path, i + 1));  
>  
> >> +            if (tmp && !strcmp(tmp, "")) {  
> >> +                usb->port = i + 1;  
> >> +                break;  
> >> +            }  
> >> +        }  
> >> +  
> >> +        if (!usb->port) {  
> >> +            LOG(ERROR, "No available port under specified controller");  
> >> +            goto out;  
> >> +        }  
> >> +    } else {  
> >> +        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");  
> >> +            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_usb_add_xenstore(libxl__gc *gc, uint32_t domid,  
> >> +                                          libxl_device_usb *usb,  
> >> +                                          bool update_json)  
> >> +{  
> >> +    char *be_path;  
> >> +    char *busid;  
> >> +    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);  
> >> +  
> >> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->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(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, 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_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;  
> >> +    char *busid;  
> >> +    int rc = 0;  
> >> +  
> >> +    *intfs = NULL;  
> >> +    *num = 0;  
> >> +  
> >> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);  
> >> +    if (!busid) {  
> >> +        rc = ERROR_FAIL;  
> >> +        goto out;  
> >> +    }  
> >> +  
> >> +    buf = GCSPRINTF("%s:", 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;  
> >> +}  
> >> +  
> >> +/* 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(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 USB device, check each interface,  
> >> + * unbind from "usbback" driver and rebind to its 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 *busid;  
> >> +    char *usb_encode = NULL;  
> >> +  
> >> +    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0)  
> >> +        return ERROR_FAIL;  
> >> +  
> >> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);  
> >> +    usb_encode = usb_interface_xenstore_encode(busid);  
> >> +  
> >> +    for (i = 0; i < num; i++) {  
> >> +        char *intf = intfs[i];  
> >> +        char *drvpath = NULL;  
> >> +  
> >> +        /* check if the USB interface is already bound to "usbbcak" */  
> >> +        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 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 *busid;  
> >> +    char *usb_encode = NULL;  
> >> +  
> >> +    if (usb_get_all_interfaces(gc, usb, &intfs, &num) < 0)  
> >> +        return ERROR_FAIL;  
> >> +  
> >> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);  
> >> +    usb_encode = usb_interface_xenstore_encode(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;  
> >> +}  
> >> +  
> >> +/* 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.  
> >> + */  
> >> +void libxl__device_usb_add(libxl__egc *egc, uint32_t domid,  
> >> +                           libxl_device_usb *usb,  
> >> +                           libxl__ao_device *aodev)  
> >> +{  
> >> +    STATE_AO_GC(aodev->ao);  
> >> +    int rc = -1;  
> >> +    char *busid = NULL;  
> >> +  
> >> +    assert(usb->hostbus > 0 && usb->hostaddr > 0);  
> >> +  
> >> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);  
> >> +    if (!busid) {  
> >> +        LOG(ERROR, "USB device doesn't exist in sysfs");  
> >> +        goto out;  
> >> +    }  
> >> +  
> >> +    if (!is_usb_assignable(gc, usb)) {  
> >> +        LOG(ERROR, "USB device is not assignable.");  
> >> +        goto out;  
> >> +    }  
> >> +  
> >> +    /* check usb device is already assigned */  
> >> +    if (is_usb_assigned(gc, usb)) {  
> >> +        LOG(ERROR, "USB device is already attached to a domain.");  
> >> +        goto out;  
> >> +    }  
> >> +  
> >> +    rc = libxl__device_usb_setdefault(gc, domid, usb, aodev->update_json);  
> >> +    if (rc) goto out;  
> >> +  
> >> +    rc = libxl__device_usb_add_xenstore(gc, domid, usb, aodev->update_json);  
>  
> >> +    if (rc) goto out;  
> >> +  
> >> +    rc = usbback_dev_assign(gc, usb);  
> >> +    if (rc) {  
> >> +        libxl__device_usb_remove_xenstore(gc, domid, usb);  
> >> +        goto out;  
> >> +    }  
> >> +  
> >> +    libxl__ao_complete(egc, ao, 0);  
> >> +    rc = 0;  
> >> +  
> >> +out:  
> >> +    aodev->rc = rc;  
> >> +    if (rc) aodev->callback(egc, aodev);  
> >> +    return;  
> >> +}  
> >> +  
> >> +int libxl_ctrlport_to_device_usb(libxl_ctx *ctx,  
> >> +                                 uint32_t domid,  
> >> +                                 int ctrl,  
> >> +                                 int port,  
> >> +                                 libxl_device_usb *usb)  
> >> +{  
> >> +    GC_INIT(ctx);  
> >> +    char *dompath, *be_path, *busid;  
> >> +    int rc = ERROR_FAIL;  
> >> +  
> >> +    dompath = libxl__xs_get_dompath(gc, domid);  
> >> +    if (!dompath)  
> >> +        goto out;  
> >> +  
> >> +    be_path = libxl__xs_read(gc, XBT_NULL,  
> >> +                  GCSPRINTF("%s/device/vusb/%d/backend", dompath, ctrl));  
> >> +    if (!be_path)  
> >> +        goto out;  
> >> +  
> >> +    busid = libxl__xs_read(gc, XBT_NULL,  
> >> +                           GCSPRINTF("%s/port/%d", be_path, port));  
> >> +    if (busid && strcmp(busid, "")) {  
> >> +        usb->ctrl = ctrl;  
> >> +        usb->port = port;  
> >> +        usb_busaddr_from_busid(gc, busid, &usb->hostbus, &usb->hostaddr);  
> >> +        rc = 0;  
> >> +    }  
> >> +  
> >> +out:  
> >> +    GC_FREE;  
> >> +    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_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;  
> >> +}  
> >> +  
> >> +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_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,  
> >> +                             libxl_usbinfo *usbinfo)  
> >> +{  
> >> +    GC_INIT(ctx);  
> >> +    char *filename;  
> >> +    char *busid;  
> >> +    void *buf = NULL;  
> >> +    int buflen, rc;  
> >> +  
> >> +    usbinfo->ctrl = usb->ctrl;  
> >> +    usbinfo->port = usb->port;  
> >> +  
> >> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);  
> >> +    if (!busid) {  
> >> +        rc = ERROR_FAIL;  
> >> +        goto out;  
> >> +    }  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/devnum", busid);  
> >> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))  
> >> +        sscanf(buf, "%x", &usbinfo->devnum);  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/busnum", busid);  
> >> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))  
> >> +        sscanf(buf, "%x", &usbinfo->busnum);  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idVendor", busid);  
> >> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))  
> >> +        sscanf(buf, "%x", &usbinfo->idVendor);  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/idProduct", busid);  
> >> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, NULL))  
> >> +        sscanf(buf, "%x", &usbinfo->idProduct);  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/manufacturer", busid);  
> >> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) &&  
> >> +        buflen > 0) {  
> >> +        /* replace \n to \0 */  
> >> +        if (((char *)buf)[buflen - 1] == '\n')  
> >> +            ((char *)buf)[buflen - 1] = '\0';  
> >> +        usbinfo->manuf = libxl__strdup(NOGC, buf);  
> >> +   }  
> >> +  
> >> +    filename = GCSPRINTF(SYSFS_USB_DEV"/%s/product", busid);  
> >> +    if (!libxl_read_sysfs_file_contents(ctx, filename, &buf, &buflen) &&  
> >> +        buflen > 0) {  
> >> +        /* replace \n to \0 */  
> >> +        if (((char *)buf)[buflen - 1] == '\n')  
> >> +            ((char *)buf)[buflen - 1] = '\0';  
> >> +        usbinfo->prod = libxl__strdup(NOGC, buf);  
> >> +    }  
> >> +  
> >> +    rc = 0;  
> >> +  
> >> +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 23f27d4..5278d03 100644  
> >> --- a/tools/libxl/libxl_types.idl  
> >> +++ b/tools/libxl/libxl_types.idl  
> >> @@ -541,6 +541,28 @@ 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),  
> >> +    ("hostbus",   integer),  
> >> +    ("hostaddr",  integer),  
> >> +    ])  
> >> +  
> >>  libxl_device_dtdev = Struct("device_dtdev", [  
> >>      ("path", string),  
> >>      ])  
> >> @@ -572,6 +594,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 +644,32 @@ 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),  
> >> +    ("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 dc5465e..a627662 100644  
> >> --- a/tools/libxl/libxl_utils.c  
> >> +++ b/tools/libxl/libxl_utils.c  
> >> @@ -1187,6 +1187,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  
> >> diff --git a/tools/libxl/libxl_utils.h b/tools/libxl/libxl_utils.h  
> >> index 1c1761d..321df9d 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_usb(libxl_ctx *ctx, uint32_t domid,  
> >> +                                 int ctrl, int port,  
> >> +                                 libxl_device_usb *usb);  
> >>    
> >>  int libxl_bitmap_alloc(libxl_ctx *ctx, libxl_bitmap *bitmap, int n_bits);  
> >>      /* Allocated bimap is from malloc, libxl_bitmap_dispose() to be  
> >   
> >  
>  
>  
> _______________________________________________ 
> Xen-devel mailing list 
> Xen-devel@lists.xen.org 
> http://lists.xen.org/xen-devel 
>  
>  

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-08-06  3:11       ` Chun Yan Liu
@ 2015-08-06 17:21         ` George Dunlap
  2015-08-07  2:31           ` Chun Yan Liu
  2015-08-07  7:41           ` Chun Yan Liu
  0 siblings, 2 replies; 30+ messages in thread
From: George Dunlap @ 2015-08-06 17:21 UTC (permalink / raw)
  To: Chun Yan Liu, George Dunlap, xen-devel
  Cc: Juergen Gross, wei.liu2, ian.campbell, Ian.Jackson, Jim Fehlig,
	Simon Cao

On 08/06/2015 04:11 AM, Chun Yan Liu wrote:
> As 4.6 goes to bug fixing stage, maybe we can pick up this thread? :-)
> 
> Beside to call for your precious review comments and suggestions so that we can
> make progress, I also want to confirm about the previous discussed two TODO
> things:
> 1) use UDEV name rule to specify usb device uniquely even across reboot. That
>     got consensus. Next thing is exposing that name to some sysfs entry, right?

So just to be clear, my understanding of the plan was that we try to fix
up the current patch series and check it in once the 4.7 window opens;
and that having the utility library to convert other names (including
this udev-style naming) into something libxl can use would be a separate
series.

I wasn't necessarily expecting you to work on it (since it wasn't your
idea), but if you're keen, I'm sure we'd be grateful. :-)

> 2) use libusb instead of reading sysfs by ourselves. As George mentioned, using
>     libusb is not simpler than reading sysfs; and if UDEV name is stored to some
>     sysfs entry for us to retrieve, then we still need reading sysfs things. Could we
>     get to a final decision?
> If these are settled down, I can update related code.

I don't think that libusb would be particularly useful for the current
pvusb code, since
1. It's already Linux-specific,
2. We need to mess around with sysfs anyway.

The same thing can't be said of the HVM path: I think it fairly likely
that the emulated pass-through will Just Work (or nearly so) on BSDs
(assuming that qemu itself works on the BSDs).

I think it we write our utility library for converting
vendorid:productid[:serialno], bus-port, &c, then it might make sense to
use libusb if it makes it more portable.

Regarding the code:

Things are looking pretty close.  A couple of comments in-line:

>>>> diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c  
>>>> index 93bb41e..9869a21 100644  
>>>> --- a/tools/libxl/libxl_device.c  
>>>> +++ b/tools/libxl/libxl_device.c  
>>>> @@ -676,6 +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;  
>>>> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) {  
>>>> +                    libxl__initiate_device_usbctrl_remove(egc, aodev);  
>>>> +                    continue;  
>>>> +                }

I take it the reason we need to special-case this is that we need to go
through and un-assign all of the devices inside the controller first?

At some point we probably want to generalize this a bit, so that usb
controllers and vscsi controllers look the same (rather than both being
special-cased).

But since this is internal, maybe we can wait for that design until we
actually have both types available.

>>>> +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);  

Hmm, so this had made me realize that I don't think we're doing quite
the right thing for non-dom0 backends.

First of all, things are a bit "interesting" for driver domain backends,
because the "namespace" of hostbus.hostaddr depends on the backend of
the virtual controller.  Which wouldn't be particularly interesting,
*except* that because the USB space is so dynamic, you normally have to
query the devices to find the hostbus.hostaddr; and you can't do any
queries from dom0 at the moment (except, for example, by ssh'ing into
the other vm).  So to even assign a hostbus.hostaddr device you have to
somehow learn, out-of-band, what those numbers are within the domain.

Secondly, if the backend is in another domain, then all the stuff re
assigning a usb device to usbback can't be done by libxl in the
toolstack domain either.  Which means I'm pretty sure this stuff will
fail at the moment for USB driver domains trying to assign a
non-existent hostbus.hostaddr to the (possibly non-existent) dom0 usbback.

A couple of ways forward:

1. Delete backend_domid and backend_name; add them back later when we
decide to implement proper USB driver domain support

2. Leave backend_domid and backend_name, but return an error if they are
not 0 and NULL respectively.

3. If backend_domid != TOOLSTACK_DOMID, then do the xenstore stuff in
libxl, but assume that someone else has figured out what the proper
hostbus.hostaddr are for the backend domain, and also assigned that
device to the usbback inside that domain.

4. Invent some protocol for talking to the backend, allowing you to
query assignable devices and assign devices to usbback in that domain
from the toolstack domain.

#4 is probably more than we want to do at this point; and #1 I think
will cause us some hassle down the road if we ever *do* want to
implement usb driver domains.

I would personally probably go for #2, unless we have some intention of
at least smoke-testing #3; but I can certainly see the advantages of
implementing #3 even if we don't end up testing it.


>>>> +int libxl_device_usb_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,  
>>>> +                             libxl_usbinfo *usbinfo)  
>>>> +{  
>>>> +    GC_INIT(ctx);  
>>>> +    char *filename;  
>>>> +    char *busid;  
>>>> +    void *buf = NULL;  
>>>> +    int buflen, rc;  
>>>> +  
>>>> +    usbinfo->ctrl = usb->ctrl;  
>>>> +    usbinfo->port = usb->port;  
>>>> +  
>>>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);  

If we're going to use "<ctrl,port>" for the "handle" here, then those
are the only fields we're allowed to *read* from the usb struct.  You
should do something like you do in ctrlport_to_device_usb() to convert
<ctrl,port> to busid.  (And I suppose then have hostbus and hostaddr in
the usbinfo struct as well.)

>>>> diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl  
>>>> index 23f27d4..5278d03 100644  
>>>> --- a/tools/libxl/libxl_types.idl  
>>>> +++ b/tools/libxl/libxl_types.idl  
>>>> @@ -541,6 +541,28 @@ 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),  

Still chewing on the name here.  Is "datapath" more descriptive?

Oh, wait -- actually, now that we have the usbctrl/usb dichotomy, we
could in theory use "type" here to be "PV / IOEMU" (or perhaps
"usbctrltype", as in libxl_device_nic), and then use "devtype" in
libxl_device_usb to specify "hostdev", "tablet", &c.

>>>> +    ("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),  
>>>> +    ("hostbus",   integer),  
>>>> +    ("hostaddr",  integer),  
>>>> +    ])  

I think we do want to plan for the future here by doing something like this:

libxl_device_usb = Struct("device_usb", [
    ("ctrl", libxl_devid),
    ("port", integer),
    ("u", KeyedUnion(None, libxl_device_usb_type, "devtype",
                      [("hostdev", Struct(None, [
                             ("hostbus",   integer),
                             ("hostaddr",  integer) ]))
                       ]))
 ])

That way we can add in more device types supported by qemu later.  (And
with juergen adding pvusb support to qemu, they might even be
appropriate for more than just the IOEMU path. :-)

Thanks Chunyan, and sorry for the delay!

 -George

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-08-06 17:21         ` George Dunlap
@ 2015-08-07  2:31           ` Chun Yan Liu
  2015-08-10 10:08             ` George Dunlap
  2015-08-07  7:41           ` Chun Yan Liu
  1 sibling, 1 reply; 30+ messages in thread
From: Chun Yan Liu @ 2015-08-07  2:31 UTC (permalink / raw)
  To: George Dunlap, George Dunlap, xen-devel
  Cc: Juergen Gross, wei.liu2, ian.campbell, Ian.Jackson, Jim Fehlig,
	Simon Cao



>>> On 8/7/2015 at 01:21 AM, in message <55C39796.8000500@citrix.com>, George
Dunlap <george.dunlap@citrix.com> wrote: 
> On 08/06/2015 04:11 AM, Chun Yan Liu wrote: 
> > As 4.6 goes to bug fixing stage, maybe we can pick up this thread? :-) 
> >  
> > Beside to call for your precious review comments and suggestions so that we  
> can 
> > make progress, I also want to confirm about the previous discussed two TODO 
> > things: 
> > 1) use UDEV name rule to specify usb device uniquely even across reboot.  
> That 
> >     got consensus. Next thing is exposing that name to some sysfs entry,  
> right? 
>  
> So just to be clear, my understanding of the plan was that we try to fix 
> up the current patch series and check it in once the 4.7 window opens; 
> and that having the utility library to convert other names (including 
> this udev-style naming) into something libxl can use would be a separate 
> series. 

Got it.

>  
> I wasn't necessarily expecting you to work on it (since it wasn't your 
> idea), but if you're keen, I'm sure we'd be grateful. :-) 
>  
> > 2) use libusb instead of reading sysfs by ourselves. As George mentioned,  
> using 
> >     libusb is not simpler than reading sysfs; and if UDEV name is stored to  
> some 
> >     sysfs entry for us to retrieve, then we still need reading sysfs  
> things. Could we 
> >     get to a final decision? 
> > If these are settled down, I can update related code. 
>  
> I don't think that libusb would be particularly useful for the current 
> pvusb code, since 
> 1. It's already Linux-specific, 
> 2. We need to mess around with sysfs anyway. 
>  
> The same thing can't be said of the HVM path: I think it fairly likely 
> that the emulated pass-through will Just Work (or nearly so) on BSDs 
> (assuming that qemu itself works on the BSDs). 
>  
> I think it we write our utility library for converting 
> vendorid:productid[:serialno], bus-port, &c, then it might make sense to 
> use libusb if it makes it more portable. 
>  
> Regarding the code: 
>  
> Things are looking pretty close.  A couple of comments in-line: 
>  
> >>>> diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c   
> >>>> index 93bb41e..9869a21 100644   
> >>>> --- a/tools/libxl/libxl_device.c   
> >>>> +++ b/tools/libxl/libxl_device.c   
> >>>> @@ -676,6 +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;   
> >>>> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) {   
> >>>> +                    libxl__initiate_device_usbctrl_remove(egc, aodev);   
> >>>> +                    continue;   
> >>>> +                } 
>  
> I take it the reason we need to special-case this is that we need to go 
> through and un-assign all of the devices inside the controller first? 

Yes.

>  
> At some point we probably want to generalize this a bit, so that usb 
> controllers and vscsi controllers look the same (rather than both being 
> special-cased). 
>  
> But since this is internal, maybe we can wait for that design until we 
> actually have both types available. 
>  
> >>>> +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);   
>  
> Hmm, so this had made me realize that I don't think we're doing quite 
> the right thing for non-dom0 backends. 
>  
> First of all, things are a bit "interesting" for driver domain backends, 
> because the "namespace" of hostbus.hostaddr depends on the backend of 
> the virtual controller.  Which wouldn't be particularly interesting, 
> *except* that because the USB space is so dynamic, you normally have to 
> query the devices to find the hostbus.hostaddr; and you can't do any 
> queries from dom0 at the moment (except, for example, by ssh'ing into 
> the other vm).  So to even assign a hostbus.hostaddr device you have to 
> somehow learn, out-of-band, what those numbers are within the domain. 
>  
> Secondly, if the backend is in another domain, then all the stuff re 
> assigning a usb device to usbback can't be done by libxl in the 
> toolstack domain either.  Which means I'm pretty sure this stuff will 
> fail at the moment for USB driver domains trying to assign a 
> non-existent hostbus.hostaddr to the (possibly non-existent) dom0 usbback. 
>  
> A couple of ways forward: 
>  
> 1. Delete backend_domid and backend_name; add them back later when we 
> decide to implement proper USB driver domain support 
>  
> 2. Leave backend_domid and backend_name, but return an error if they are 
> not 0 and NULL respectively. 
>  
> 3. If backend_domid != TOOLSTACK_DOMID, then do the xenstore stuff in 
> libxl, but assume that someone else has figured out what the proper 
> hostbus.hostaddr are for the backend domain, and also assigned that 
> device to the usbback inside that domain. 
>  
> 4. Invent some protocol for talking to the backend, allowing you to 
> query assignable devices and assign devices to usbback in that domain 
> from the toolstack domain. 
>  
> #4 is probably more than we want to do at this point; and #1 I think 
> will cause us some hassle down the road if we ever *do* want to 
> implement usb driver domains. 
>  
> I would personally probably go for #2, unless we have some intention of 
> at least smoke-testing #3; but I can certainly see the advantages of 
> implementing #3 even if we don't end up testing it. 
>  
>  
> >>>> +int libxl_device_usb_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,   
> >>>> +                             libxl_usbinfo *usbinfo)   
> >>>> +{   
> >>>> +    GC_INIT(ctx);   
> >>>> +    char *filename;   
> >>>> +    char *busid;   
> >>>> +    void *buf = NULL;   
> >>>> +    int buflen, rc;   
> >>>> +   
> >>>> +    usbinfo->ctrl = usb->ctrl;   
> >>>> +    usbinfo->port = usb->port;   
> >>>> +   
> >>>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);   
>  
> If we're going to use "<ctrl,port>" for the "handle" here, then those 
> are the only fields we're allowed to *read* from the usb struct.  You 
> should do something like you do in ctrlport_to_device_usb() to convert 
> <ctrl,port> to busid.  (And I suppose then have hostbus and hostaddr in 
> the usbinfo struct as well.) 

OK. I'll adjust the codes.
Currently this is only used to print info. Current codes prints usbctrl info first,
then gets usb list from usbctrl, then calls this function, then print usb info.
I'll adjust that: put conversion from <ctrl,port> to usb device, and get info at
the same function. Don't need to get usb list and then get info in 2 steps.

> >>>>     
> >>>> +libxl_usb_protocol = Enumeration("usb_protocol", [   
> >>>> +    (0, "AUTO"),   
> >>>> +    (1, "PV"),   
> >>>> +    (2, "QEMU"),   
> >>>> +    ])   
> >>>> +   
> >>>> +libxl_device_usbctrl = Struct("device_usbctrl", [   
> >>>> +    ("protocol", libxl_usb_protocol),   
>  
> Still chewing on the name here.  Is "datapath" more descriptive? 
>  
> Oh, wait -- actually, now that we have the usbctrl/usb dichotomy, we 
> could in theory use "type" here to be "PV / IOEMU" (or perhaps

Currently in xl user interface, it is exposed to user as 'type', so maybe
just keep the same to use 'type' here. I'll update then. Naming it as 'protocol'
before is just to avoid confusing with usb device type.

> "usbctrltype", as in libxl_device_nic), and then use "devtype" in 
> libxl_device_usb to specify "hostdev", "tablet", &c. 
>  
> >>>> +    ("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),   
> >>>> +    ("hostbus",   integer),   
> >>>> +    ("hostaddr",  integer),   
> >>>> +    ])   
>  
> I think we do want to plan for the future here by doing something like this: 
>  
> libxl_device_usb = Struct("device_usb", [ 
>     ("ctrl", libxl_devid), 
>     ("port", integer), 
>     ("u", KeyedUnion(None, libxl_device_usb_type, "devtype", 
>                       [("hostdev", Struct(None, [ 
>                              ("hostbus",   integer), 
>                              ("hostaddr",  integer) ])) 
>                        ])) 
>  ]) 
>  

Yes, that's the future look. For pvusb, currenlty with kernel pvusb driver, the
devtype is not really necessary. But I can add 'devtype' if it is preferred now.

Thanks very much!
-Chunyan

> That way we can add in more device types supported by qemu later.  (And 
> with juergen adding pvusb support to qemu, they might even be 
> appropriate for more than just the IOEMU path. :-) 


>  
> Thanks Chunyan, and sorry for the delay! 
>  
>  -George 
>  
>  
> _______________________________________________ 
> Xen-devel mailing list 
> Xen-devel@lists.xen.org 
> http://lists.xen.org/xen-devel 
>  
>  

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-08-06 17:21         ` George Dunlap
  2015-08-07  2:31           ` Chun Yan Liu
@ 2015-08-07  7:41           ` Chun Yan Liu
  1 sibling, 0 replies; 30+ messages in thread
From: Chun Yan Liu @ 2015-08-07  7:41 UTC (permalink / raw)
  To: George Dunlap, George Dunlap, xen-devel
  Cc: Juergen Gross, wei.liu2, ian.campbell, Ian.Jackson, Jim Fehlig,
	Simon Cao



>>> On 8/7/2015 at 01:21 AM, in message <55C39796.8000500@citrix.com>, George
Dunlap <george.dunlap@citrix.com> wrote: 
> On 08/06/2015 04:11 AM, Chun Yan Liu wrote: 
> > As 4.6 goes to bug fixing stage, maybe we can pick up this thread? :-) 
> >  
> > Beside to call for your precious review comments and suggestions so that we  
> can 
> > make progress, I also want to confirm about the previous discussed two TODO 
> > things: 
> > 1) use UDEV name rule to specify usb device uniquely even across reboot.  
> That 
> >     got consensus. Next thing is exposing that name to some sysfs entry,  
> right? 
>  
> So just to be clear, my understanding of the plan was that we try to fix 
> up the current patch series and check it in once the 4.7 window opens; 
> and that having the utility library to convert other names (including 
> this udev-style naming) into something libxl can use would be a separate 
> series. 
>  
> I wasn't necessarily expecting you to work on it (since it wasn't your 
> idea), but if you're keen, I'm sure we'd be grateful. :-) 
>  
> > 2) use libusb instead of reading sysfs by ourselves. As George mentioned,  
> using 
> >     libusb is not simpler than reading sysfs; and if UDEV name is stored to  
> some 
> >     sysfs entry for us to retrieve, then we still need reading sysfs  
> things. Could we 
> >     get to a final decision? 
> > If these are settled down, I can update related code. 
>  
> I don't think that libusb would be particularly useful for the current 
> pvusb code, since 
> 1. It's already Linux-specific, 
> 2. We need to mess around with sysfs anyway. 
>  
> The same thing can't be said of the HVM path: I think it fairly likely 
> that the emulated pass-through will Just Work (or nearly so) on BSDs 
> (assuming that qemu itself works on the BSDs). 
>  
> I think it we write our utility library for converting 
> vendorid:productid[:serialno], bus-port, &c, then it might make sense to 
> use libusb if it makes it more portable. 
>  
> Regarding the code: 
>  
> Things are looking pretty close.  A couple of comments in-line: 
>  
> >>>> diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c   
> >>>> index 93bb41e..9869a21 100644   
> >>>> --- a/tools/libxl/libxl_device.c   
> >>>> +++ b/tools/libxl/libxl_device.c   
> >>>> @@ -676,6 +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;   
> >>>> +                if (dev->backend_kind == LIBXL__DEVICE_KIND_VUSB) {   
> >>>> +                    libxl__initiate_device_usbctrl_remove(egc, aodev);   
> >>>> +                    continue;   
> >>>> +                } 
>  
> I take it the reason we need to special-case this is that we need to go 
> through and un-assign all of the devices inside the controller first? 
>  
> At some point we probably want to generalize this a bit, so that usb 
> controllers and vscsi controllers look the same (rather than both being 
> special-cased). 
>  
> But since this is internal, maybe we can wait for that design until we 
> actually have both types available. 
>  
> >>>> +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);   
>  
> Hmm, so this had made me realize that I don't think we're doing quite 
> the right thing for non-dom0 backends.  

For non-dom0 backends, here 'be_path' should be replaced with the right backend.

>  
> First of all, things are a bit "interesting" for driver domain backends, 
> because the "namespace" of hostbus.hostaddr depends on the backend of 
> the virtual controller.  Which wouldn't be particularly interesting, 
> *except* that because the USB space is so dynamic, you normally have to 
> query the devices to find the hostbus.hostaddr; and you can't do any 
> queries from dom0 at the moment (except, for example, by ssh'ing into 
> the other vm).  So to even assign a hostbus.hostaddr device you have to 
> somehow learn, out-of-band, what those numbers are within the domain.

I think you are right. As for the network driver domain, when specifying vif,
we also need to know the bridge name in that driver domain, then we can use:
vif = [ 'bridge=xenbr0, mac=00:16:3E:0d:13:00, model=e1000, backend=domnet' ]

>  
> Secondly, if the backend is in another domain, then all the stuff re 
> assigning a usb device to usbback can't be done by libxl in the 
> toolstack domain either.

Yes. I think so. Should be done in driver domain then.

>  Which means I'm pretty sure this stuff will 
> fail at the moment for USB driver domains trying to assign a 
> non-existent hostbus.hostaddr to the (possibly non-existent) dom0 usbback.

Maybe I'm wrong but as I understand about driver domain (refer to network driver domain),
all usb devices should be managed by usb driver domain, so with driver domain, here
the hostbus.hostaddr should be the number within driver domain, and it should assign
the device to driver domain's usbback, rather than Dom0's. This part of work should
be done in driver domain. Code structure is quite different.

>From this prespective, maybe we can add a check: if backend != Dom0, bypass the
unbind/bind work, only do xenstore stuff, leave unbind/bind work to the specific
backend to do. And currently in the entry we can return error if backend != Dom0,
as you suggested in following #2.

How do you think?

- Chunyan
 
>  
> A couple of ways forward: 
>  
> 1. Delete backend_domid and backend_name; add them back later when we 
> decide to implement proper USB driver domain support 
>  
> 2. Leave backend_domid and backend_name, but return an error if they are 
> not 0 and NULL respectively. 
>  
> 3. If backend_domid != TOOLSTACK_DOMID, then do the xenstore stuff in 
> libxl, but assume that someone else has figured out what the proper 
> hostbus.hostaddr are for the backend domain, and also assigned that 
> device to the usbback inside that domain. 
>  
> 4. Invent some protocol for talking to the backend, allowing you to 
> query assignable devices and assign devices to usbback in that domain 
> from the toolstack domain. 
>  
> #4 is probably more than we want to do at this point; and #1 I think 
> will cause us some hassle down the road if we ever *do* want to 
> implement usb driver domains. 
>  
> I would personally probably go for #2, unless we have some intention of 
> at least smoke-testing #3; but I can certainly see the advantages of 
> implementing #3 even if we don't end up testing it. 
>  
>  
> >>>> +int libxl_device_usb_getinfo(libxl_ctx *ctx, libxl_device_usb *usb,   
> >>>> +                             libxl_usbinfo *usbinfo)   
> >>>> +{   
> >>>> +    GC_INIT(ctx);   
> >>>> +    char *filename;   
> >>>> +    char *busid;   
> >>>> +    void *buf = NULL;   
> >>>> +    int buflen, rc;   
> >>>> +   
> >>>> +    usbinfo->ctrl = usb->ctrl;   
> >>>> +    usbinfo->port = usb->port;   
> >>>> +   
> >>>> +    busid = usb_busaddr_to_busid(gc, usb->hostbus, usb->hostaddr);   
>  
> If we're going to use "<ctrl,port>" for the "handle" here, then those 
> are the only fields we're allowed to *read* from the usb struct.  You 
> should do something like you do in ctrlport_to_device_usb() to convert 
> <ctrl,port> to busid.  (And I suppose then have hostbus and hostaddr in 
> the usbinfo struct as well.) 
>  
> >>>> diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl   
> >>>> index 23f27d4..5278d03 100644   
> >>>> --- a/tools/libxl/libxl_types.idl   
> >>>> +++ b/tools/libxl/libxl_types.idl   
> >>>> @@ -541,6 +541,28 @@ 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),   
>  
> Still chewing on the name here.  Is "datapath" more descriptive? 
>  
> Oh, wait -- actually, now that we have the usbctrl/usb dichotomy, we 
> could in theory use "type" here to be "PV / IOEMU" (or perhaps 
> "usbctrltype", as in libxl_device_nic), and then use "devtype" in 
> libxl_device_usb to specify "hostdev", "tablet", &c. 
>  
> >>>> +    ("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),   
> >>>> +    ("hostbus",   integer),   
> >>>> +    ("hostaddr",  integer),   
> >>>> +    ])   
>  
> I think we do want to plan for the future here by doing something like this: 
>  
> libxl_device_usb = Struct("device_usb", [ 
>     ("ctrl", libxl_devid), 
>     ("port", integer), 
>     ("u", KeyedUnion(None, libxl_device_usb_type, "devtype", 
>                       [("hostdev", Struct(None, [ 
>                              ("hostbus",   integer), 
>                              ("hostaddr",  integer) ])) 
>                        ])) 
>  ]) 
>  
> That way we can add in more device types supported by qemu later.  (And 
> with juergen adding pvusb support to qemu, they might even be 
> appropriate for more than just the IOEMU path. :-) 
>  
> Thanks Chunyan, and sorry for the delay! 
>  
>  -George 
>  
>  
>  

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

* Re: [PATCH V5 3/7] libxl: add pvusb API
  2015-08-07  2:31           ` Chun Yan Liu
@ 2015-08-10 10:08             ` George Dunlap
  0 siblings, 0 replies; 30+ messages in thread
From: George Dunlap @ 2015-08-10 10:08 UTC (permalink / raw)
  To: Chun Yan Liu, George Dunlap, xen-devel
  Cc: Juergen Gross, wei.liu2, ian.campbell, Ian.Jackson, Jim Fehlig,
	Simon Cao

On 08/07/2015 03:31 AM, Chun Yan Liu wrote:
>>>>>> +    ("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),   
>>>>>> +    ("hostbus",   integer),   
>>>>>> +    ("hostaddr",  integer),   
>>>>>> +    ])   
>>  
>> I think we do want to plan for the future here by doing something like this: 
>>  
>> libxl_device_usb = Struct("device_usb", [ 
>>     ("ctrl", libxl_devid), 
>>     ("port", integer), 
>>     ("u", KeyedUnion(None, libxl_device_usb_type, "devtype", 
>>                       [("hostdev", Struct(None, [ 
>>                              ("hostbus",   integer), 
>>                              ("hostaddr",  integer) ])) 
>>                        ])) 
>>  ]) 
>>  
> 
> Yes, that's the future look. For pvusb, currenlty with kernel pvusb driver, the
> devtype is not really necessary. But I can add 'devtype' if it is preferred now.

Yes, I think as much as possible we want the interface which is actually
checked in to be forward-compatible.

Thanks!
 -George

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

end of thread, other threads:[~2015-08-10 10:08 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-06-25 10:07 [PATCH V5 0/7] xen pvusb toolstack work Chunyan Liu
2015-06-25 10:07 ` [PATCH V5 1/7] libxl: export some functions for pvusb use Chunyan Liu
2015-06-25 10:07 ` [PATCH V5 2/7] libxl_read_file_contents: add new entry to read sysfs file Chunyan Liu
2015-06-25 11:09   ` Ian Jackson
2015-06-26  5:47     ` Chun Yan Liu
2015-06-26  5:48     ` Chun Yan Liu
2015-06-26 13:05       ` Ian Jackson
2015-06-29 10:15         ` Chun Yan Liu
2015-06-29 10:52           ` Ian Jackson
2015-06-30  1:43             ` Chun Yan Liu
2015-06-30  9:08               ` Ian Jackson
2015-06-30  9:32                 ` Chun Yan Liu
2015-06-30  9:42                   ` Ian Jackson
2015-06-25 10:07 ` [PATCH V5 3/7] libxl: add pvusb API Chunyan Liu
2015-06-25 11:15   ` Ian Jackson
2015-07-07  1:25   ` Chun Yan Liu
2015-07-07  9:57     ` George Dunlap
2015-08-06  3:11       ` Chun Yan Liu
2015-08-06 17:21         ` George Dunlap
2015-08-07  2:31           ` Chun Yan Liu
2015-08-10 10:08             ` George Dunlap
2015-08-07  7:41           ` Chun Yan Liu
2015-07-16 15:01   ` Juergen Gross
2015-06-25 10:07 ` [PATCH V5 4/7] libxl: add libxl_device_usb_assignable_list API Chunyan Liu
2015-06-25 10:07 ` [PATCH V5 5/7] xl: add pvusb commands Chunyan Liu
2015-06-25 10:07 ` [PATCH V5 6/7] xl: add usb-assignable-list command Chunyan Liu
2015-06-25 10:07 ` [PATCH V5 7/7] domcreate: support pvusb in configuration file Chunyan Liu
2015-07-13 10:08   ` Juergen Gross
2015-08-06  2:49     ` Chun Yan Liu
2015-07-13 10:09 ` [PATCH V5 0/7] xen pvusb toolstack work Juergen Gross

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).