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; | ... | ... |