[libvirt] [PATCH] Add ability to attach USB devices to guests using vendor, product and serial id.

david.waring at rd.bbc.co.uk david.waring at rd.bbc.co.uk
Fri Jun 14 12:43:28 UTC 2013


From: David Waring <davidjw at rd.bbc.co.uk>

---
 docs/formatdomain.html.in   | 21 +++++++++++---------
 src/conf/domain_conf.c      | 18 +++++++++++++++--
 src/conf/domain_conf.h      |  1 +
 src/conf/node_device_conf.c |  5 +++++
 src/conf/node_device_conf.h |  1 +
 src/lxc/lxc_hostdev.c       |  5 +++--
 src/qemu/qemu_hostdev.c     |  5 +++--
 src/util/virusb.c           | 47 +++++++++++++++++++++++++++++++++++++++++----
 src/util/virusb.h           |  2 ++
 9 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 755d084..87dca10 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -2428,10 +2428,11 @@
       </dd>
       <dt><code>source</code></dt>
       <dd>The source element describes the device as seen from the host.
-      The USB device can either be addressed by vendor / product id using the
-      <code>vendor</code> and <code>product</code> elements or by the device's
-      address on the hosts using the <code>address</code> element. PCI devices
-      on the other hand can only be described by their <code>address</code>.
+      The USB device can either be addressed by vendor / product id and optional
+      serial number using the <code>vendor</code>, <code>product</code> and
+      <code>serial</code> elements or by the device's address on the hosts using
+      the <code>address</code> element. PCI devices on the other hand can only
+      be described by their <code>address</code>.
       SCSI devices are described by both the <code>adapter</code> and
       <code>address</code> elements.
 
@@ -2455,11 +2456,13 @@
           </tr>
         </table>
       </dd>
-      <dt><code>vendor</code>, <code>product</code></dt>
-      <dd>The <code>vendor</code> and <code>product</code> elements each have an
-      <code>id</code> attribute that specifies the USB vendor and product id.
-      The ids can be given in decimal, hexadecimal (starting with 0x) or
-      octal (starting with 0) form.</dd>
+      <dt><code>vendor</code>, <code>product</code>, <code>serial</code></dt>
+      <dd>The <code>vendor</code>, <code>product</code> and <code>serial</code>
+      elements each have an <code>id</code> attribute that specifies the USB
+      vendor, product and device serial id. The ids can be given in decimal,
+      hexadecimal (starting with 0x) or octal (starting with 0) form for
+      vendor and product. The serial number is a string that matches the serial
+      number of the device.</dd>
       <dt><code>boot</code></dt>
       <dd>Specifies that the device is bootable. The <code>order</code>
       attribute determines the order in which devices will be tried during
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 2373397..1ef775d 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -3541,6 +3541,16 @@ virDomainHostdevSubsysUsbDefParseXML(const xmlNodePtr node,
                                    "%s", _("usb product needs id"));
                     goto out;
                 }
+            } else if (xmlStrEqual(cur->name, BAD_CAST "serial")) {
+                char* serial = virXMLPropString(cur, "id");
+
+                if (serial) {
+                    def->source.subsys.u.usb.serial = serial;
+                } else {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   "%s", _("usb serial needs id"));
+                    goto out;
+                }
             } else if (xmlStrEqual(cur->name, BAD_CAST "address")) {
                 char *bus, *device;
 
@@ -9532,9 +9542,10 @@ virDomainHostdevMatchSubsysUSB(virDomainHostdevDefPtr a,
             a->source.subsys.u.usb.device == b->source.subsys.u.usb.device)
             return 1;
     } else {
-        /* specified by product & vendor id */
+        /* specified by product, vendor id and optionally serial number */
         if (a->source.subsys.u.usb.product == b->source.subsys.u.usb.product &&
-            a->source.subsys.u.usb.vendor == b->source.subsys.u.usb.vendor)
+            a->source.subsys.u.usb.vendor == b->source.subsys.u.usb.vendor &&
+            STREQ_NULLABLE(a->source.subsys.u.usb.serial, b->source.subsys.u.usb.serial))
             return 1;
     }
     return 0;
