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 | 197 | p->cancel_cb(p, p->cancel_opaque); |
198 | 198 | } |
199 | 199 | |
200 | +int usb_device_del_addr(int bus_num, int addr); | |
200 | 201 | void usb_attach(USBPort *port, USBDevice *dev); |
201 | 202 | int usb_generic_handle_packet(USBDevice *s, USBPacket *p); |
202 | 203 | int set_usb_string(uint8_t *buf, const char *str); | ... | ... |
usb-linux.c
... | ... | @@ -22,6 +22,7 @@ |
22 | 22 | * THE SOFTWARE. |
23 | 23 | */ |
24 | 24 | #include "qemu-common.h" |
25 | +#include "qemu-timer.h" | |
25 | 26 | #include "hw/usb.h" |
26 | 27 | #include "console.h" |
27 | 28 | |
... | ... | @@ -77,6 +78,7 @@ typedef struct USBHostDevice { |
77 | 78 | uint8_t descr[1024]; |
78 | 79 | int descr_len; |
79 | 80 | int urbs_ready; |
81 | + QEMUTimer *timer; | |
80 | 82 | } USBHostDevice; |
81 | 83 | |
82 | 84 | typedef struct PendingURB { |
... | ... | @@ -165,7 +167,11 @@ static int usb_host_update_interfaces(USBHostDevice *dev, int configuration) |
165 | 167 | } |
166 | 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 | 175 | break; |
170 | 176 | |
171 | 177 | i += config_descr_len; |
... | ... | @@ -230,8 +236,11 @@ static void usb_host_handle_destroy(USBDevice *dev) |
230 | 236 | { |
231 | 237 | USBHostDevice *s = (USBHostDevice *)dev; |
232 | 238 | |
239 | + qemu_del_timer(s->timer); | |
240 | + | |
233 | 241 | if (s->fd >= 0) |
234 | 242 | close(s->fd); |
243 | + | |
235 | 244 | qemu_free(s); |
236 | 245 | } |
237 | 246 | |
... | ... | @@ -594,6 +603,22 @@ static int usb_linux_update_endp_table(USBHostDevice *s) |
594 | 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 | 622 | /* XXX: exclude high speed devices or implement EHCI */ |
598 | 623 | USBDevice *usb_host_device_open(const char *devname) |
599 | 624 | { |
... | ... | @@ -604,24 +629,30 @@ USBDevice *usb_host_device_open(const char *devname) |
604 | 629 | int bus_num, addr; |
605 | 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 | 638 | dev = qemu_mallocz(sizeof(USBHostDevice)); |
608 | 639 | if (!dev) |
609 | 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 | 647 | printf("usb_host_device_open %s\n", devname); |
613 | 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 | 650 | snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d", |
620 | 651 | bus_num, addr); |
621 | 652 | fd = open(buf, O_RDWR | O_NONBLOCK); |
622 | 653 | if (fd < 0) { |
623 | 654 | perror(buf); |
624 | - return NULL; | |
655 | + goto fail; | |
625 | 656 | } |
626 | 657 | |
627 | 658 | /* read the device description */ |
... | ... | @@ -645,7 +676,7 @@ USBDevice *usb_host_device_open(const char *devname) |
645 | 676 | dev->configuration = 1; |
646 | 677 | |
647 | 678 | /* XXX - do something about initial configuration */ |
648 | - if (!usb_host_update_interfaces(dev, 1)) | |
679 | + if (!usb_host_update_interfaces(dev, -1)) | |
649 | 680 | goto fail; |
650 | 681 | |
651 | 682 | ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); |
... | ... | @@ -700,11 +731,18 @@ USBDevice *usb_host_device_open(const char *devname) |
700 | 731 | fcntl(dev->pipe_fds[1], F_SETFL, O_NONBLOCK); |
701 | 732 | qemu_set_fd_handler(dev->pipe_fds[0], urb_completion_pipe_read, NULL, dev); |
702 | 733 | #endif |
734 | + | |
735 | + /* Start the timer to detect disconnect */ | |
736 | + qemu_mod_timer(dev->timer, qemu_get_clock(rt_clock) + 1000); | |
737 | + | |
703 | 738 | dev->urbs_ready = 0; |
704 | 739 | return (USBDevice *)dev; |
705 | 740 | fail: |
706 | - if (dev) | |
741 | + if (dev) { | |
742 | + if (dev->timer) | |
743 | + qemu_del_timer(dev->timer); | |
707 | 744 | qemu_free(dev); |
745 | + } | |
708 | 746 | close(fd); |
709 | 747 | return NULL; |
710 | 748 | } | ... | ... |
vl.c
... | ... | @@ -5809,22 +5809,15 @@ static int usb_device_add(const char *devname) |
5809 | 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 | 5814 | USBPort *port; |
5815 | 5815 | USBPort **lastp; |
5816 | 5816 | USBDevice *dev; |
5817 | - int bus_num, addr; | |
5818 | - const char *p; | |
5819 | 5817 | |
5820 | 5818 | if (!used_usb_ports) |
5821 | 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 | 5821 | if (bus_num != 0) |
5829 | 5822 | return -1; |
5830 | 5823 | |
... | ... | @@ -5847,6 +5840,23 @@ static int usb_device_del(const char *devname) |
5847 | 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 | 5860 | void do_usb_add(const char *devname) |
5851 | 5861 | { |
5852 | 5862 | int ret; | ... | ... |