aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>2008-08-21 19:27:48 +0000
committeraliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>2008-08-21 19:27:48 +0000
commit1f3870ab242018b724b845957f7f928a2d7c1f5b (patch)
treeb6d330c5e7906e7935310141be310c33b44fcefa
parentcd01b4a312248dd4e12c3d389d1a349cea4015d8 (diff)
husb: support for USB host device auto disconnect (Max Krasnyansky)
I got really annoyed by the fact that you have to manually do usb_del in the monitor when host device is unplugged and decided to fix it :) Basically we now automatically remove guest USB device when the actual host device is disconnected. At first I've extended set_fd_handlerX() stuff to support checking for exceptions on fds. But unfortunately usbfs code does not wake up user-space process when device is removed, which means we need a timer to periodically check if device is still there. So I removed fd exception stuff and implemented it with the timer. Signed-off-by: Max Krasnyansky <maxk@kernel.org> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5047 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--hw/usb.h1
-rw-r--r--usb-linux.c56
-rw-r--r--vl.c26
3 files changed, 66 insertions, 17 deletions
diff --git a/hw/usb.h b/hw/usb.h
index 8bdc68d545..2edb982e98 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_del_addr(int bus_num, int addr);
void usb_attach(USBPort *port, USBDevice *dev);
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
int set_usb_string(uint8_t *buf, const char *str);
diff --git a/usb-linux.c b/usb-linux.c
index d3e4e2ef45..0023c1dfd5 100644
--- a/usb-linux.c
+++ b/usb-linux.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include "qemu-common.h"
+#include "qemu-timer.h"
#include "hw/usb.h"
#include "console.h"
@@ -77,6 +78,7 @@ typedef struct USBHostDevice {
uint8_t descr[1024];
int descr_len;
int urbs_ready;
+ QEMUTimer *timer;
} USBHostDevice;
typedef struct PendingURB {
@@ -165,7 +167,11 @@ static int usb_host_update_interfaces(USBHostDevice *dev, int configuration)
}
config_descr_len = dev->descr[i];
- if (configuration == dev->descr[i + 5])
+#ifdef DEBUG
+ printf("config #%d need %d\n", dev->descr[i + 5], configuration);
+#endif
+
+ if (configuration < 0 || configuration == dev->descr[i + 5])
break;
i += config_descr_len;
@@ -230,8 +236,11 @@ static void usb_host_handle_destroy(USBDevice *dev)
{
USBHostDevice *s = (USBHostDevice *)dev;
+ qemu_del_timer(s->timer);
+
if (s->fd >= 0)
close(s->fd);
+
qemu_free(s);
}
@@ -594,6 +603,22 @@ static int usb_linux_update_endp_table(USBHostDevice *s)
return 0;
}
+static void usb_host_device_check(void *priv)
+{
+ USBHostDevice *s = priv;
+ struct usbdevfs_connectinfo ci;
+ int err;
+
+ err = ioctl(s->fd, USBDEVFS_CONNECTINFO, &ci);
+ if (err < 0) {
+ printf("usb device %d.%d disconnected\n", 0, s->dev.addr);
+ usb_device_del_addr(0, s->dev.addr);
+ return;
+ }
+
+ 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)
{
@@ -604,24 +629,30 @@ 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;
+
+
dev = qemu_mallocz(sizeof(USBHostDevice));
if (!dev)
goto fail;
-#ifdef DEBUG_ISOCH
+ 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);
#endif
- if (usb_host_find_device(&bus_num, &addr,
- product_name, sizeof(product_name),
- devname) < 0)
- return NULL;
snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
bus_num, addr);
fd = open(buf, O_RDWR | O_NONBLOCK);
if (fd < 0) {
perror(buf);
- return NULL;
+ goto fail;
}
/* read the device description */
@@ -645,7 +676,7 @@ USBDevice *usb_host_device_open(const char *devname)
dev->configuration = 1;
/* XXX - do something about initial configuration */
- if (!usb_host_update_interfaces(dev, 1))
+ if (!usb_host_update_interfaces(dev, -1))
goto fail;
ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
@@ -700,11 +731,18 @@ USBDevice *usb_host_device_open(const char *devname)
fcntl(dev->pipe_fds[1], F_SETFL, O_NONBLOCK);
qemu_set_fd_handler(dev->pipe_fds[0], urb_completion_pipe_read, NULL, dev);
#endif
+
+ /* Start the timer to detect disconnect */
+ qemu_mod_timer(dev->timer, qemu_get_clock(rt_clock) + 1000);
+
dev->urbs_ready = 0;
return (USBDevice *)dev;
fail:
- if (dev)
+ if (dev) {
+ if (dev->timer)
+ qemu_del_timer(dev->timer);
qemu_free(dev);
+ }
close(fd);
return NULL;
}
diff --git a/vl.c b/vl.c
index 39503a3929..22242489dd 100644
--- a/vl.c
+++ b/vl.c
@@ -5809,22 +5809,15 @@ static int usb_device_add(const char *devname)
return 0;
}
-static int usb_device_del(const char *devname)
+int usb_device_del_addr(int bus_num, int addr)
{
USBPort *port;
USBPort **lastp;
USBDevice *dev;
- int bus_num, addr;
- const char *p;
if (!used_usb_ports)
return -1;
- p = strchr(devname, '.');
- if (!p)
- return -1;
- bus_num = strtoul(devname, NULL, 0);
- addr = strtoul(p + 1, NULL, 0);
if (bus_num != 0)
return -1;
@@ -5847,6 +5840,23 @@ static int usb_device_del(const char *devname)
return 0;
}
+static int usb_device_del(const char *devname)
+{
+ int bus_num, addr;
+ const char *p;
+
+ if (!used_usb_ports)
+ return -1;
+
+ p = strchr(devname, '.');
+ if (!p)
+ return -1;
+ bus_num = strtoul(devname, NULL, 0);
+ addr = strtoul(p + 1, NULL, 0);
+
+ return usb_device_del_addr(bus_num, addr);
+}
+
void do_usb_add(const char *devname)
{
int ret;