Commit 1f3870ab242018b724b845957f7f928a2d7c1f5b
1 parent
cd01b4a3
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
Showing
3 changed files
with
66 additions
and
17 deletions
hw/usb.h
@@ -197,6 +197,7 @@ static inline void usb_cancel_packet(USBPacket * p) | @@ -197,6 +197,7 @@ static inline void usb_cancel_packet(USBPacket * p) | ||
197 | p->cancel_cb(p, p->cancel_opaque); | 197 | p->cancel_cb(p, p->cancel_opaque); |
198 | } | 198 | } |
199 | 199 | ||
200 | +int usb_device_del_addr(int bus_num, int addr); | ||
200 | void usb_attach(USBPort *port, USBDevice *dev); | 201 | void usb_attach(USBPort *port, USBDevice *dev); |
201 | int usb_generic_handle_packet(USBDevice *s, USBPacket *p); | 202 | int usb_generic_handle_packet(USBDevice *s, USBPacket *p); |
202 | int set_usb_string(uint8_t *buf, const char *str); | 203 | int set_usb_string(uint8_t *buf, const char *str); |
usb-linux.c
@@ -22,6 +22,7 @@ | @@ -22,6 +22,7 @@ | ||
22 | * THE SOFTWARE. | 22 | * THE SOFTWARE. |
23 | */ | 23 | */ |
24 | #include "qemu-common.h" | 24 | #include "qemu-common.h" |
25 | +#include "qemu-timer.h" | ||
25 | #include "hw/usb.h" | 26 | #include "hw/usb.h" |
26 | #include "console.h" | 27 | #include "console.h" |
27 | 28 | ||
@@ -77,6 +78,7 @@ typedef struct USBHostDevice { | @@ -77,6 +78,7 @@ typedef struct USBHostDevice { | ||
77 | uint8_t descr[1024]; | 78 | uint8_t descr[1024]; |
78 | int descr_len; | 79 | int descr_len; |
79 | int urbs_ready; | 80 | int urbs_ready; |
81 | + QEMUTimer *timer; | ||
80 | } USBHostDevice; | 82 | } USBHostDevice; |
81 | 83 | ||
82 | typedef struct PendingURB { | 84 | typedef struct PendingURB { |
@@ -165,7 +167,11 @@ static int usb_host_update_interfaces(USBHostDevice *dev, int configuration) | @@ -165,7 +167,11 @@ static int usb_host_update_interfaces(USBHostDevice *dev, int configuration) | ||
165 | } | 167 | } |
166 | config_descr_len = dev->descr[i]; | 168 | config_descr_len = dev->descr[i]; |
167 | 169 | ||
168 | - if (configuration == dev->descr[i + 5]) | 170 | +#ifdef DEBUG |
171 | + printf("config #%d need %d\n", dev->descr[i + 5], configuration); | ||
172 | +#endif | ||
173 | + | ||
174 | + if (configuration < 0 || configuration == dev->descr[i + 5]) | ||
169 | break; | 175 | break; |
170 | 176 | ||
171 | i += config_descr_len; | 177 | i += config_descr_len; |
@@ -230,8 +236,11 @@ static void usb_host_handle_destroy(USBDevice *dev) | @@ -230,8 +236,11 @@ static void usb_host_handle_destroy(USBDevice *dev) | ||
230 | { | 236 | { |
231 | USBHostDevice *s = (USBHostDevice *)dev; | 237 | USBHostDevice *s = (USBHostDevice *)dev; |
232 | 238 | ||
239 | + qemu_del_timer(s->timer); | ||
240 | + | ||
233 | if (s->fd >= 0) | 241 | if (s->fd >= 0) |
234 | close(s->fd); | 242 | close(s->fd); |
243 | + | ||
235 | qemu_free(s); | 244 | qemu_free(s); |
236 | } | 245 | } |
237 | 246 | ||
@@ -594,6 +603,22 @@ static int usb_linux_update_endp_table(USBHostDevice *s) | @@ -594,6 +603,22 @@ static int usb_linux_update_endp_table(USBHostDevice *s) | ||
594 | return 0; | 603 | return 0; |
595 | } | 604 | } |
596 | 605 | ||
606 | +static void usb_host_device_check(void *priv) | ||
607 | +{ | ||
608 | + USBHostDevice *s = priv; | ||
609 | + struct usbdevfs_connectinfo ci; | ||
610 | + int err; | ||
611 | + | ||
612 | + err = ioctl(s->fd, USBDEVFS_CONNECTINFO, &ci); | ||
613 | + if (err < 0) { | ||
614 | + printf("usb device %d.%d disconnected\n", 0, s->dev.addr); | ||
615 | + usb_device_del_addr(0, s->dev.addr); | ||
616 | + return; | ||
617 | + } | ||
618 | + | ||
619 | + qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000); | ||
620 | +} | ||
621 | + | ||
597 | /* XXX: exclude high speed devices or implement EHCI */ | 622 | /* XXX: exclude high speed devices or implement EHCI */ |
598 | USBDevice *usb_host_device_open(const char *devname) | 623 | USBDevice *usb_host_device_open(const char *devname) |
599 | { | 624 | { |
@@ -604,24 +629,30 @@ USBDevice *usb_host_device_open(const char *devname) | @@ -604,24 +629,30 @@ USBDevice *usb_host_device_open(const char *devname) | ||
604 | int bus_num, addr; | 629 | int bus_num, addr; |
605 | char product_name[PRODUCT_NAME_SZ]; | 630 | char product_name[PRODUCT_NAME_SZ]; |
606 | 631 | ||
632 | + if (usb_host_find_device(&bus_num, &addr, | ||
633 | + product_name, sizeof(product_name), | ||
634 | + devname) < 0) | ||
635 | + return NULL; | ||
636 | + | ||
637 | + | ||
607 | dev = qemu_mallocz(sizeof(USBHostDevice)); | 638 | dev = qemu_mallocz(sizeof(USBHostDevice)); |
608 | if (!dev) | 639 | if (!dev) |
609 | goto fail; | 640 | goto fail; |
610 | 641 | ||
611 | -#ifdef DEBUG_ISOCH | 642 | + dev->timer = qemu_new_timer(rt_clock, usb_host_device_check, (void *) dev); |
643 | + if (!dev->timer) | ||
644 | + goto fail; | ||
645 | + | ||
646 | +#ifdef DEBUG | ||
612 | printf("usb_host_device_open %s\n", devname); | 647 | printf("usb_host_device_open %s\n", devname); |
613 | #endif | 648 | #endif |
614 | - if (usb_host_find_device(&bus_num, &addr, | ||
615 | - product_name, sizeof(product_name), | ||
616 | - devname) < 0) | ||
617 | - return NULL; | ||
618 | 649 | ||
619 | snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d", | 650 | snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d", |
620 | bus_num, addr); | 651 | bus_num, addr); |
621 | fd = open(buf, O_RDWR | O_NONBLOCK); | 652 | fd = open(buf, O_RDWR | O_NONBLOCK); |
622 | if (fd < 0) { | 653 | if (fd < 0) { |
623 | perror(buf); | 654 | perror(buf); |
624 | - return NULL; | 655 | + goto fail; |
625 | } | 656 | } |
626 | 657 | ||
627 | /* read the device description */ | 658 | /* read the device description */ |
@@ -645,7 +676,7 @@ USBDevice *usb_host_device_open(const char *devname) | @@ -645,7 +676,7 @@ USBDevice *usb_host_device_open(const char *devname) | ||
645 | dev->configuration = 1; | 676 | dev->configuration = 1; |
646 | 677 | ||
647 | /* XXX - do something about initial configuration */ | 678 | /* XXX - do something about initial configuration */ |
648 | - if (!usb_host_update_interfaces(dev, 1)) | 679 | + if (!usb_host_update_interfaces(dev, -1)) |
649 | goto fail; | 680 | goto fail; |
650 | 681 | ||
651 | ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); | 682 | ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); |
@@ -700,11 +731,18 @@ USBDevice *usb_host_device_open(const char *devname) | @@ -700,11 +731,18 @@ USBDevice *usb_host_device_open(const char *devname) | ||
700 | fcntl(dev->pipe_fds[1], F_SETFL, O_NONBLOCK); | 731 | fcntl(dev->pipe_fds[1], F_SETFL, O_NONBLOCK); |
701 | qemu_set_fd_handler(dev->pipe_fds[0], urb_completion_pipe_read, NULL, dev); | 732 | qemu_set_fd_handler(dev->pipe_fds[0], urb_completion_pipe_read, NULL, dev); |
702 | #endif | 733 | #endif |
734 | + | ||
735 | + /* Start the timer to detect disconnect */ | ||
736 | + qemu_mod_timer(dev->timer, qemu_get_clock(rt_clock) + 1000); | ||
737 | + | ||
703 | dev->urbs_ready = 0; | 738 | dev->urbs_ready = 0; |
704 | return (USBDevice *)dev; | 739 | return (USBDevice *)dev; |
705 | fail: | 740 | fail: |
706 | - if (dev) | 741 | + if (dev) { |
742 | + if (dev->timer) | ||
743 | + qemu_del_timer(dev->timer); | ||
707 | qemu_free(dev); | 744 | qemu_free(dev); |
745 | + } | ||
708 | close(fd); | 746 | close(fd); |
709 | return NULL; | 747 | return NULL; |
710 | } | 748 | } |
vl.c
@@ -5809,22 +5809,15 @@ static int usb_device_add(const char *devname) | @@ -5809,22 +5809,15 @@ static int usb_device_add(const char *devname) | ||
5809 | return 0; | 5809 | return 0; |
5810 | } | 5810 | } |
5811 | 5811 | ||
5812 | -static int usb_device_del(const char *devname) | 5812 | +int usb_device_del_addr(int bus_num, int addr) |
5813 | { | 5813 | { |
5814 | USBPort *port; | 5814 | USBPort *port; |
5815 | USBPort **lastp; | 5815 | USBPort **lastp; |
5816 | USBDevice *dev; | 5816 | USBDevice *dev; |
5817 | - int bus_num, addr; | ||
5818 | - const char *p; | ||
5819 | 5817 | ||
5820 | if (!used_usb_ports) | 5818 | if (!used_usb_ports) |
5821 | return -1; | 5819 | return -1; |
5822 | 5820 | ||
5823 | - p = strchr(devname, '.'); | ||
5824 | - if (!p) | ||
5825 | - return -1; | ||
5826 | - bus_num = strtoul(devname, NULL, 0); | ||
5827 | - addr = strtoul(p + 1, NULL, 0); | ||
5828 | if (bus_num != 0) | 5821 | if (bus_num != 0) |
5829 | return -1; | 5822 | return -1; |
5830 | 5823 | ||
@@ -5847,6 +5840,23 @@ static int usb_device_del(const char *devname) | @@ -5847,6 +5840,23 @@ static int usb_device_del(const char *devname) | ||
5847 | return 0; | 5840 | return 0; |
5848 | } | 5841 | } |
5849 | 5842 | ||
5843 | +static int usb_device_del(const char *devname) | ||
5844 | +{ | ||
5845 | + int bus_num, addr; | ||
5846 | + const char *p; | ||
5847 | + | ||
5848 | + if (!used_usb_ports) | ||
5849 | + return -1; | ||
5850 | + | ||
5851 | + p = strchr(devname, '.'); | ||
5852 | + if (!p) | ||
5853 | + return -1; | ||
5854 | + bus_num = strtoul(devname, NULL, 0); | ||
5855 | + addr = strtoul(p + 1, NULL, 0); | ||
5856 | + | ||
5857 | + return usb_device_del_addr(bus_num, addr); | ||
5858 | +} | ||
5859 | + | ||
5850 | void do_usb_add(const char *devname) | 5860 | void do_usb_add(const char *devname) |
5851 | { | 5861 | { |
5852 | int ret; | 5862 | int ret; |