summary refs log tree commit diff stats
path: root/hw/usb/host-libusb.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/usb/host-libusb.c')
-rw-r--r--hw/usb/host-libusb.c56
1 files changed, 52 insertions, 4 deletions
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 8b774f4939..da59c294bb 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -81,6 +81,7 @@ struct USBHostDevice {
     uint32_t                         iso_urb_frames;
     uint32_t                         options;
     uint32_t                         loglevel;
+    bool                             needs_autoscan;
 
     /* state */
     QTAILQ_ENTRY(USBHostDevice)      next;
@@ -974,9 +975,32 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data)
     }
 }
 
+static libusb_device *usb_host_find_ref(int bus, int addr)
+{
+    libusb_device **devs = NULL;
+    libusb_device *ret = NULL;
+    int i, n;
+
+    if (usb_host_init() != 0) {
+        return NULL;
+    }
+    n = libusb_get_device_list(ctx, &devs);
+    for (i = 0; i < n; i++) {
+        if (libusb_get_bus_number(devs[i]) == bus &&
+            libusb_get_device_address(devs[i]) == addr) {
+            ret = libusb_ref_device(devs[i]);
+            break;
+        }
+    }
+    libusb_free_device_list(devs, 1);
+    return ret;
+}
+
 static void usb_host_realize(USBDevice *udev, Error **errp)
 {
     USBHostDevice *s = USB_HOST_DEVICE(udev);
+    libusb_device *ldev;
+    int rc;
 
     if (s->match.vendor_id > 0xffff) {
         error_setg(errp, "vendorid out of range");
@@ -997,11 +1021,33 @@ static void usb_host_realize(USBDevice *udev, Error **errp)
     QTAILQ_INIT(&s->requests);
     QTAILQ_INIT(&s->isorings);
 
+    if (s->match.addr && s->match.bus_num &&
+        !s->match.vendor_id &&
+        !s->match.product_id &&
+        !s->match.port) {
+        s->needs_autoscan = false;
+        ldev = usb_host_find_ref(s->match.bus_num,
+                                 s->match.addr);
+        if (!ldev) {
+            error_setg(errp, "failed to find host usb device %d:%d",
+                       s->match.bus_num, s->match.addr);
+            return;
+        }
+        rc = usb_host_open(s, ldev);
+        libusb_unref_device(ldev);
+        if (rc < 0) {
+            error_setg(errp, "failed to open host usb device %d:%d",
+                       s->match.bus_num, s->match.addr);
+            return;
+        }
+    } else {
+        s->needs_autoscan = true;
+        QTAILQ_INSERT_TAIL(&hostdevs, s, next);
+        usb_host_auto_check(NULL);
+    }
+
     s->exit.notify = usb_host_exit_notifier;
     qemu_add_exit_notifier(&s->exit);
-
-    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
-    usb_host_auto_check(NULL);
 }
 
 static void usb_host_instance_init(Object *obj)
@@ -1019,7 +1065,9 @@ static void usb_host_handle_destroy(USBDevice *udev)
     USBHostDevice *s = USB_HOST_DEVICE(udev);
 
     qemu_remove_exit_notifier(&s->exit);
-    QTAILQ_REMOVE(&hostdevs, s, next);
+    if (s->needs_autoscan) {
+        QTAILQ_REMOVE(&hostdevs, s, next);
+    }
     usb_host_close(s);
 }