summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hw/usb.h1
-rw-r--r--usb-linux.c209
-rw-r--r--vl.c57
3 files changed, 225 insertions, 42 deletions
diff --git a/hw/usb.h b/hw/usb.h
index 2edb982e98..4a009a5848 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -197,6 +197,7 @@ static inline void usb_cancel_packet(USBPacket * p)
     p->cancel_cb(p, p->cancel_opaque);
 }
 
+int usb_device_add_dev(USBDevice *dev);
 int usb_device_del_addr(int bus_num, int addr);
 void usb_attach(USBPort *port, USBDevice *dev);
 int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
diff --git a/usb-linux.c b/usb-linux.c
index 0023c1dfd5..622255c9e2 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -3,6 +3,9 @@
  *
  * Copyright (c) 2005 Fabrice Bellard
  *
+ * Support for host device auto connect & disconnect
+ *       Copyright (c) 2008 Max Krasnyansky
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
@@ -67,6 +70,8 @@ struct endp_data {
     uint8_t type;
 };
 
+
+
 /* FIXME: move USBPacket to PendingURB */
 typedef struct USBHostDevice {
     USBDevice dev;
@@ -78,9 +83,51 @@ typedef struct USBHostDevice {
     uint8_t descr[1024];
     int descr_len;
     int urbs_ready;
+
     QEMUTimer *timer;
+
+    /* Host side address */
+    int bus_num;
+    int addr;
+
+    struct USBHostDevice *next;
 } USBHostDevice;
 
+static USBHostDevice *hostdev_list;
+
+static void hostdev_link(USBHostDevice *dev)
+{
+    dev->next = hostdev_list;
+    hostdev_list = dev;
+}
+
+static void hostdev_unlink(USBHostDevice *dev)
+{
+    USBHostDevice *pdev = hostdev_list;
+    USBHostDevice **prev = &hostdev_list;
+
+    while (pdev) {
+	if (pdev == dev) {
+            *prev = dev->next;
+            return;
+        }
+
+        prev = &pdev->next;
+        pdev = pdev->next;
+    }
+}
+
+static USBHostDevice *hostdev_find(int bus_num, int addr)
+{
+    USBHostDevice *s = hostdev_list;
+    while (s) {
+        if (s->bus_num == bus_num && s->addr == addr)
+            return s;
+        s = s->next;
+    }
+    return NULL;
+}
+
 typedef struct PendingURB {
     struct usbdevfs_urb *urb;
     int status;
@@ -238,6 +285,8 @@ static void usb_host_handle_destroy(USBDevice *dev)
 
     qemu_del_timer(s->timer);
 
+    hostdev_unlink(s);
+
     if (s->fd >= 0)
         close(s->fd);
 
@@ -619,32 +668,26 @@ static void usb_host_device_check(void *priv)
     qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
 }
 
-/* XXX: exclude high speed devices or implement EHCI */
-USBDevice *usb_host_device_open(const char *devname)
+static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *prod_name)
 {
     int fd = -1, ret;
     USBHostDevice *dev = NULL;
     struct usbdevfs_connectinfo ci;
     char buf[1024];
-    int bus_num, addr;
-    char product_name[PRODUCT_NAME_SZ];
-
-    if (usb_host_find_device(&bus_num, &addr,
-                             product_name, sizeof(product_name),
-                             devname) < 0)
-        return NULL;
-
 
     dev = qemu_mallocz(sizeof(USBHostDevice));
     if (!dev)
         goto fail;
 
+    dev->bus_num = bus_num;
+    dev->addr = addr;
+
     dev->timer = qemu_new_timer(rt_clock, usb_host_device_check, (void *) dev);
     if (!dev->timer)
 	goto fail;
 
 #ifdef DEBUG
-    printf("usb_host_device_open %s\n", devname);
+    printf("usb_host_device_open %d.%d\n", bus_num, addr);
 #endif
 
     snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
@@ -704,12 +747,12 @@ USBDevice *usb_host_device_open(const char *devname)
     dev->dev.handle_data = usb_host_handle_data;
     dev->dev.handle_destroy = usb_host_handle_destroy;
 
-    if (product_name[0] == '\0')
+    if (!prod_name || prod_name[0] == '\0')
         snprintf(dev->dev.devname, sizeof(dev->dev.devname),
-                 "host:%s", devname);
+                 "host:%d.%d", bus_num, addr);
     else
         pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
