[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v7 RFC 1/2] libxl: Introduce functions to add and remove USB devices to an HVM guest



This patch exposes a generic interface which can be expanded in the
future to implement USB for PVUSB, qemu, and stubdoms.  It can also be
extended to include other types of USB other than host USB (for example,
tablets, mice, or keyboards).

For each device removed or added, one of two protocols is available:
* PVUSB
* qemu (DEVICEMODEL)

The caller can additionally specify "AUTO", in which case the library will
try to determine the best protocol automatically.

At the moment, the only protocol implemented is DEVICEMODEL, and the only
device type impelmented is HOSTDEV.

This uses the qmp functionality, and is thus only available for
qemu-xen, not qemu-traditional.

Signed-off-by: George Dunlap <george.dunlap@xxxxxxxxxxxxx>
---
History:
v7:
 - Correct spelling mistake
 - Add libxl_device_usb_destroy
 - Add backend_domname to device_struct
 - Don't have a default backend
 - Implement libxl__device_usb_setdefault
 - Get rid of "internal" usbdev struct
 - Fix author of libxl_usb.c
 - Use LOG() consistently
 - Don't unnecessarily cast hostbus/hostaddr to uint16 in the USB xenstore path
 - Create libxl__domain_usb_init function to put all usb-related xenstore stuff
   in one file
 - Call libxl__xs_transaction_abort on the failure path of usb_add_xenstore
 - Do element-by-element compare rather than memcmp
 - Use QMP_PARAMETERS_SPRINTF for id in libxl__qmp_usb_hostdev_{add,remove}
   rather than a separate GSPRINTF
 - Fix memory leak in failure path of get_assigned_devices()
 - Handle non-NULL empty list returned by libxl__xs_directory in 
get_assigned_devices
 - Don't garbage-collect the list when called from libxl_device_usb_list()
v6:
 - Return a proper error code if libxl__xs_mkdir fails
 - Make casts cuddly
 - Add a space after switch statements
v5:
 - fix erroneous use of NOGC in qmp functions
 - Don't check return of libxl__sprintf in libxl_create.c
 - Check return values of new xs actions in libxl_create.c
 - Use updated template for xenstore transactions, do checks
 - use xs_read_checked
 - Fixed if (x) spacing to match coding style
 - use GCSFPRINTF
 - use libxl__calloc
 - Fix comment for LIBXL_HAVE_USB
 - use idl-generated *_from_string() and *_to_string() functions

CC: Ian Jackson <ian.jackson@xxxxxxxxxx>
CC: Roger Pau Monne <roger.pau@xxxxxxxxxx>
CC: sstanisi@xxxxxxxxx
CC: Fabio Fantoni <fabio.fantoni@xxxxxxx>
CC: Ian Campbell <ian.campbell@xxxxxxxxxx>
CC: Anthony Perard <anthony.perard@xxxxxxxxxx>
CC: Simon (Bo) Cao <caobosimon@xxxxxxxxx>
---
 tools/libxl/Makefile           |    2 +-
 tools/libxl/libxl.c            |    3 +-
 tools/libxl/libxl.h            |   40 +++
 tools/libxl/libxl_create.c     |    3 +
 tools/libxl/libxl_internal.h   |   11 +
 tools/libxl/libxl_qmp.c        |   56 +++++
 tools/libxl/libxl_types.idl    |   22 ++
 tools/libxl/libxl_usb.c        |  521 ++++++++++++++++++++++++++++++++++++++++
 tools/ocaml/libs/xl/genwrap.py |    1 +
 9 files changed, 656 insertions(+), 3 deletions(-)
 create mode 100644 tools/libxl/libxl_usb.c

diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
index 4cfa275..a50e6cf 100644
--- a/tools/libxl/Makefile
+++ b/tools/libxl/Makefile
@@ -76,7 +76,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_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_usb.o 
$(LIBXL_OBJS-y)
 LIBXL_OBJS += _libxl_types.o libxl_flask.o _libxl_types_internal.o
 
 LIBXL_TESTS += timedereg
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index 4ea7abb..7ef5e25 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -1767,8 +1767,7 @@ static int libxl__device_nextid(libxl__gc *gc, uint32_t 
domid, char *device)
     return nextid;
 }
 
