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
This commit is contained in:
aliguori 2008-08-21 19:27:48 +00:00
parent cd01b4a312
commit 1f3870ab24
3 changed files with 68 additions and 19 deletions

View File

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

View File

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

26
vl.c
View File

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