aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-11-17 12:47:15 +0100
committerGerd Hoffmann <kraxel@redhat.com>2012-12-04 14:41:54 +0100
commitbe41efde3ca0372dbf7543e09ff473b4eec25057 (patch)
tree56fc5bf4190b7f73613fc4097ca6ad295bd69a98 /hw
parent8beba9304391189666df1b62b23a5101b3831317 (diff)
usb: Don't allow USB_RET_ASYNC for interrupt packets
It is tempting to use USB_RET_ASYNC for interrupt packets, rather then the current NAK + polling approach, but this causes issues for migration, as an async completed packet will not getting written back to guest memory until the next poll time, and if a migration happens in between it will get lost! Make an exception for host devices, because: 1) host-linux actually uses async completion for interrupt endpoints 2) host devices don't migrate anyways Ideally we would convert host-linux.c to handle (input) interrupt endpoints in a buffered manner like it does for isoc endpoints, keeping multiple urbs submitted to ensure the devices timing requirements are met, as well as making its interrupt ep handling the same as other usb-devices. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/usb.h1
-rw-r--r--hw/usb/core.c4
-rw-r--r--hw/usb/host-bsd.c1
-rw-r--r--hw/usb/host-linux.c1
4 files changed, 7 insertions, 0 deletions
diff --git a/hw/usb.h b/hw/usb.h
index 7d6de69ec4..58f812f7d0 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -197,6 +197,7 @@ struct USBEndpoint {
enum USBDeviceFlags {
USB_DEV_FLAG_FULL_PATH,
+ USB_DEV_FLAG_IS_HOST,
};
/* definition of a USB device */
diff --git a/hw/usb/core.c b/hw/usb/core.c
index 52b53108cd..8e360d3ec0 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -406,7 +406,11 @@ void usb_handle_packet(USBDevice *dev, USBPacket *p)
if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
usb_process_one(p);
if (p->status == USB_RET_ASYNC) {
+ /* hcd drivers cannot handle async for isoc */
assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
+ /* using async for interrupt packets breaks migration */
+ assert(p->ep->type != USB_ENDPOINT_XFER_INT ||
+ (dev->flags & USB_DEV_FLAG_IS_HOST));
usb_packet_set_state(p, USB_PACKET_ASYNC);
QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
} else if (p->status == USB_RET_ADD_TO_QUEUE) {
diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c
index 6473e8b747..dae0009378 100644
--- a/hw/usb/host-bsd.c
+++ b/hw/usb/host-bsd.c
@@ -292,6 +292,7 @@ static void usb_host_handle_destroy(USBDevice *opaque)
static int usb_host_initfn(USBDevice *dev)
{
+ dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
return 0;
}
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index aa77b7704d..bdafb6bc87 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -1476,6 +1476,7 @@ static int usb_host_initfn(USBDevice *dev)
{
USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
+ dev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
dev->auto_attach = 0;
s->fd = -1;
s->hub_fd = -1;