-static int libxl__resolve_domid(libxl__gc *gc, const char *name,
-                                uint32_t *domid)
+int libxl__resolve_domid(libxl__gc *gc, const char *name, uint32_t *domid)
 {
     if (!name)
         return 0;
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index c7aa817..963e650 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -82,6 +82,12 @@
 #define LIBXL_HAVE_DOMAIN_NODEAFFINITY 1
 
 /*
+ * LIBXL_HAVE_DEVICE_USB indicates the functions for doing hot-plug of
+ * USB devices.
+ */
+#define LIBXL_HAVE_DEVICE_USB 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
@@ -924,6 +930,40 @@ int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, 
libxl_device_disk *disk,
                        const libxl_asyncop_how *ao_how)
                        LIBXL_EXTERNAL_CALLERS_ONLY;
 
+/*
+ * USB
+ * 
+ * For each device removed or added, one of these protocols is available:
+ * - PV (i.e., PVUSB)
+ * - DEVICEMODEL (i.e, qemu)
+ *
+ * PV is available for either PV or HVM domains.  DEVICEMODEL is only
+ * available for HVM domains.  The caller can additionally specify
+ * "AUTO", in which case the library will try to determine the best
+ * protocol automatically.
+ *
+ * At the moment, the only protocol implemented is DEVICEMODEL, and the only
+ * device type implemented is HOSTDEV.
+ *
+ * This uses the qmp functionality, and is thus only available for
+ * qemu-xen, not qemu-traditional.
+ */
+int libxl_device_usb_add(libxl_ctx *ctx, uint32_t domid,
+                         libxl_device_usb *dev,
+                         const libxl_asyncop_how *ao_how)
+                         LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_usb_remove(libxl_ctx *ctx, uint32_t domid, 
+                            libxl_device_usb *dev,
+                            const libxl_asyncop_how *ao_how)
+                            LIBXL_EXTERNAL_CALLERS_ONLY;
+int libxl_device_usb_destroy(libxl_ctx *ctx, uint32_t domid, 
+                             libxl_device_usb *dev,
+                             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_EXTERNAL_CALLERS_ONLY;
+
 /* Network Interfaces */
 int libxl_device_nic_add(libxl_ctx *ctx, uint32_t domid, libxl_device_nic *nic,
                          const libxl_asyncop_how *ao_how)
diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c
index d015cf4..80a75a4 100644
--- a/tools/libxl/libxl_create.c
+++ b/tools/libxl/libxl_create.c
@@ -537,6 +537,9 @@ retry_transaction:
     xs_rm(ctx->xsh, t, libxl_path);
     libxl__xs_mkdir(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm));
 
+    rc = libxl__domain_usb_init(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm));
+    if (rc) goto out;
+
     xs_write(ctx->xsh, t, libxl__sprintf(gc, "%s/vm", dom_path), vm_path, 
strlen(vm_path));
     rc = libxl__domain_rename(gc, *domid, 0, info->name, t);
     if (rc)
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index c2b73c4..bf394e4 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -1031,6 +1031,11 @@ _hidden int libxl__wait_for_backend(libxl__gc *gc, const 
char *be_path,
                                     const char *state);
 _hidden int libxl__nic_type(libxl__gc *gc, libxl__device *dev,
                             libxl_nic_type *nictype);
+_hidden int libxl__resolve_domid(libxl__gc *gc, const char *name, 
+                                 uint32_t *domid);
+_hidden int libxl__domain_usb_init(libxl__gc *gc, xs_transaction_t t,
+                                   char *libxl_path, struct xs_permissions 
*perm,
+                                   int perm_size);
 
 /*
  * For each aggregate type which can be used as an input we provide:
@@ -1056,6 +1061,8 @@ _hidden int libxl__device_vtpm_setdefault(libxl__gc *gc, 
libxl_device_vtpm *vtpm
 _hidden int libxl__device_vfb_setdefault(libxl__gc *gc, libxl_device_vfb *vfb);
 _hidden int libxl__device_vkb_setdefault(libxl__gc *gc, libxl_device_vkb *vkb);
 _hidden int libxl__device_pci_setdefault(libxl__gc *gc, libxl_device_pci *pci);
+_hidden int libxl__device_usb_setdefault(libxl__gc *gc, libxl_device_usb *usb,
+                                         uint32_t domid);
 
 _hidden const char *libxl__device_nic_devname(libxl__gc *gc,
                                               uint32_t domid,
@@ -1585,6 +1592,10 @@ _hidden int libxl__qmp_set_global_dirty_log(libxl__gc 
*gc, int domid, bool enabl
 _hidden int libxl__qmp_insert_cdrom(libxl__gc *gc, int domid, const 
libxl_device_disk *disk);
 /* Add a virtual CPU */
 _hidden int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int index);