-                product_name);
+                prod_name);
 
 #ifdef USE_ASYNCIO
     /* set up the signal handlers */
@@ -735,8 +778,11 @@ USBDevice *usb_host_device_open(const char *devname)
     /* Start the timer to detect disconnect */
     qemu_mod_timer(dev->timer, qemu_get_clock(rt_clock) + 1000);
 
+    hostdev_link(dev);
+
     dev->urbs_ready = 0;
     return (USBDevice *)dev;
+
 fail:
     if (dev) {
 	if (dev->timer)
@@ -747,6 +793,24 @@ fail:
     return NULL;
 }
 
+USBDevice *usb_host_device_open(const char *devname)
+{
+    int bus_num, addr;
+    char product_name[PRODUCT_NAME_SZ];
+
+    if (usb_host_find_device(&bus_num, &addr,
+                             product_name, sizeof(product_name),
+                             devname) < 0)
+        return NULL;
+
+     if (hostdev_find(bus_num, addr)) {
+        printf("host usb device %d.%d is already open\n", bus_num, addr);
+        return NULL;
+     }
+
+    return usb_host_device_open_addr(bus_num, addr, product_name);
+}
+ 
 static int get_tag_value(char *buf, int buf_size,
                          const char *str, const char *tag,
                          const char *stopchars)
@@ -846,6 +910,108 @@ static int usb_host_scan(void *opaque, USBScanFunc *func)
     return ret;
 }
 