@@ -14343,6 +14354,9 @@ virDomainHostdevDefFormatSubsys(virBufferPtr buf,
                               def->source.subsys.u.usb.vendor);
             virBufferAsprintf(buf, "<product id='0x%.4x'/>\n",
                               def->source.subsys.u.usb.product);
+            if (def->source.subsys.u.usb.serial != NULL)
+                virBufferAsprintf(buf, "<serial id='%s' />\n",
+                                  def->source.subsys.u.usb.serial);
         }
         if (def->source.subsys.u.usb.bus ||
             def->source.subsys.u.usb.device) {
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 5b159ac..d1086f4 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -413,6 +413,7 @@ struct _virDomainHostdevSubsys {
 
             unsigned vendor;
             unsigned product;
+            char *serial;
         } usb;
         struct {
             virDevicePCIAddress addr; /* host address */
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index 4eeb3b3..7a56cc3 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -347,6 +347,9 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDefPtr def)
                                       data->usb_dev.vendor_name);
             else
                 virBufferAddLit(&buf, " />\n");
+            if (data->usb_dev.serial)
+                virBufferEscapeString(&buf, "    <serial id='%s' />\n",
+                                      data->usb_dev.serial);
             break;
         case VIR_NODE_DEV_CAP_USB_INTERFACE:
             virBufferAsprintf(&buf, "    <number>%d</number>\n",
@@ -952,6 +955,7 @@ virNodeDevCapUsbDevParseXML(xmlXPathContextPtr ctxt,
 
     data->usb_dev.vendor_name  = virXPathString("string(./vendor[1])", ctxt);
     data->usb_dev.product_name = virXPathString("string(./product[1])", ctxt);
+    data->usb_dev.serial = virXPathString("string(./serial[1]/@id)", ctxt);
 
     ret = 0;
 out:
@@ -1383,6 +1387,7 @@ void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
     case VIR_NODE_DEV_CAP_USB_DEV:
         VIR_FREE(data->usb_dev.product_name);
         VIR_FREE(data->usb_dev.vendor_name);
+        VIR_FREE(data->usb_dev.serial);
         break;
     case VIR_NODE_DEV_CAP_USB_INTERFACE:
         VIR_FREE(data->usb_if.description);
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index 1c5855c..2c14d63 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -118,6 +118,7 @@ struct _virNodeDevCapsDef {
             unsigned int vendor;
             char *product_name;
             char *vendor_name;
+            char *serial;
         } usb_dev;
         struct {
             unsigned int number;
diff --git a/src/lxc/lxc_hostdev.c b/src/lxc/lxc_hostdev.c
index 257e93b..ec931bc 100644
--- a/src/lxc/lxc_hostdev.c
+++ b/src/lxc/lxc_hostdev.c
@@ -127,6 +127,7 @@ virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
 {
     unsigned vendor = hostdev->source.subsys.u.usb.vendor;
     unsigned product = hostdev->source.subsys.u.usb.product;
+    const char *serial = hostdev->source.subsys.u.usb.serial;
     unsigned bus = hostdev->source.subsys.u.usb.bus;
     unsigned device = hostdev->source.subsys.u.usb.device;
     bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;
@@ -135,7 +136,7 @@ virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
     *usb = NULL;
 
     if (vendor && bus) {
-        rc = virUSBDeviceFind(vendor, product, bus, device,
+        rc = virUSBDeviceFind(vendor, product, serial, bus, device,
                               NULL,
                               autoAddress ? false : mandatory,
                               usb);
@@ -157,7 +158,7 @@ virLXCFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
     if (vendor) {
         virUSBDeviceList *devs;
 
-        rc = virUSBDeviceFindByVendor(vendor, product,
+        rc = virUSBDeviceFindByVendor(vendor, product, serial, 
                                       NULL,
                                       mandatory, &devs);
         if (rc < 0)
diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c
index 9013f60..d8582d7 100644
--- a/src/qemu/qemu_hostdev.c
+++ b/src/qemu/qemu_hostdev.c
@@ -722,6 +722,7 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
 {
     unsigned vendor = hostdev->source.subsys.u.usb.vendor;
     unsigned product = hostdev->source.subsys.u.usb.product;
+    const char *serial = hostdev->source.subsys.u.usb.serial;
     unsigned bus = hostdev->source.subsys.u.usb.bus;
     unsigned device = hostdev->source.subsys.u.usb.device;
     bool autoAddress = hostdev->source.subsys.u.usb.autoAddress;
@@ -730,7 +731,7 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
     *usb = NULL;
 
     if (vendor && bus) {
-        rc = virUSBDeviceFind(vendor, product, bus, device,
+        rc = virUSBDeviceFind(vendor, product, serial, bus, device,
                               NULL,
                               autoAddress ? false : mandatory,
                               usb);
@@ -752,7 +753,7 @@ qemuFindHostdevUSBDevice(virDomainHostdevDefPtr hostdev,
     if (vendor) {
         virUSBDeviceListPtr devs;
 
-        rc = virUSBDeviceFindByVendor(vendor, product, NULL, mandatory, &devs);
+        rc = virUSBDeviceFindByVendor(vendor, product, serial, NULL, mandatory, &devs);
         if (rc < 0)
             return -1;
 
diff --git a/src/util/virusb.c b/src/util/virusb.c
index d34e44f..5ca396e 100644
--- a/src/util/virusb.c
+++ b/src/util/virusb.c
@@ -54,6 +54,7 @@ struct _virUSBDevice {
 
     char          name[USB_ADDR_LEN]; /* domain:bus:slot.function */
     char          id[USB_ID_LEN];     /* product vendor */
+    char          *serial;            /* serial number */
     char          *path;
     const char    *used_by;           /* name of the domain using this dev */
 };
@@ -87,6 +88,30 @@ static int virUSBOnceInit(void)
 
 VIR_ONCE_GLOBAL_INIT(virUSB)
 
+static int virUSBSysReadFileString(const char *f_name, const char *d_name, char **buf)
+{
+    int ret = -1, tmp;
+    char *filename = NULL;
+
+    tmp = virAsprintf(&filename, USB_SYSFS "/devices/%s/%s", d_name, f_name);
+    if (tmp < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    tmp = virFileReadAll(filename, 1024, buf);
+    if (tmp < 0)
+        goto cleanup;
+
+    if (tmp > 0 && (*buf)[tmp-1] == '\n')
+        (*buf)[tmp-1]=0;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(filename);
+    return ret;
+}
+
 static int virUSBSysReadFile(const char *f_name, const char *d_name,
                              int base, unsigned int *value)
 {
@@ -120,6 +145,7 @@ cleanup:
 static virUSBDeviceListPtr
 virUSBDeviceSearch(unsigned int vendor,
                    unsigned int product,
+                   const char *serial,
                    unsigned int bus,
                    unsigned int devno,
                    const char *vroot,
@@ -128,6 +154,7 @@ virUSBDeviceSearch(unsigned int vendor,
     DIR *dir = NULL;
     bool found = false;
     char *ignore = NULL;
+    char *found_serial = NULL;
     struct dirent *de;
     virUSBDeviceListPtr list = NULL, ret = NULL;
     virUSBDevicePtr usb;
@@ -158,6 +185,10 @@ virUSBDeviceSearch(unsigned int vendor,
                               16, &found_prod) < 0)
             goto cleanup;
 
+        VIR_FREE(found_serial);
+        found_serial=NULL;
+        virUSBSysReadFileString("serial", de->d_name, &found_serial);
+
         if (STRPREFIX(de->d_name, "usb"))
             tmpstr += 3;
 
@@ -173,7 +204,8 @@ virUSBDeviceSearch(unsigned int vendor,
             goto cleanup;
 
         if ((flags & USB_DEVICE_FIND_BY_VENDOR) &&
-            (found_prod != product || found_vend != vendor))
+            (found_prod != product || found_vend != vendor ||
+             (serial != NULL && !STREQ_NULLABLE(found_serial, serial))))
             continue;
 
         if (flags & USB_DEVICE_FIND_BY_BUS) {
@@ -197,6 +229,8 @@ virUSBDeviceSearch(unsigned int vendor,
     ret = list;
 
 cleanup:
+    VIR_FREE(found_serial);
+
     if (dir) {
         int saved_errno = errno;
         closedir(dir);
@@ -211,6 +245,7 @@ cleanup:
 int
 virUSBDeviceFindByVendor(unsigned int vendor,
                          unsigned int product,
+                         const char *serial,
                          const char *vroot,
                          bool mandatory,
                          virUSBDeviceListPtr *devices)
@@ -218,7 +253,7 @@ virUSBDeviceFindByVendor(unsigned int vendor,
     virUSBDeviceListPtr list;
     int count;
 
-    if (!(list = virUSBDeviceSearch(vendor, product, 0 , 0,
+    if (!(list = virUSBDeviceSearch(vendor, product, serial, 0 , 0,
                                     vroot,
                                     USB_DEVICE_FIND_BY_VENDOR)))
         return -1;
@@ -256,7 +291,7 @@ virUSBDeviceFindByBus(unsigned int bus,
 {
     virUSBDeviceListPtr list;
 
-    if (!(list = virUSBDeviceSearch(0, 0, bus, devno,
+    if (!(list = virUSBDeviceSearch(0, 0, NULL, bus, devno,
                                     vroot,
                                     USB_DEVICE_FIND_BY_BUS)))
         return -1;
@@ -289,6 +324,7 @@ virUSBDeviceFindByBus(unsigned int bus,
 int
 virUSBDeviceFind(unsigned int vendor,
                  unsigned int product,
+                 const char *serial,
                  unsigned int bus,
                  unsigned int devno,
                  const char *vroot,
@@ -298,7 +334,7 @@ virUSBDeviceFind(unsigned int vendor,
     virUSBDeviceListPtr list;
 
     unsigned int flags = USB_DEVICE_FIND_BY_VENDOR|USB_DEVICE_FIND_BY_BUS;
-    if (!(list = virUSBDeviceSearch(vendor, product, bus, devno,
+    if (!(list = virUSBDeviceSearch(vendor, product, serial, bus, devno,
                                     vroot, flags)))
         return -1;
 
@@ -368,6 +404,8 @@ virUSBDeviceNew(unsigned int bus,
         return NULL;
     }
 
+    /* XXX fixme. Read serial number if available */
+
     VIR_DEBUG("%s %s: initialized", dev->id, dev->name);
 
     return dev;
@@ -379,6 +417,7 @@ virUSBDeviceFree(virUSBDevicePtr dev)
     if (!dev)
         return;
     VIR_DEBUG("%s %s: freeing", dev->id, dev->name);
+    VIR_FREE(dev->serial);
     VIR_FREE(dev->path);
     VIR_FREE(dev);
 }
diff --git a/src/util/virusb.h b/src/util/virusb.h
index aa59d12..014ae91 100644
--- a/src/util/virusb.h
+++ b/src/util/virusb.h
@@ -47,12 +47,14 @@ int virUSBDeviceFindByBus(unsigned int bus,
 
 int virUSBDeviceFindByVendor(unsigned int vendor,
                              unsigned int product,
+                             const char *serial,
                              const char *vroot,
                              bool mandatory,
                              virUSBDeviceListPtr *devices);
 
 int virUSBDeviceFind(unsigned int vendor,
                      unsigned int product,
+                     const char *serial,
                      unsigned int bus,
                      unsigned int devno,
                      const char *vroot,
-- 
1.8.1.4




More information about the libvir-list mailing list