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