+struct USBAutoFilter {
+    struct USBAutoFilter *next;
+    int bus_num;
+    int addr;
+    int vendor_id;
+    int product_id;
+};
+
+static QEMUTimer *usb_auto_timer;
+static struct USBAutoFilter *usb_auto_filter;
+
+static int usb_host_auto_scan(void *opaque, int bus_num, int addr,
+                     int class_id, int vendor_id, int product_id,
+                     const char *product_name, int speed)
+{
+    struct USBAutoFilter *f;
+    struct USBDevice *dev;
+
+    /* Ignore hubs */
+    if (class_id == 9)
+        return 0;
+
+    for (f = usb_auto_filter; f; f = f->next) {
+        // printf("Auto match: bus_num %d addr %d vid %d pid %d\n",
+	//    bus_num, addr, vendor_id, product_id);
+
+	if (f->bus_num >= 0 && f->bus_num != bus_num)
+            continue;
+
+	if (f->addr >= 0 && f->addr != addr)
+            continue;
+
+	if (f->vendor_id >= 0 && f->vendor_id != vendor_id)
+            continue;
+
+	if (f->product_id >= 0 && f->product_id != product_id)
+            continue;
+
+        /* We got a match */
+
+        /* Allredy attached ? */
+        if (hostdev_find(bus_num, addr))
+            return 0;
+
+        printf("Auto open: bus_num %d addr %d\n", bus_num, addr);
+
+	dev = usb_host_device_open_addr(bus_num, addr, product_name);
+	if (dev)
+	    usb_device_add_dev(dev);
+    }
+
+    return 0;
+}
+
+static void usb_host_auto_timer(void *unused)
+{
+    usb_host_scan(NULL, usb_host_auto_scan);
+    qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
+}
+
+/*
+ * Add autoconnect filter
+ * -1 means 'any' (device, vendor, etc)
+ */
+static void usb_host_auto_add(int bus_num, int addr, int vendor_id, int product_id)
+{
+    struct USBAutoFilter *f = qemu_mallocz(sizeof(*f));
+    if (!f) {
+        printf("Failed to allocate auto filter\n");
+	return;
+    }
+
+    f->bus_num = bus_num;
+    f->addr    = addr;
+    f->vendor_id  = vendor_id;
+    f->product_id = product_id;
+
+    if (!usb_auto_filter) {
+        /*
+         * First entry. Init and start the monitor.
+         * Right now we're using timer to check for new devices.
+         * If this turns out to be too expensive we can move that into a 
+         * separate thread.
+         */
+	usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_timer, NULL);
+	if (!usb_auto_timer) {
+            printf("Failed to allocate timer\n");
+            qemu_free(f);
+            return;
+        }
+
+        /* Check for new devices every two seconds */
+        qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
+    }
+
+    printf("Auto filter: bus_num %d addr %d vid %d pid %d\n",
+	bus_num, addr, vendor_id, product_id);
+
+    f->next = usb_auto_filter;
+    usb_auto_filter = f;
+}
+
 typedef struct FindDeviceState {
     int vendor_id;
     int product_id;
@@ -887,6 +1053,12 @@ static int usb_host_find_device(int *pbus_num, int *paddr,
     p = strchr(devname, '.');
     if (p) {
         *pbus_num = strtoul(devname, NULL, 0);
+
+        if (*(p + 1) == '*') {
+            usb_host_auto_add(*pbus_num, -1, -1, -1);
+	    return -1;
+	}
+
         *paddr = strtoul(p + 1, NULL, 0);
         fs.bus_num = *pbus_num;
         fs.addr = *paddr;
@@ -898,6 +1070,12 @@ static int usb_host_find_device(int *pbus_num, int *paddr,
     p = strchr(devname, ':');
     if (p) {
         fs.vendor_id = strtoul(devname, NULL, 16);
+
+        if (*(p + 1) == '*') {
+            usb_host_auto_add(-1, -1, fs.vendor_id, -1);
+	    return -1;
+	}
+
         fs.product_id = strtoul(p + 1, NULL, 16);
         ret = usb_host_scan(&fs, usb_host_find_device_scan);
         if (ret) {
@@ -996,6 +1174,9 @@ void usb_host_info(void)
     usb_host_scan(NULL, usb_host_info_device);
 }
 
+
+
+
 #else
 
 void usb_host_info(void)
diff --git a/vl.c b/vl.c
index 22242489dd..a4838b410d 100644
--- a/vl.c
+++ b/vl.c
@@ -5747,6 +5747,32 @@ void qemu_register_usb_port(USBPort *port, void *opaque, int index,
     free_usb_ports = port;
 }
 
+int usb_device_add_dev(USBDevice *dev)
+{
+    USBPort *port;
+
+    /* Find a USB port to add the device to.  */
+    port = free_usb_ports;
+    if (!port->next) {
+        USBDevice *hub;
+
+        /* Create a new hub and chain it on.  */
+        free_usb_ports = NULL;
+        port->next = used_usb_ports;
+        used_usb_ports = port;
+
+        hub = usb_hub_init(VM_USB_HUB_SIZE);
+        usb_attach(port, hub);
+        port = free_usb_ports;
+    }
+
+    free_usb_ports = port->next;
+    port->next = used_usb_ports;
+    used_usb_ports = port;
+    usb_attach(port, dev);
+    return 0;
+}
+
 static int usb_device_add(const char *devname)
 {
     const char *p;
@@ -5787,26 +5813,7 @@ static int usb_device_add(const char *devname)
     if (!dev)
         return -1;
 
-    /* Find a USB port to add the device to.  */
-    port = free_usb_ports;
-    if (!port->next) {
-        USBDevice *hub;
-
-        /* Create a new hub and chain it on.  */
-        free_usb_ports = NULL;
-        port->next = used_usb_ports;
-        used_usb_ports = port;
-
-        hub = usb_hub_init(VM_USB_HUB_SIZE);
-        usb_attach(port, hub);
-        port = free_usb_ports;
-    }
-
-    free_usb_ports = port->next;
-    port->next = used_usb_ports;
-    used_usb_ports = port;
-    usb_attach(port, dev);
-    return 0;
+    return usb_device_add_dev(dev);
 }
 
 int usb_device_del_addr(int bus_num, int addr)
@@ -5859,18 +5866,12 @@ static int usb_device_del(const char *devname)
 
 void do_usb_add(const char *devname)
 {
-    int ret;
-    ret = usb_device_add(devname);
-    if (ret < 0)
-        term_printf("Could not add USB device '%s'\n", devname);
+    usb_device_add(devname);
 }
 
 void do_usb_del(const char *devname)
 {
-    int ret;
-    ret = usb_device_del(devname);
-    if (ret < 0)
-        term_printf("Could not remove USB device '%s'\n", devname);
+    usb_device_del(devname);
 }
 
 void usb_info(void)