From 1f3870ab242018b724b845957f7f928a2d7c1f5b Mon Sep 17 00:00:00 2001 From: aliguori Date: Thu, 21 Aug 2008 19:27:48 +0000 Subject: [PATCH] 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 Signed-off-by: Anthony Liguori git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5047 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/usb.h | 1 + usb-linux.c | 60 +++++++++++++++++++++++++++++++++++++++++++---------- vl.c | 26 ++++++++++++++++------- 3 files changed, 68 insertions(+), 19 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]; - dev = qemu_mallocz(sizeof(USBHostDevice)); - if (!dev) - goto fail; - -#ifdef DEBUG_ISOCH - 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; + + dev = qemu_mallocz(sizeof(USBHostDevice)); + if (!dev) + goto fail; + + 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 + 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;