+_hidden int libxl__qmp_usb_add(libxl__gc *gc, int domid,
+                               libxl_device_usb *dev);
+_hidden int libxl__qmp_usb_remove(libxl__gc *gc, int domid,
+                                  libxl_device_usb *dev);
 /* close and free the QMP handler */
 _hidden void libxl__qmp_close(libxl__qmp_handler *qmp);
 /* remove the socket file, if the file has already been removed,
diff --git a/tools/libxl/libxl_qmp.c b/tools/libxl/libxl_qmp.c
index 8433e42..56ae368 100644
--- a/tools/libxl/libxl_qmp.c
+++ b/tools/libxl/libxl_qmp.c
@@ -42,6 +42,7 @@
 
 #define QMP_RECEIVE_BUFFER_SIZE 4096
 #define PCI_PT_QDEV_ID "pci-pt-%02x_%02x.%01x"
+#define HOST_USB_QDEV_ID "usb-hostdev-%04x.%04x"
 
 typedef int (*qmp_callback_t)(libxl__qmp_handler *qmp,
                               const libxl__json_object *tree,
@@ -939,6 +940,61 @@ int libxl__qmp_cpu_add(libxl__gc *gc, int domid, int idx)
     return qmp_run_command(gc, domid, "cpu-add", args, NULL, NULL);
 }
 
+static int libxl__qmp_usb_hostdev_add(libxl__gc *gc, int domid,
+                                      libxl_device_usb *usb)
+{
+    libxl__json_object *args = NULL;
+
+    qmp_parameters_add_string(gc, &args, "driver", "usb-host");
+    QMP_PARAMETERS_SPRINTF(&args, "hostbus", "0x%x", usb->u.hostdev.hostbus);
+    QMP_PARAMETERS_SPRINTF(&args, "hostaddr", "0x%x", usb->u.hostdev.hostaddr);
+    QMP_PARAMETERS_SPRINTF(&args, "id", HOST_USB_QDEV_ID,
+                           usb->u.hostdev.hostbus, usb->u.hostdev.hostaddr);
+
+    return qmp_run_command(gc, domid, "device_add", args, NULL, NULL);
+}
+
+int libxl__qmp_usb_add(libxl__gc *gc, int domid, libxl_device_usb *usb)
+{
+    int rc;
+    switch (usb->type) {
+    case LIBXL_DEVICE_USB_TYPE_HOSTDEV:
+        rc = libxl__qmp_usb_hostdev_add(gc, domid, usb);
+        break;
+    default:
+        return ERROR_INVAL;
+    }
+    return rc;
+}
+
+
+static int libxl__qmp_usb_hostdev_remove(libxl__gc *gc, int domid,
+                                         libxl_device_usb *usb)
+{
+    libxl__json_object *args = NULL;
+
+    QMP_PARAMETERS_SPRINTF(&args, "id", HOST_USB_QDEV_ID,
+                           usb->u.hostdev.hostbus, usb->u.hostdev.hostaddr);
+
+    return qmp_run_command(gc, domid, "device_del", args, NULL, NULL);
+}
+
+int libxl__qmp_usb_remove(libxl__gc *gc, int domid,
+                          libxl_device_usb *usb)
+{
+    int rc;
+    switch (usb->type) {
+    case LIBXL_DEVICE_USB_TYPE_HOSTDEV:
+        rc = libxl__qmp_usb_hostdev_remove(gc, domid, usb);
+        break;
+    default:
+        return ERROR_INVAL;
+    }
+    return rc;
+}
+
+
+
 int libxl__qmp_initializations(libxl__gc *gc, uint32_t domid,
                                const libxl_domain_config *guest_config)
 {
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index 52f1aa9..ae94ad6 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -457,6 +457,28 @@ libxl_device_vtpm = Struct("device_vtpm", [
     ("uuid",             libxl_uuid),
 ])
 
+libxl_device_usb_protocol = Enumeration("usb_protocol", [
+    (0, "AUTO"),
+    (1, "PV"),
+    (2, "DEVICEMODEL"),
+    ])
+
+libxl_device_usb_type = Enumeration("device_usb_type", [
+    (1, "HOSTDEV"),
+    ])
+
+libxl_device_usb = Struct("device_usb", [
+        ("protocol", libxl_device_usb_protocol,
+         {'init_val': 'LIBXL_USB_PROTOCOL_AUTO'}),
+        ("backend_domid",    libxl_domid),
+        ("backend_domname",  string),
+        ("u", KeyedUnion(None, libxl_device_usb_type, "type",
+                         [("hostdev", Struct(None, [
+                                ("hostbus",   integer),
+                                ("hostaddr",  integer) ]))
+                          ]))
+    ])
+
 libxl_domain_config = Struct("domain_config", [
     ("c_info", libxl_domain_create_info),
     ("b_info", libxl_domain_build_info),
diff --git a/tools/libxl/libxl_usb.c b/tools/libxl/libxl_usb.c
new file mode 100644
index 0000000..03b6ea1
--- /dev/null
+++ b/tools/libxl/libxl_usb.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2009      Citrix Ltd.
+ * Author George Dunlap <george.dunlap@xxxxxxxxxxxxx>
+ *
+ * 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"
+
+/*
+ * /libxl/<domid>/usb/<devid>/{type, protocol, backend, [typeinfo]}
+ */
+#define USB_INFO_PATH "%s/usb"
+#define USB_HOSTDEV_ID "hostdev-%04x-%04x"
+
+/*
+ * Just use plain numbers for now.  Replace these with strings at some point.
+ */
+static char * hostdev_xs_path(libxl__gc *gc, uint32_t domid,
+                                    libxl_device_usb *usb)
+{
+    /* FIXME: Assert usb->type here? */
+    return GCSPRINTF(USB_INFO_PATH "/%s",
+                     libxl__xs_libxl_path(gc, domid),
+                     GCSPRINTF(USB_HOSTDEV_ID,
+                               usb->u.hostdev.hostbus,
+                               usb->u.hostdev.hostaddr));
+}
+
+static void hostdev_xs_append_params(libxl__gc *gc, libxl_device_usb *usb,
+                                  flexarray_t *a)
+{
+    /* FIXME: Assert usb->type here? */
+    flexarray_append_pair(a, "hostbus",
+                          GCSPRINTF("%d",
+                                    usb->u.hostdev.hostbus));
+    flexarray_append_pair(a, "hostaddr",
+                          GCSPRINTF("%d",
+                                    usb->u.hostdev.hostaddr));
+}
+
+static int read_hostdev_xenstore_entry(libxl__gc *gc, const char * path,
+                                       libxl_device_usb *usb)
+{
+    int rc;
+    const char * val;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/hostbus", path), &val);
+    if (rc) goto out;
+    usb->u.hostdev.hostbus = atoi(val);
+    
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                 GCSPRINTF("%s/hostaddr", path), &val);
+    if (rc) goto out;
+    usb->u.hostdev.hostaddr = atoi(val);
+
+    rc = 0;
+out:
+    return rc;
+}
+
+static int usb_read_xenstore(libxl__gc *gc, const char * path,
+                                           libxl_device_usb *usb)
+{
+    const char *val;
+    int rc;
+
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/protocol", path), &val);
+    if (rc) goto out;
+    rc = libxl_usb_protocol_from_string(val, &usb->protocol);
+    if (rc) goto out;
+        
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/backend_domid", path), &val);
+    if (rc) goto out;
+    usb->backend_domid = atoi(val);
+    
+    rc = libxl__xs_read_checked(gc, XBT_NULL,
+                                GCSPRINTF("%s/type", path), &val);
+    if (rc) goto out;
+    rc = libxl_device_usb_type_from_string(val, &usb->type);
+    if (rc) goto out;
+
+    switch (usb->type) {
+    case LIBXL_DEVICE_USB_TYPE_HOSTDEV:
+        rc = read_hostdev_xenstore_entry(gc, path, usb);
+        if (rc) goto out;
+        break;
+    default:
+        LOG(ERROR, "Internal error (unimplemented type)");
+        goto out;
+    }
+
+    rc = 0;
+out:
+    return rc;
+}
+
+static int usb_add_xenstore(libxl__gc *gc, uint32_t domid,
+                            libxl_device_usb *usb)
+{
+    flexarray_t *dev;
+    char *dev_path;
+    xs_transaction_t t = 0;
+    struct xs_permissions noperm[1];
+    int rc = 0;
+
+    noperm[0].id = 0;
+    noperm[0].perms = XS_PERM_NONE;
+
+    dev = flexarray_make(gc, 16, 1);
+
+    flexarray_append_pair(dev, "protocol",
+                          GCSPRINTF("%s",
+                              libxl_usb_protocol_to_string(usb->protocol)));
+
+    flexarray_append_pair(dev, "backend_domid",
+                          GCSPRINTF("%d", usb->backend_domid));
+
+    flexarray_append_pair(dev, "type",
+                          GCSPRINTF("%s",
+                               libxl_device_usb_type_to_string(usb->type)));
+
+    switch (usb->type) {
+    case LIBXL_DEVICE_USB_TYPE_HOSTDEV:
+        dev_path = hostdev_xs_path(gc, domid, usb);
+        hostdev_xs_append_params(gc, usb, dev);
+        break;
+    default:
+        LOG(ERROR, "Invalid device type: %d", usb->type);
+        return ERROR_FAIL;
+    }
+
+    LOG(DEBUG, "Adding new usb device to xenstore");
+
+    for(;;) {
+        rc = libxl__xs_transaction_start(gc, &t);
+        if (rc) goto out;
+
+        /* Helpfully, mkdir returns 0 on failure, 1 on success */
+        if (!libxl__xs_mkdir(gc, t, dev_path, noperm, ARRAY_SIZE(noperm))) {
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        /* And libxl__xs_writev *always* returns 0 no matter what */
+        libxl__xs_writev(gc, t, dev_path,
+                         libxl__xs_kvs_of_flexarray(gc, dev, dev->count));
+
+        rc = libxl__xs_transaction_commit(gc, &t);
+        if (!rc) break;
+        if (rc <0) goto out;
+    }
+
+out:
+    libxl__xs_transaction_abort(gc, &t);
+    return rc;
+}
+
+static int usb_remove_xenstore(libxl__gc *gc, uint32_t domid,
+                               libxl_device_usb *usb)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    char *dev_path;
+
+    LOG(DEBUG, "Removing device from xenstore");
+
+    switch (usb->type) {
+    case LIBXL_DEVICE_USB_TYPE_HOSTDEV:
+        dev_path = hostdev_xs_path(gc, domid, usb);
+        break;
+    default:
+        LOG(ERROR, "Invalid device type: %d", usb->type);
+        return ERROR_FAIL;
+    }
+
+    xs_rm(ctx->xsh, XBT_NULL, dev_path);
+
+    return 0;
+}
+
+int libxl__domain_usb_init(libxl__gc *gc, xs_transaction_t t, char *libxl_path,
+                           struct xs_permissions *perm, int perm_size) {
+    int rc;
+    char * libxl_usb_path = GCSPRINTF(USB_INFO_PATH, libxl_path);
+
+    /* Helpfully, libxl__xs_rm_checked() returns 0 on success... */
+    rc = libxl__xs_rm_checked(gc, t, libxl_usb_path);
+    if (rc) goto out;
+
+    /* ...but libxl__xs_mkdir() returns 1 on success, 0 on failure. */
+    if (!libxl__xs_mkdir(gc, t, libxl_usb_path, perm, perm_size)) {
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+out:
+    return rc;
+}
+
+static int _get_assigned_devices(libxl__gc *gc, libxl__gc *list_gc, uint32_t 
domid,
+                                libxl_device_usb **list, int *num)
+{
+    char **devlist, *dompath;
+    unsigned int nd = 0;
+    int i, rc = 0;
+
+    *list = NULL;
+    *num = 0;
+
+    dompath = GCSPRINTF(USB_INFO_PATH,
+                        libxl__xs_libxl_path(gc, domid));
+
+    devlist = libxl__xs_directory(gc, XBT_NULL, dompath, &nd);
+    if (!devlist || !nd)
+        goto out;
+
+    *list = libxl__calloc(list_gc, nd, sizeof(libxl_device_usb));
+
+    for(i = 0; i < nd; i++) {
+        char *path;
+
+        path = GCSPRINTF("%s/%s", dompath, devlist[i]);
+        
+        rc = usb_read_xenstore(gc, path, (*list) + i);
+        if (rc) {
+            free(*list);
+            *list = NULL;
+            *num = 0;
+            goto out;
+        }
+    }
+
+    *num = nd;
+
+out:
+    return rc;
+}
+
+/* GC or NOGC indicates whether the list of devices is garbage collected. */
+#define GET_ASSIGNED_DEVICES_GC(gc, domid, list, num) \
+            _get_assigned_devices(gc, gc, domid, list, num)
+#define GET_ASSIGNED_DEVICES_NOGC(gc, domid, list, num)\
+             _get_assigned_devices(gc, NOGC, domid, list, num)
+
+static int is_usbdev_type_hostdev_equal(libxl_device_usb *a,
+                                        libxl_device_usb *b)
+{
+    return a->u.hostdev.hostbus == b->u.hostdev.hostbus
+           && a->u.hostdev.hostaddr == b->u.hostdev.hostaddr;
+}
+
+static int is_usbdev_in_array(libxl_device_usb *assigned, int num_assigned,
+                              libxl_device_usb *dev)
+{
+    int i;
+
+    /* Devices are the same if:
+     * - They have the same backend_domid
+     * - They are of the same type
+     * - Their types match
+     * In theory, someone could try to pass the same device through
+     * via both PVUSB and qemu; not comparing protocol will prevent that.
+     */
+    for(i = 0; i < num_assigned; i++) {
+        if (assigned[i].type != dev->type
+            || assigned[i].backend_domid != dev->backend_domid)
+            continue;
+
+        switch (dev->type) {
+        case LIBXL_DEVICE_USB_TYPE_HOSTDEV:
+            if (!is_usbdev_type_hostdev_equal(dev, assigned+i))
+                continue;
+        }
+
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * USB add
+ */
+static int do_usb_add(libxl__gc *gc, uint32_t domid,
+                      libxl_device_usb *usb)
+{
+    int rc;
+
+    switch (usb->protocol) {
+    case LIBXL_USB_PROTOCOL_DEVICEMODEL:
+        switch (libxl__device_model_version_running(gc, domid)) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            LOG(ERROR, "usb-add not yet implemented for qemu-traditional");
+            return ERROR_INVAL;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            rc = libxl__qmp_usb_add(gc, domid, usb);
+            if (rc)
+                goto out;
+            break;
+        default:
+            return ERROR_INVAL;
+        }
+        break;
+    default:
+        return ERROR_FAIL;
+    }
+
+    rc = usb_add_xenstore(gc, domid, usb);
+out:
+    return rc;
+}
+
+
+int libxl__device_usb_setdefault(libxl__gc *gc, libxl_device_usb *usb, 
uint32_t domid)
+{
+    int rc;
+    libxl_domain_type domtype = libxl__domain_type(gc, domid);
+
+    rc = libxl__resolve_domid(gc, usb->backend_domname, &usb->backend_domid);
+    if (rc < 0) return rc;
+
+    if (usb->protocol == LIBXL_USB_PROTOCOL_AUTO) {
+        if (domtype == LIBXL_DOMAIN_TYPE_PV) {
+            usb->protocol = LIBXL_USB_PROTOCOL_PV;
+        } else if (domtype == LIBXL_DOMAIN_TYPE_HVM) {
+            /* FIXME: See if we can detect PV frontend */
+            usb->protocol = LIBXL_USB_PROTOCOL_DEVICEMODEL;
+        }
+    }
+
+    return rc;
+}
+
+static int libxl__device_usb_add(libxl__gc *gc, uint32_t domid,
+                                 libxl_device_usb *usb)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    libxl_device_usb *assigned;
+    int rc = ERROR_FAIL, num_assigned;
+    libxl_domid dm_domid = libxl_get_stubdom_id(ctx, domid);
+
+    rc = libxl__device_usb_setdefault(gc, usb, domid);
+    if (rc < 0) goto out;
+
+    /* Check to make sure we're doing something that's implemented */
+    if (usb->protocol != LIBXL_USB_PROTOCOL_DEVICEMODEL) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "Protocol not implemented");
+        goto out;
+    }
+
+    if (dm_domid != 0) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "Stubdoms not yet supported");
+        goto out;
+    }
+
+    /* Double-check to make sure it's not already assigned */
+    rc = GET_ASSIGNED_DEVICES_GC(gc, domid, &assigned, &num_assigned);
+    if (rc) {
+        LOG(ERROR, "cannot determine if device is assigned,"
+            " refusing to continue");
+        goto out;
+    }
+    if (is_usbdev_in_array(assigned, num_assigned, usb)) {
+        LOG(ERROR, "USB device already attached to a domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* Do the add */
+    if (do_usb_add(gc, domid, usb))
+        rc = ERROR_FAIL;
+
+out:
+    return rc;
+}
+
+int libxl_device_usb_add(libxl_ctx *ctx, uint32_t domid,
+                         libxl_device_usb *usb,
+                         const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+    rc = libxl__device_usb_add(gc, domid, usb);
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+/*
+ * USB remove
+ */
+static int do_usb_remove(libxl__gc *gc, uint32_t domid,
+                         libxl_device_usb *usb)
+{
+    int rc;
+
+    switch (usb->protocol) {
+    case LIBXL_USB_PROTOCOL_DEVICEMODEL:
+        switch (libxl__device_model_version_running(gc, domid)) {
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
+            LOG(ERROR, "usb-remove not yet implemented for qemu-traditional");
+            return ERROR_INVAL;
+        case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
+            rc = libxl__qmp_usb_remove(gc, domid, usb);
+            if (rc)
+                goto out;
+            break;
+        default:
+            return ERROR_INVAL;
+        }
+        break;
+    default:
+        return ERROR_FAIL;
+    }
+
+    rc = usb_remove_xenstore(gc, domid, usb);
+out:
+    return rc;
+}
+static int libxl__device_usb_remove_common(libxl__gc *gc, uint32_t domid,
+                                           libxl_device_usb *usb)
+{
+    libxl_ctx *ctx = libxl__gc_owner(gc);
+    libxl_device_usb *assigned;
+    int rc = ERROR_FAIL, num_assigned;
+    libxl_domid dm_domid = libxl_get_stubdom_id(ctx, domid);
+
+    rc = libxl__device_usb_setdefault(gc, usb, domid);
+    if (rc < 0) goto out;
+
+    /* Check to make sure we're doing something that's impemented */
+    if (usb->protocol != LIBXL_USB_PROTOCOL_DEVICEMODEL) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "Protocol not implemented");
+        goto out;
+    }
+
+    if (dm_domid != 0) {
+        rc = ERROR_FAIL;
+        LOG(ERROR, "Stubdoms not yet supported");
+        goto out;
+    }
+
+    /* Double-check to make sure it's not already assigned */
+    rc = GET_ASSIGNED_DEVICES_GC(gc, domid, &assigned, &num_assigned);
+    if (rc) {
+        LOG(ERROR, "cannot determine if device is assigned,"
+            " refusing to continue");
+        goto out;
+    }
+    if (!is_usbdev_in_array(assigned, num_assigned, usb)) {
+        LOG(ERROR, "USB device doesn't seem to be attached to the domain");
+        rc = ERROR_FAIL;
+        goto out;
+    }
+
+    /* Do the remove */
+    if (do_usb_remove(gc, domid, usb))
+        rc = ERROR_FAIL;
+
+out:
+    return rc;
+}
+
+int libxl_device_usb_remove(libxl_ctx *ctx, uint32_t domid,
+                            libxl_device_usb *usb,
+                            const libxl_asyncop_how *ao_how)
+{
+    AO_CREATE(ctx, domid, ao_how);
+    int rc;
+
+    rc = libxl__device_usb_remove_common(gc, domid, usb);
+
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+int libxl_device_usb_destroy(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_common(gc, domid, usb);
+
+    libxl__ao_complete(egc, ao, rc);
+    return AO_INPROGRESS;
+}
+
+libxl_device_usb *libxl_device_usb_list(libxl_ctx *ctx,
+                                        uint32_t domid, int *num)
+{
+    GC_INIT(ctx);
+    libxl_device_usb *usb = NULL;
+    int rc;
+
+    rc = GET_ASSIGNED_DEVICES_NOGC(gc, domid, &usb, num);
+    if (rc) {
+        LOG(ERROR, "Could not get assigned devices");
+        goto out;
+    }
+
+out:
+    GC_FREE;
+    return usb;
+}
diff --git a/tools/ocaml/libs/xl/genwrap.py b/tools/ocaml/libs/xl/genwrap.py
index 5e43831..09f7370 100644
--- a/tools/ocaml/libs/xl/genwrap.py
+++ b/tools/ocaml/libs/xl/genwrap.py
@@ -501,6 +501,7 @@ if __name__ == '__main__':
     blacklist = [
         "cpupoolinfo",
         "vcpuinfo",
+        "device_usb"
         ]
 
     for t in blacklist:
-- 
1.7.9.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.