diff --git a/.gitignore b/.gitignore
index 0ad020f..0314ae9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@ qemu-kvm-0.13.0-25fdf4a.tar.gz
 /qemu-1.2-0.1.20120806git3e430569.fc18.src.rpm
 /qemu-kvm-1.2-3e430569.tar.gz
 /qemu-kvm-1.2.0-rc1.tar.gz
+/qemu-kvm-1.2.0.tar.gz
diff --git a/0201-spice-abort-on-invalid-streaming-cmdline-params.patch b/0201-spice-abort-on-invalid-streaming-cmdline-params.patch
new file mode 100644
index 0000000..fa5c5b1
--- /dev/null
+++ b/0201-spice-abort-on-invalid-streaming-cmdline-params.patch
@@ -0,0 +1,38 @@
+From 46851363e2aed89fc535803c8a23f6bed9934312 Mon Sep 17 00:00:00 2001
+From: Christophe Fergeau <cfergeau@redhat.com>
+Date: Mon, 13 Aug 2012 10:32:32 +0200
+Subject: [PATCH 201/215] spice: abort on invalid streaming cmdline params
+
+When parsing its command line parameters, spice aborts when it
+finds unexpected values, except for the 'streaming-video' option.
+This happens because the parsing of the parameters for this option
+is done using the 'name2enum' helper, which does not error out
+on unknown values. Using the 'parse_name' helper makes sure we
+error out in this case. Looking at git history, the use of
+'name2enum' instead of 'parse_name' seems to have been an oversight,
+so let's change to that now.
+
+Fixes rhbz#831708
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/spice-core.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/ui/spice-core.c b/ui/spice-core.c
+index 4fc48f8..bb4f585 100644
+--- a/ui/spice-core.c
++++ b/ui/spice-core.c
+@@ -344,7 +344,8 @@ static const char *stream_video_names[] = {
+     [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
+ };
+ #define parse_stream_video(_name) \
+-    name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names))
++    parse_name(_name, "stream video control", \
++               stream_video_names, ARRAY_SIZE(stream_video_names))
+ 
+ static const char *compression_names[] = {
+     [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
+-- 
+1.7.12
+
diff --git a/0202-spice-notify-spice-server-on-vm-start-stop.patch b/0202-spice-notify-spice-server-on-vm-start-stop.patch
new file mode 100644
index 0000000..150bb5c
--- /dev/null
+++ b/0202-spice-notify-spice-server-on-vm-start-stop.patch
@@ -0,0 +1,52 @@
+From 2d2ccb50223c16fbf08140b9dd59275657de2a61 Mon Sep 17 00:00:00 2001
+From: Yonit Halperin <yhalperi@redhat.com>
+Date: Tue, 21 Aug 2012 11:51:55 +0300
+Subject: [PATCH 202/215] spice: notify spice server on vm start/stop
+
+Spice server needs to know about the vm state in order to prevent
+attempts to write to devices when they are stopped, mainly during
+the non-live stage of migration.
+Instead, spice will take care of restoring this writes, on the migration
+target side, after migration completes.
+
+Signed-off-by: Yonit Halperin <yhalperi@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ ui/spice-core.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/ui/spice-core.c b/ui/spice-core.c
+index bb4f585..a515c94 100644
+--- a/ui/spice-core.c
++++ b/ui/spice-core.c
+@@ -546,6 +546,18 @@ static int add_channel(const char *name, const char *value, void *opaque)
+     return 0;
+ }
+ 
++static void vm_change_state_handler(void *opaque, int running,
++                                    RunState state)
++{
++#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */
++    if (running) {
++        spice_server_vm_start(spice_server);
++    } else {
++        spice_server_vm_stop(spice_server);
++    }
++#endif
++}
++
+ void qemu_spice_init(void)
+ {
+     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
+@@ -719,6 +731,8 @@ void qemu_spice_init(void)
+     qemu_spice_input_init();
+     qemu_spice_audio_init();
+ 
++    qemu_add_vm_change_state_handler(vm_change_state_handler, &spice_server);
++
+     g_free(x509_key_file);
+     g_free(x509_cert_file);
+     g_free(x509_cacert_file);
+-- 
+1.7.12
+
diff --git a/0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch b/0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch
new file mode 100644
index 0000000..e0b42d0
--- /dev/null
+++ b/0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch
@@ -0,0 +1,170 @@
+From f7d481b35b63f2df58aaef5afcac5ca1e67ce233 Mon Sep 17 00:00:00 2001
+From: Yonit Halperin <yhalperi@redhat.com>
+Date: Tue, 21 Aug 2012 11:51:56 +0300
+Subject: [PATCH 203/215] spice: notify on vm state change only via
+ spice_server_vm_start/stop
+
+QXLWorker->start/stop are deprecated since spice-server 0.11.2
+
+Signed-off-by: Yonit Halperin <yhalperi@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/qxl.c           |  7 ++++---
+ ui/spice-core.c    |  4 ++++
+ ui/spice-display.c | 32 ++++++++++++++++++++++++++++++--
+ ui/spice-display.h |  9 +++++++--
+ 4 files changed, 45 insertions(+), 7 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index c2dd3b4..95bbc03 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -958,9 +958,10 @@ static void qxl_update_irq(PCIQXLDevice *d)
+ static void qxl_check_state(PCIQXLDevice *d)
+ {
+     QXLRam *ram = d->ram;
++    int spice_display_running = qemu_spice_display_is_running(&d->ssd);
+ 
+-    assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cmd_ring));
+-    assert(!d->ssd.running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
++    assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cmd_ring));
++    assert(!spice_display_running || SPICE_RING_IS_EMPTY(&ram->cursor_ring));
+ }
+ 
+ static void qxl_reset_state(PCIQXLDevice *d)
+@@ -1538,7 +1539,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
+     uint32_t old_pending;
+     uint32_t le_events = cpu_to_le32(events);
+ 
+-    assert(d->ssd.running);
++    assert(qemu_spice_display_is_running(&d->ssd));
+     old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
+     if ((old_pending & le_events) == le_events) {
+         return;
+diff --git a/ui/spice-core.c b/ui/spice-core.c
+index a515c94..1a7a773 100644
+--- a/ui/spice-core.c
++++ b/ui/spice-core.c
+@@ -37,6 +37,7 @@
+ #include "migration.h"
+ #include "monitor.h"
+ #include "hw/hw.h"
++#include "spice-display.h"
+ 
+ /* core bits */
+ 
+@@ -551,9 +552,11 @@ static void vm_change_state_handler(void *opaque, int running,
+ {
+ #if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */
+     if (running) {
++        qemu_spice_display_start();
+         spice_server_vm_start(spice_server);
+     } else {
+         spice_server_vm_stop(spice_server);
++        qemu_spice_display_stop();
+     }
+ #endif
+ }
+@@ -755,6 +758,7 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin)
+         spice_server = spice_server_new();
+         spice_server_init(spice_server, &core_interface);
+     }
++
+     return spice_server_add_interface(spice_server, sin);
+ }
+ 
+diff --git a/ui/spice-display.c b/ui/spice-display.c
+index 3e8f0b3..1c31418 100644
+--- a/ui/spice-display.c
++++ b/ui/spice-display.c
+@@ -126,18 +126,44 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd)
+     ssd->worker->wakeup(ssd->worker);
+ }
+ 
+-void qemu_spice_start(SimpleSpiceDisplay *ssd)
++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
++static void qemu_spice_start(SimpleSpiceDisplay *ssd)
+ {
+     trace_qemu_spice_start(ssd->qxl.id);
+     ssd->worker->start(ssd->worker);
+ }
+ 
+-void qemu_spice_stop(SimpleSpiceDisplay *ssd)
++static void qemu_spice_stop(SimpleSpiceDisplay *ssd)
+ {
+     trace_qemu_spice_stop(ssd->qxl.id);
+     ssd->worker->stop(ssd->worker);
+ }
+ 
++#else
++
++static int spice_display_is_running;
++
++void qemu_spice_display_start(void)
++{
++    spice_display_is_running = true;
++}
++
++void qemu_spice_display_stop(void)
++{
++    spice_display_is_running = false;
++}
++
++#endif
++
++int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
++{
++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
++    return ssd->running;
++#else
++    return spice_display_is_running;
++#endif
++}
++
+ static SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd)
+ {
+     SimpleSpiceUpdate *update;
+@@ -272,6 +298,7 @@ void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd)
+ void qemu_spice_vm_change_state_handler(void *opaque, int running,
+                                         RunState state)
+ {
++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
+     SimpleSpiceDisplay *ssd = opaque;
+ 
+     if (running) {
+@@ -281,6 +308,7 @@ void qemu_spice_vm_change_state_handler(void *opaque, int running,
+         qemu_spice_stop(ssd);
+         ssd->running = false;
+     }
++#endif
+ }
+ 
+ void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds)
+diff --git a/ui/spice-display.h b/ui/spice-display.h
+index 12e50b6..672d65e 100644
+--- a/ui/spice-display.h
++++ b/ui/spice-display.h
+@@ -82,7 +82,9 @@ struct SimpleSpiceDisplay {
+ 
+     QXLRect dirty;
+     int notify;
++#if SPICE_SERVER_VERSION < 0x000b02 /* before 0.11.2 */
+     int running;
++#endif
+ 
+     /*
+      * All struct members below this comment can be accessed from
+@@ -129,5 +131,8 @@ void qemu_spice_create_primary_surface(SimpleSpiceDisplay *ssd, uint32_t id,
+ void qemu_spice_destroy_primary_surface(SimpleSpiceDisplay *ssd,
+                                         uint32_t id, qxl_async_io async);
+ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd);
+-void qemu_spice_start(SimpleSpiceDisplay *ssd);
+-void qemu_spice_stop(SimpleSpiceDisplay *ssd);
++#if SPICE_SERVER_VERSION >= 0x000b02 /* before 0.11.2 */
++void qemu_spice_display_start(void);
++void qemu_spice_display_stop(void);
++#endif
++int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd);
+-- 
+1.7.12
+
diff --git a/0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch b/0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch
new file mode 100644
index 0000000..631271a
--- /dev/null
+++ b/0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch
@@ -0,0 +1,90 @@
+From d8543fcc36b38c76a638d15ed95d8b5acf27d93a Mon Sep 17 00:00:00 2001
+From: Yonit Halperin <yhalperi@redhat.com>
+Date: Tue, 21 Aug 2012 11:51:57 +0300
+Subject: [PATCH 204/215] spice migration: add QEVENT_SPICE_MIGRATE_COMPLETED
+
+When migrating, libvirt queries the migration status, and upon migration
+completions, it closes the migration src. On the other hand, when
+migration is completed, spice transfers data from the src to destination
+via the client. This data is required for keeping the spice session
+after migration, without suffering from data loss and inconsistencies.
+In order to allow this data transfer, we add QEVENT for signaling
+libvirt that spice migration has completed, and libvirt needs to wait
+for this event before quitting the src process.
+
+Signed-off-by: Yonit Halperin <yhalperi@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ monitor.c       | 1 +
+ monitor.h       | 1 +
+ ui/spice-core.c | 9 ++++++++-
+ 3 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/monitor.c b/monitor.c
+index c14698d..99eee98 100644
+--- a/monitor.c
++++ b/monitor.c
+@@ -455,6 +455,7 @@ static const char *monitor_event_names[] = {
+     [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK",
+     [QEVENT_WAKEUP] = "WAKEUP",
+     [QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE",
++    [QEVENT_SPICE_MIGRATE_COMPLETED] = "SPICE_MIGRATE_COMPLETED",
+ };
+ QEMU_BUILD_BUG_ON(ARRAY_SIZE(monitor_event_names) != QEVENT_MAX)
+ 
+diff --git a/monitor.h b/monitor.h
+index 47d556b..5fc2983 100644
+--- a/monitor.h
++++ b/monitor.h
+@@ -43,6 +43,7 @@ typedef enum MonitorEvent {
+     QEVENT_SUSPEND_DISK,
+     QEVENT_WAKEUP,
+     QEVENT_BALLOON_CHANGE,
++    QEVENT_SPICE_MIGRATE_COMPLETED,
+ 
+     /* Add to 'monitor_event_names' array in monitor.c when
+      * defining new events here */
+diff --git a/ui/spice-core.c b/ui/spice-core.c
+index 1a7a773..851e869 100644
+--- a/ui/spice-core.c
++++ b/ui/spice-core.c
+@@ -285,6 +285,7 @@ typedef struct SpiceMigration {
+ } SpiceMigration;
+ 
+ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin);
++static void migrate_end_complete_cb(SpiceMigrateInstance *sin);
+ 
+ static const SpiceMigrateInterface migrate_interface = {
+     .base.type = SPICE_INTERFACE_MIGRATION,
+@@ -292,7 +293,7 @@ static const SpiceMigrateInterface migrate_interface = {
+     .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
+     .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
+     .migrate_connect_complete = migrate_connect_complete_cb,
+-    .migrate_end_complete = NULL,
++    .migrate_end_complete = migrate_end_complete_cb,
+ };
+ 
+ static SpiceMigration spice_migrate;
+@@ -305,6 +306,11 @@ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
+     }
+     sm->connect_complete.cb = NULL;
+ }
++
++static void migrate_end_complete_cb(SpiceMigrateInstance *sin)
++{
++    monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL);
++}
+ #endif
+ 
+ /* config string parsing */
+@@ -489,6 +495,7 @@ static void migration_state_notifier(Notifier *notifier, void *data)
+     } else if (migration_has_finished(s)) {
+ #ifndef SPICE_INTERFACE_MIGRATION
+         spice_server_migrate_switch(spice_server);
++        monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL);
+ #else
+         spice_server_migrate_end(spice_server, true);
+     } else if (migration_has_failed(s)) {
+-- 
+1.7.12
+
diff --git a/0205-spice-add-migrated-flag-to-spice-info.patch b/0205-spice-add-migrated-flag-to-spice-info.patch
new file mode 100644
index 0000000..0e63c64
--- /dev/null
+++ b/0205-spice-add-migrated-flag-to-spice-info.patch
@@ -0,0 +1,94 @@
+From ad7734d7a3cb4560dcc0bef2794adeddc793af75 Mon Sep 17 00:00:00 2001
+From: Yonit Halperin <yhalperi@redhat.com>
+Date: Tue, 21 Aug 2012 11:51:58 +0300
+Subject: [PATCH 205/215] spice: add 'migrated' flag to spice info
+
+The flag is 'true' when spice migration has completed on the src side.
+It is needed for a case where libvirt dies before migration completes
+and it misses the event QEVENT_SPICE_MIGRATE_COMPLETED.
+When libvirt is restored and queries the migration status, it also needs
+to query spice and check if its migration has completed.
+
+Signed-off-by: Yonit Halperin <yhalperi@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hmp.c            | 2 ++
+ qapi-schema.json | 5 ++++-
+ ui/spice-core.c  | 4 ++++
+ 3 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/hmp.c b/hmp.c
+index 81c8acb..ec4274b 100644
+--- a/hmp.c
++++ b/hmp.c
+@@ -413,6 +413,8 @@ void hmp_info_spice(Monitor *mon)
+         monitor_printf(mon, "     address: %s:%" PRId64 " [tls]\n",
+                        info->host, info->tls_port);
+     }
++    monitor_printf(mon, "    migrated: %s\n",
++                   info->migrated ? "true" : "false");
+     monitor_printf(mon, "        auth: %s\n", info->auth);
+     monitor_printf(mon, "    compiled: %s\n", info->compiled_version);
+     monitor_printf(mon, "  mouse-mode: %s\n",
+diff --git a/qapi-schema.json b/qapi-schema.json
+index bd8ad74..8ddde12 100644
+--- a/qapi-schema.json
++++ b/qapi-schema.json
+@@ -808,6 +808,9 @@
+ #
+ # @enabled: true if the SPICE server is enabled, false otherwise
+ #
++# @migrated: true if the last guest migration completed and spice
++#            migration had completed as well. false otherwise.
++#
+ # @host: #optional The hostname the SPICE server is bound to.  This depends on
+ #        the name resolution on the host and may be an IP address.
+ #
+@@ -833,7 +836,7 @@
+ # Since: 0.14.0
+ ##
+ { 'type': 'SpiceInfo',
+-  'data': {'enabled': 'bool', '*host': 'str', '*port': 'int',
++  'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
+            '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
+            'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
+ 
+diff --git a/ui/spice-core.c b/ui/spice-core.c
+index 851e869..ab069c5 100644
+--- a/ui/spice-core.c
++++ b/ui/spice-core.c
+@@ -46,6 +46,7 @@ static Notifier migration_state;
+ static const char *auth = "spice";
+ static char *auth_passwd;
+ static time_t auth_expires = TIME_MAX;
++static int spice_migration_completed;
+ int using_spice = 0;
+ 
+ static QemuThread me;
+@@ -310,6 +311,7 @@ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
+ static void migrate_end_complete_cb(SpiceMigrateInstance *sin)
+ {
+     monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL);
++    spice_migration_completed = true;
+ }
+ #endif
+ 
+@@ -443,6 +445,7 @@ SpiceInfo *qmp_query_spice(Error **errp)
+     }
+ 
+     info->enabled = true;
++    info->migrated = spice_migration_completed;
+ 
+     addr = qemu_opt_get(opts, "addr");
+     port = qemu_opt_get_number(opts, "port", 0);
+@@ -496,6 +499,7 @@ static void migration_state_notifier(Notifier *notifier, void *data)
+ #ifndef SPICE_INTERFACE_MIGRATION
+         spice_server_migrate_switch(spice_server);
+         monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL);
++        spice_migration_completed = true;
+ #else
+         spice_server_migrate_end(spice_server, true);
+     } else if (migration_has_failed(s)) {
+-- 
+1.7.12
+
diff --git a/0206-spice-adding-seamless-migration-option-to-the-comman.patch b/0206-spice-adding-seamless-migration-option-to-the-comman.patch
new file mode 100644
index 0000000..ac0e9a7
--- /dev/null
+++ b/0206-spice-adding-seamless-migration-option-to-the-comman.patch
@@ -0,0 +1,77 @@
+From 978a72a62c36e932a4b7ae18a5cf23d2d30e9755 Mon Sep 17 00:00:00 2001
+From: Yonit Halperin <yhalperi@redhat.com>
+Date: Tue, 21 Aug 2012 11:51:59 +0300
+Subject: [PATCH 206/215] spice: adding seamless-migration option to the
+ command line
+
+The seamless-migration flag is required in order to identify
+whether libvirt supports the new QEVENT_SPICE_MIGRATE_COMPLETED or not
+(by default the flag is off).
+New libvirt versions that wait for QEVENT_SPICE_MIGRATE_COMPLETED should turn on this flag.
+When this flag is off, spice fallbacks to its old migration method, which
+can result in data loss.
+
+Signed-off-by: Yonit Halperin <yhalperi@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ qemu-config.c   | 3 +++
+ qemu-options.hx | 3 +++
+ ui/spice-core.c | 7 +++++++
+ 3 files changed, 13 insertions(+)
+
+diff --git a/qemu-config.c b/qemu-config.c
+index 238390e..3eaee48 100644
+--- a/qemu-config.c
++++ b/qemu-config.c
+@@ -541,6 +541,9 @@ QemuOptsList qemu_spice_opts = {
+         },{
+             .name = "playback-compression",
+             .type = QEMU_OPT_BOOL,
++        }, {
++            .name = "seamless-migration",
++            .type = QEMU_OPT_BOOL,
+         },
+         { /* end of list */ }
+     },
+diff --git a/qemu-options.hx b/qemu-options.hx
+index ea06324..dd7aa63 100644
+--- a/qemu-options.hx
++++ b/qemu-options.hx
+@@ -920,6 +920,9 @@ Enable/disable passing mouse events via vdagent.  Default is on.
+ @item playback-compression=[on|off]
+ Enable/disable audio stream compression (using celt 0.5.1).  Default is on.
+ 
++@item seamless-migration=[on|off]
++Enable/disable spice seamless migration. Default is off.
++
+ @end table
+ ETEXI
+ 
+diff --git a/ui/spice-core.c b/ui/spice-core.c
+index ab069c5..ba0d0bd 100644
+--- a/ui/spice-core.c
++++ b/ui/spice-core.c
+@@ -585,6 +585,9 @@ void qemu_spice_init(void)
+     int port, tls_port, len, addr_flags;
+     spice_image_compression_t compression;
+     spice_wan_compression_t wan_compr;
++#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */
++    bool seamless_migration;
++#endif
+ 
+     qemu_thread_get_self(&me);
+ 
+@@ -728,6 +731,10 @@ void qemu_spice_init(void)
+     spice_server_set_uuid(spice_server, qemu_uuid);
+ #endif
+ 
++#if SPICE_SERVER_VERSION >= 0x000b02 /* 0.11.2 */
++    seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0);
++    spice_server_set_seamless_migration(spice_server, seamless_migration);
++#endif
+     if (0 != spice_server_init(spice_server, &core_interface)) {
+         error_report("failed to initialize spice server");
+         exit(1);
+-- 
+1.7.12
+
diff --git a/0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch b/0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch
new file mode 100644
index 0000000..6440642
--- /dev/null
+++ b/0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch
@@ -0,0 +1,47 @@
+From 54704702dbbb1d55f2aaecf5c837a583d9e209a5 Mon Sep 17 00:00:00 2001
+From: Yonit Halperin <yhalperi@redhat.com>
+Date: Tue, 21 Aug 2012 13:54:20 +0300
+Subject: [PATCH 207/215] spice: increase the verbosity of spice section in
+ "qemu --help"
+
+Added all spice options to the help string. This can be used by libvirt
+to determine which spice related features are supported by qemu.
+
+Signed-off-by: Yonit Halperin <yhalperi@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ qemu-options.hx | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/qemu-options.hx b/qemu-options.hx
+index dd7aa63..1af4fec 100644
+--- a/qemu-options.hx
++++ b/qemu-options.hx
+@@ -838,7 +838,23 @@ Enable SDL.
+ ETEXI
+ 
+ DEF("spice", HAS_ARG, QEMU_OPTION_spice,
+-    "-spice <args>   enable spice\n", QEMU_ARCH_ALL)
++    "-spice [port=port][,tls-port=secured-port][,x509-dir=<dir>]\n"
++    "       [,x509-key-file=<file>][,x509-key-password=<file>]\n"
++    "       [,x509-cert-file=<file>][,x509-cacert-file=<file>]\n"
++    "       [,x509-dh-key-file=<file>][,addr=addr][,ipv4|ipv6]\n"
++    "       [,tls-ciphers=<list>]\n"
++    "       [,tls-channel=[main|display|cursor|inputs|record|playback]]\n"
++    "       [,plaintext-channel=[main|display|cursor|inputs|record|playback]]\n"
++    "       [,sasl][,password=<secret>][,disable-ticketing]\n"
++    "       [,image-compression=[auto_glz|auto_lz|quic|glz|lz|off]]\n"
++    "       [,jpeg-wan-compression=[auto|never|always]]\n"
++    "       [,zlib-glz-wan-compression=[auto|never|always]]\n"
++    "       [,streaming-video=[off|all|filter]][,disable-copy-paste]\n"
++    "       [,agent-mouse=[on|off]][,playback-compression=[on|off]]\n"
++    "       [,seamless-migration=[on|off]]\n"
++    "   enable spice\n"
++    "   at least one of {port, tls-port} is mandatory\n",
++    QEMU_ARCH_ALL)
+ STEXI
+ @item -spice @var{option}[,@var{option}[,...]]
+ @findex -spice
+-- 
+1.7.12
+
diff --git a/0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch b/0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch
new file mode 100644
index 0000000..beefe40
--- /dev/null
+++ b/0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch
@@ -0,0 +1,37 @@
+From c3236169b390617d13ede20ff0aed5d0bc1aba66 Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy@redhat.com>
+Date: Tue, 21 Aug 2012 13:51:31 +0300
+Subject: [PATCH 208/215] qxl/update_area_io: guest_bug on invalid parameters
+
+Signed-off-by: Alon Levy <alevy@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/qxl.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 95bbc03..baf9bb4 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -1386,6 +1386,18 @@ async_common:
+         QXLCookie *cookie = NULL;
+         QXLRect update = d->ram->update_area;
+ 
++        if (d->ram->update_surface > NUM_SURFACES) {
++            qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n",
++                              d->ram->update_surface);
++            return;
++        }
++        if (update.left >= update.right || update.top >= update.bottom) {
++            qxl_set_guest_bug(d,
++                    "QXL_IO_UPDATE_AREA: invalid area (%ux%u)x(%ux%u)\n",
++                    update.left, update.top, update.right, update.bottom);
++            return;
++        }
++
+         if (async == QXL_ASYNC) {
+             cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                     QXL_IO_UPDATE_AREA_ASYNC);
+-- 
+1.7.12
+
diff --git a/0209-qxl-disallow-unknown-revisions.patch b/0209-qxl-disallow-unknown-revisions.patch
new file mode 100644
index 0000000..630be61
--- /dev/null
+++ b/0209-qxl-disallow-unknown-revisions.patch
@@ -0,0 +1,33 @@
+From 31052a357ad124c9ed17fbc39a0db5c8d6d0d9c5 Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy@redhat.com>
+Date: Tue, 21 Aug 2012 13:51:32 +0300
+Subject: [PATCH 209/215] qxl: disallow unknown revisions
+
+Signed-off-by: Alon Levy <alevy@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/qxl.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index baf9bb4..d134a70 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -1798,10 +1798,13 @@ static int qxl_init_common(PCIQXLDevice *qxl)
+         io_size = 16;
+         break;
+     case 3: /* qxl-3 */
+-    default:
+         pci_device_rev = QXL_DEFAULT_REVISION;
+         io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
+         break;
++    default:
++        error_report("Invalid revision %d for qxl device (max %d)",
++                     qxl->revision, QXL_DEFAULT_REVISION);
++        return -1;
+     }
+ 
+     pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
+-- 
+1.7.12
+
diff --git a/0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch b/0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch
new file mode 100644
index 0000000..a628f52
--- /dev/null
+++ b/0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch
@@ -0,0 +1,321 @@
+From cc8b6a27e848cb88a4df98d48517c5b658d70efc Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy@redhat.com>
+Date: Wed, 22 Aug 2012 11:16:25 +0300
+Subject: [PATCH 210/215] qxl: add QXL_IO_MONITORS_CONFIG_ASYNC
+
+Revision bumped to 4 for new IO support, enabled for spice-server >=
+0.11.1. New io enabled if revision is 4. Revision can be set to 4.
+
+[ kraxel: 3 continues to be the default revision.  Once we have a new
+          stable spice-server release and the qemu patches to enable
+          the new bits merged we'll go flip the switch and make rev4
+          the default ]
+
+This io calls the corresponding new spice api
+spice_qxl_monitors_config_async to let spice-server read a new guest set
+monitors config and notify the client.
+
+On migration reissue spice_qxl_monitors_config_async.
+
+RHBZ: 770842
+
+Signed-off-by: Alon Levy <alevy@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+
+fixup
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ configure          |  7 ++++
+ hw/qxl.c           | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++---
+ hw/qxl.h           |  7 ++++
+ trace-events       |  1 +
+ ui/spice-display.h |  1 +
+ 5 files changed, 109 insertions(+), 4 deletions(-)
+
+diff --git a/configure b/configure
+index 9fc4fb5..c57d1c1 100755
+--- a/configure
++++ b/configure
+@@ -2711,6 +2711,9 @@ EOF
+     spice="yes"
+     libs_softmmu="$libs_softmmu $spice_libs"
+     QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags"
++    if $pkg_config --atleast-version=0.12.0 spice-protocol >/dev/null 2>&1; then
++        spice_qxl_io_monitors_config_async="yes"
++    fi
+   else
+     if test "$spice" = "yes" ; then
+       feature_not_found "spice"
+@@ -3448,6 +3451,10 @@ if test "$spice" = "yes" ; then
+   echo "CONFIG_SPICE=y" >> $config_host_mak
+ fi
+ 
++if test "$spice_qxl_io_monitors_config_async" = "yes" ; then
++  echo "CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC=y" >> $config_host_mak
++fi
++
+ if test "$smartcard" = "yes" ; then
+   echo "CONFIG_SMARTCARD=y" >> $config_host_mak
+ fi
+diff --git a/hw/qxl.c b/hw/qxl.c
+index d134a70..adf17fd 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -27,6 +27,11 @@
+ 
+ #include "qxl.h"
+ 
++#ifndef CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC
++/* spice-protocol is too old, add missing definitions */
++#define QXL_IO_MONITORS_CONFIG_ASYNC (QXL_IO_FLUSH_RELEASE + 1)
++#endif
++
+ /*
+  * NOTE: SPICE_RING_PROD_ITEM accesses memory on the pci bar and as
+  * such can be changed by the guest, so to avoid a guest trigerrable
+@@ -249,6 +254,39 @@ static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async)
+     }
+ }
+ 
++static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
++{
++    trace_qxl_spice_monitors_config(qxl->id);
++/* 0x000b01 == 0.11.1 */
++#if SPICE_SERVER_VERSION >= 0x000b01 && \
++    defined(CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC)
++    if (replay) {
++        /*
++         * don't use QXL_COOKIE_TYPE_IO:
++         *  - we are not running yet (post_load), we will assert
++         *    in send_events
++         *  - this is not a guest io, but a reply, so async_io isn't set.
++         */
++        spice_qxl_monitors_config_async(&qxl->ssd.qxl,
++                qxl->guest_monitors_config,
++                MEMSLOT_GROUP_GUEST,
++                (uintptr_t)qxl_cookie_new(
++                    QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
++                    0));
++    } else {
++        qxl->guest_monitors_config = qxl->ram->monitors_config;
++        spice_qxl_monitors_config_async(&qxl->ssd.qxl,
++                qxl->ram->monitors_config,
++                MEMSLOT_GROUP_GUEST,
++                (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO,
++                                          QXL_IO_MONITORS_CONFIG_ASYNC));
++    }
++#else
++    fprintf(stderr, "qxl: too old spice-protocol/spice-server for "
++            "QXL_IO_MONITORS_CONFIG_ASYNC\n");
++#endif
++}
++
+ void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
+ {
+     trace_qxl_spice_reset_image_cache(qxl->id);
+@@ -538,6 +576,7 @@ static const char *io_port_to_string(uint32_t io_port)
+                                         = "QXL_IO_DESTROY_ALL_SURFACES_ASYNC",
+         [QXL_IO_FLUSH_SURFACES_ASYNC]   = "QXL_IO_FLUSH_SURFACES_ASYNC",
+         [QXL_IO_FLUSH_RELEASE]          = "QXL_IO_FLUSH_RELEASE",
++        [QXL_IO_MONITORS_CONFIG_ASYNC]  = "QXL_IO_MONITORS_CONFIG_ASYNC",
+     };
+     return io_port_to_string[io_port];
+ }
+@@ -819,6 +858,7 @@ static void interface_async_complete_io(PCIQXLDevice *qxl, QXLCookie *cookie)
+     case QXL_IO_DESTROY_PRIMARY_ASYNC:
+     case QXL_IO_UPDATE_AREA_ASYNC:
+     case QXL_IO_FLUSH_SURFACES_ASYNC:
++    case QXL_IO_MONITORS_CONFIG_ASYNC:
+         break;
+     case QXL_IO_CREATE_PRIMARY_ASYNC:
+         qxl_create_guest_primary_complete(qxl);
+@@ -894,6 +934,8 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
+     case QXL_COOKIE_TYPE_RENDER_UPDATE_AREA:
+         qxl_render_update_area_done(qxl, cookie);
+         break;
++    case QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG:
++        break;
+     default:
+         fprintf(stderr, "qxl: %s: unexpected cookie type %d\n",
+                 __func__, cookie->type);
+@@ -1315,6 +1357,13 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
+         return;
+     }
+ 
++    if (d->revision <= QXL_REVISION_STABLE_V10 &&
++        io_port >= QXL_IO_FLUSH_SURFACES_ASYNC) {
++        qxl_set_guest_bug(d, "unsupported io %d for revision %d\n",
++            io_port, d->revision);
++        return;
++    }
++
+     switch (io_port) {
+     case QXL_IO_RESET:
+     case QXL_IO_SET_MODE:
+@@ -1334,7 +1383,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
+             io_port, io_port_to_string(io_port));
+         /* be nice to buggy guest drivers */
+         if (io_port >= QXL_IO_UPDATE_AREA_ASYNC &&
+-            io_port <= QXL_IO_DESTROY_ALL_SURFACES_ASYNC) {
++            io_port < QXL_IO_RANGE_SIZE) {
+             qxl_send_events(d, QXL_INTERRUPT_IO_CMD);
+         }
+         return;
+@@ -1362,6 +1411,7 @@ static void ioport_write(void *opaque, target_phys_addr_t addr,
+         io_port = QXL_IO_DESTROY_ALL_SURFACES;
+         goto async_common;
+     case QXL_IO_FLUSH_SURFACES_ASYNC:
++    case QXL_IO_MONITORS_CONFIG_ASYNC:
+ async_common:
+         async = QXL_ASYNC;
+         qemu_mutex_lock(&d->async_lock);
+@@ -1503,6 +1553,9 @@ async_common:
+         d->mode = QXL_MODE_UNDEFINED;
+         qxl_spice_destroy_surfaces(d, async);
+         break;
++    case QXL_IO_MONITORS_CONFIG_ASYNC:
++        qxl_spice_monitors_config_async(d, 0);
++        break;
+     default:
+         qxl_set_guest_bug(d, "%s: unexpected ioport=0x%x\n", __func__, io_port);
+     }
+@@ -1798,9 +1851,17 @@ static int qxl_init_common(PCIQXLDevice *qxl)
+         io_size = 16;
+         break;
+     case 3: /* qxl-3 */
+-        pci_device_rev = QXL_DEFAULT_REVISION;
++        pci_device_rev = QXL_REVISION_STABLE_V10;
++        io_size = 32; /* PCI region size must be pow2 */
++        break;
++/* 0x000b01 == 0.11.1 */
++#if SPICE_SERVER_VERSION >= 0x000b01 && \
++        defined(CONFIG_QXL_IO_MONITORS_CONFIG_ASYNC)
++    case 4: /* qxl-4 */
++        pci_device_rev = QXL_REVISION_STABLE_V12;
+         io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1);
+         break;
++#endif
+     default:
+         error_report("Invalid revision %d for qxl device (max %d)",
+                      qxl->revision, QXL_DEFAULT_REVISION);
+@@ -1999,7 +2060,9 @@ static int qxl_post_load(void *opaque, int version)
+         }
+         qxl_spice_loadvm_commands(d, cmds, out);
+         g_free(cmds);
+-
++        if (d->guest_monitors_config) {
++            qxl_spice_monitors_config_async(d, 1);
++        }
+         break;
+     case QXL_MODE_COMPAT:
+         /* note: no need to call qxl_create_memslots, qxl_set_mode
+@@ -2012,6 +2075,14 @@ static int qxl_post_load(void *opaque, int version)
+ 
+ #define QXL_SAVE_VERSION 21
+ 
++static bool qxl_monitors_config_needed(void *opaque)
++{
++    PCIQXLDevice *qxl = opaque;
++
++    return qxl->guest_monitors_config != 0;
++}
++
++
+ static VMStateDescription qxl_memslot = {
+     .name               = "qxl-memslot",
+     .version_id         = QXL_SAVE_VERSION,
+@@ -2042,6 +2113,16 @@ static VMStateDescription qxl_surface = {
+     }
+ };
+ 
++static VMStateDescription qxl_vmstate_monitors_config = {
++    .name               = "qxl/monitors-config",
++    .version_id         = 1,
++    .minimum_version_id = 1,
++    .fields = (VMStateField[]) {
++        VMSTATE_UINT64(guest_monitors_config, PCIQXLDevice),
++        VMSTATE_END_OF_LIST()
++    },
++};
++
+ static VMStateDescription qxl_vmstate = {
+     .name               = "qxl",
+     .version_id         = QXL_SAVE_VERSION,
+@@ -2049,7 +2130,7 @@ static VMStateDescription qxl_vmstate = {
+     .pre_save           = qxl_pre_save,
+     .pre_load           = qxl_pre_load,
+     .post_load          = qxl_post_load,
+-    .fields = (VMStateField []) {
++    .fields = (VMStateField[]) {
+         VMSTATE_PCI_DEVICE(pci, PCIQXLDevice),
+         VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState),
+         VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice),
+@@ -2068,6 +2149,14 @@ static VMStateDescription qxl_vmstate = {
+         VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
+         VMSTATE_END_OF_LIST()
+     },
++    .subsections = (VMStateSubsection[]) {
++        {
++            .vmsd = &qxl_vmstate_monitors_config,
++            .needed = qxl_monitors_config_needed,
++        }, {
++            /* empty */
++        }
++    }
+ };
+ 
+ static Property qxl_properties[] = {
+diff --git a/hw/qxl.h b/hw/qxl.h
+index 172baf6..9cfedb7 100644
+--- a/hw/qxl.h
++++ b/hw/qxl.h
+@@ -71,6 +71,8 @@ typedef struct PCIQXLDevice {
+     } guest_surfaces;
+     QXLPHYSICAL        guest_cursor;
+ 
++    QXLPHYSICAL        guest_monitors_config;
++
+     QemuMutex          track_lock;
+ 
+     /* thread signaling */
+@@ -128,7 +130,12 @@ typedef struct PCIQXLDevice {
+         }                                                               \
+     } while (0)
+ 
++#if 0
++/* spice-server 0.12 is still in development */
++#define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V12
++#else
+ #define QXL_DEFAULT_REVISION QXL_REVISION_STABLE_V10
++#endif
+ 
+ /* qxl.c */
+ void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
+diff --git a/trace-events b/trace-events
+index 0de70d9..a58b0b7 100644
+--- a/trace-events
++++ b/trace-events
+@@ -967,6 +967,7 @@ qxl_spice_destroy_surfaces(int qid, int async) "%d async=%d"
+ qxl_spice_destroy_surface_wait_complete(int qid, uint32_t id) "%d sid=%d"
+ qxl_spice_destroy_surface_wait(int qid, uint32_t id, int async) "%d sid=%d async=%d"
+ qxl_spice_flush_surfaces_async(int qid, uint32_t surface_count, uint32_t num_free_res) "%d s#=%d, res#=%d"
++qxl_spice_monitors_config(int id) "%d"
+ qxl_spice_loadvm_commands(int qid, void *ext, uint32_t count) "%d ext=%p count=%d"
+ qxl_spice_oom(int qid) "%d"
+ qxl_spice_reset_cursor(int qid) "%d"
+diff --git a/ui/spice-display.h b/ui/spice-display.h
+index 672d65e..bcff114 100644
+--- a/ui/spice-display.h
++++ b/ui/spice-display.h
+@@ -51,6 +51,7 @@ typedef enum qxl_async_io {
+ enum {
+     QXL_COOKIE_TYPE_IO,
+     QXL_COOKIE_TYPE_RENDER_UPDATE_AREA,
++    QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
+ };
+ 
+ typedef struct QXLCookie {
+-- 
+1.7.12
+
diff --git a/0211-configure-print-spice-protocol-and-spice-server-vers.patch b/0211-configure-print-spice-protocol-and-spice-server-vers.patch
new file mode 100644
index 0000000..711256c
--- /dev/null
+++ b/0211-configure-print-spice-protocol-and-spice-server-vers.patch
@@ -0,0 +1,37 @@
+From 1d2790682dfac06c2358ac99f5e0bad6a065702d Mon Sep 17 00:00:00 2001
+From: Alon Levy <alevy@redhat.com>
+Date: Wed, 22 Aug 2012 11:16:26 +0300
+Subject: [PATCH 211/215] configure: print spice-protocol and spice-server
+ versions
+
+Signed-off-by: Alon Levy <alevy@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ configure | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/configure b/configure
+index c57d1c1..25c406f 100755
+--- a/configure
++++ b/configure
+@@ -2711,6 +2711,8 @@ EOF
+     spice="yes"
+     libs_softmmu="$libs_softmmu $spice_libs"
+     QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags"
++    spice_protocol_version=$($pkg_config --modversion spice-protocol)
++    spice_server_version=$($pkg_config --modversion spice-server)
+     if $pkg_config --atleast-version=0.12.0 spice-protocol >/dev/null 2>&1; then
+         spice_qxl_io_monitors_config_async="yes"
+     fi
+@@ -3169,7 +3171,7 @@ echo "libcap-ng support $cap_ng"
+ echo "vhost-net support $vhost_net"
+ echo "Trace backend     $trace_backend"
+ echo "Trace output file $trace_file-<pid>"
+-echo "spice support     $spice"
++echo "spice support     $spice ($spice_protocol_version/$spice_server_version)"
+ echo "rbd support       $rbd"
+ echo "xfsctl support    $xfs"
+ echo "nss used          $smartcard_nss"
+-- 
+1.7.12
+
diff --git a/0212-spice-make-number-of-surfaces-runtime-configurable.patch b/0212-spice-make-number-of-surfaces-runtime-configurable.patch
new file mode 100644
index 0000000..0101a20
--- /dev/null
+++ b/0212-spice-make-number-of-surfaces-runtime-configurable.patch
@@ -0,0 +1,201 @@
+From 72e437d9b775cb92f93c3acd0109239f1bb3e6e2 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 4 Sep 2012 11:39:41 +0200
+Subject: [PATCH 212/215] spice: make number of surfaces runtime-configurable.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/qxl.c           | 31 +++++++++++++++++--------------
+ hw/qxl.h           |  3 +--
+ ui/spice-display.c |  5 ++++-
+ ui/spice-display.h |  3 +--
+ 4 files changed, 23 insertions(+), 19 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index adf17fd..8725f67 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -236,7 +236,8 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
+ {
+     trace_qxl_spice_destroy_surfaces_complete(qxl->id);
+     qemu_mutex_lock(&qxl->track_lock);
+-    memset(&qxl->guest_surfaces.cmds, 0, sizeof(qxl->guest_surfaces.cmds));
++    memset(qxl->guest_surfaces.cmds, 0,
++           sizeof(qxl->guest_surfaces.cmds) * qxl->ssd.num_surfaces);
+     qxl->guest_surfaces.count = 0;
+     qemu_mutex_unlock(&qxl->track_lock);
+ }
+@@ -345,7 +346,7 @@ static void init_qxl_rom(PCIQXLDevice *d)
+     rom->slot_id_bits  = MEMSLOT_SLOT_BITS;
+     rom->slots_start   = 1;
+     rom->slots_end     = NUM_MEMSLOTS - 1;
+-    rom->n_surfaces    = cpu_to_le32(NUM_SURFACES);
++    rom->n_surfaces    = cpu_to_le32(d->ssd.num_surfaces);
+ 
+     for (i = 0, n = 0; i < ARRAY_SIZE(qxl_modes); i++) {
+         fb = qxl_modes[i].y_res * qxl_modes[i].stride;
+@@ -449,9 +450,9 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
+         }
+         uint32_t id = le32_to_cpu(cmd->surface_id);
+ 
+-        if (id >= NUM_SURFACES) {
++        if (id >= qxl->ssd.num_surfaces) {
+             qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE id %d >= %d", id,
+-                              NUM_SURFACES);
++                              qxl->ssd.num_surfaces);
+             return 1;
+         }
+         qemu_mutex_lock(&qxl->track_lock);
+@@ -527,7 +528,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+     info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+     info->internal_groupslot_id = 0;
+     info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS;
+-    info->n_surfaces = NUM_SURFACES;
++    info->n_surfaces = qxl->ssd.num_surfaces;
+ }
+ 
+ static const char *qxl_mode_to_string(int mode)
+@@ -1436,7 +1437,7 @@ async_common:
+         QXLCookie *cookie = NULL;
+         QXLRect update = d->ram->update_area;
+ 
+-        if (d->ram->update_surface > NUM_SURFACES) {
++        if (d->ram->update_surface > d->ssd.num_surfaces) {
+             qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: invalid surface id %d\n",
+                               d->ram->update_surface);
+             return;
+@@ -1529,7 +1530,7 @@ async_common:
+         }
+         break;
+     case QXL_IO_DESTROY_SURFACE_WAIT:
+-        if (val >= NUM_SURFACES) {
++        if (val >= d->ssd.num_surfaces) {
+             qxl_set_guest_bug(d, "QXL_IO_DESTROY_SURFACE (async=%d):"
+                              "%" PRIu64 " >= NUM_SURFACES", async, val);
+             goto cancel_async;
+@@ -1707,7 +1708,7 @@ static void qxl_dirty_surfaces(PCIQXLDevice *qxl)
+     vram_start =  (intptr_t)memory_region_get_ram_ptr(&qxl->vram_bar);
+ 
+     /* dirty the off-screen surfaces */
+-    for (i = 0; i < NUM_SURFACES; i++) {
++    for (i = 0; i < qxl->ssd.num_surfaces; i++) {
+         QXLSurfaceCmd *cmd;
+         intptr_t surface_offset;
+         int surface_size;
+@@ -1835,7 +1836,6 @@ static int qxl_init_common(PCIQXLDevice *qxl)
+     qxl->mode = QXL_MODE_UNDEFINED;
+     qxl->generation = 1;
+     qxl->num_memslots = NUM_MEMSLOTS;
+-    qxl->num_surfaces = NUM_SURFACES;
+     qemu_mutex_init(&qxl->track_lock);
+     qemu_mutex_init(&qxl->async_lock);
+     qxl->current_async = QXL_UNDEFINED_IO;
+@@ -1877,6 +1877,7 @@ static int qxl_init_common(PCIQXLDevice *qxl)
+     init_qxl_rom(qxl);
+     init_qxl_ram(qxl);
+ 
++    qxl->guest_surfaces.cmds = g_new0(QXLPHYSICAL, qxl->ssd.num_surfaces);
+     memory_region_init_ram(&qxl->vram_bar, "qxl.vram", qxl->vram_size);
+     vmstate_register_ram(&qxl->vram_bar, &qxl->pci.qdev);
+     memory_region_init_alias(&qxl->vram32_bar, "qxl.vram32", &qxl->vram_bar,
+@@ -2042,8 +2043,8 @@ static int qxl_post_load(void *opaque, int version)
+         qxl_create_guest_primary(d, 1, QXL_SYNC);
+ 
+         /* replay surface-create and cursor-set commands */
+-        cmds = g_malloc0(sizeof(QXLCommandExt) * (NUM_SURFACES + 1));
+-        for (in = 0, out = 0; in < NUM_SURFACES; in++) {
++        cmds = g_malloc0(sizeof(QXLCommandExt) * (d->ssd.num_surfaces + 1));
++        for (in = 0, out = 0; in < d->ssd.num_surfaces; in++) {
+             if (d->guest_surfaces.cmds[in] == 0) {
+                 continue;
+             }
+@@ -2143,9 +2144,10 @@ static VMStateDescription qxl_vmstate = {
+                              qxl_memslot, struct guest_slots),
+         VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0,
+                        qxl_surface, QXLSurfaceCreate),
+-        VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice),
+-        VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0,
+-                      vmstate_info_uint64, uint64_t),
++        VMSTATE_INT32_EQUAL(ssd.num_surfaces, PCIQXLDevice),
++        VMSTATE_VARRAY_INT32(guest_surfaces.cmds, PCIQXLDevice,
++                             ssd.num_surfaces, 0,
++                             vmstate_info_uint64, uint64_t),
+         VMSTATE_UINT64(guest_cursor, PCIQXLDevice),
+         VMSTATE_END_OF_LIST()
+     },
+@@ -2173,6 +2175,7 @@ static Property qxl_properties[] = {
+         DEFINE_PROP_UINT32("vram_size_mb", PCIQXLDevice, vram32_size_mb, -1),
+         DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
+         DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
++        DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024),
+         DEFINE_PROP_END_OF_LIST(),
+ };
+ 
+diff --git a/hw/qxl.h b/hw/qxl.h
+index 9cfedb7..5553824 100644
+--- a/hw/qxl.h
++++ b/hw/qxl.h
+@@ -40,7 +40,6 @@ typedef struct PCIQXLDevice {
+     uint32_t           revision;
+ 
+     int32_t            num_memslots;
+-    int32_t            num_surfaces;
+ 
+     uint32_t           current_async;
+     QemuMutex          async_lock;
+@@ -65,7 +64,7 @@ typedef struct PCIQXLDevice {
+     } guest_primary;
+ 
+     struct surfaces {
+-        QXLPHYSICAL    cmds[NUM_SURFACES];
++        QXLPHYSICAL    *cmds;
+         uint32_t       count;
+         uint32_t       max;
+     } guest_surfaces;
+diff --git a/ui/spice-display.c b/ui/spice-display.c
+index 1c31418..99bc665 100644
+--- a/ui/spice-display.c
++++ b/ui/spice-display.c
+@@ -317,6 +317,9 @@ void qemu_spice_display_init_common(SimpleSpiceDisplay *ssd, DisplayState *ds)
+     qemu_mutex_init(&ssd->lock);
+     ssd->mouse_x = -1;
+     ssd->mouse_y = -1;
++    if (ssd->num_surfaces == 0) {
++        ssd->num_surfaces = 1024;
++    }
+     ssd->bufsize = (16 * 1024 * 1024);
+     ssd->buf = g_malloc(ssd->bufsize);
+ }
+@@ -427,7 +430,7 @@ static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
+     info->num_memslots_groups = NUM_MEMSLOTS_GROUPS;
+     info->internal_groupslot_id = 0;
+     info->qxl_ram_size = ssd->bufsize;
+-    info->n_surfaces = NUM_SURFACES;
++    info->n_surfaces = ssd->num_surfaces;
+ }
+ 
+ static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext)
+diff --git a/ui/spice-display.h b/ui/spice-display.h
+index bcff114..512ab78 100644
+--- a/ui/spice-display.h
++++ b/ui/spice-display.h
+@@ -32,8 +32,6 @@
+ #define MEMSLOT_GROUP_GUEST 1
+ #define NUM_MEMSLOTS_GROUPS 2
+ 
+-#define NUM_SURFACES 1024
+-
+ /*
+  * Internal enum to differenciate between options for
+  * io calls that have a sync (old) version and an _async (new)
+@@ -80,6 +78,7 @@ struct SimpleSpiceDisplay {
+     QXLInstance qxl;
+     uint32_t unique;
+     QemuPfConv *conv;
++    int32_t num_surfaces;
+ 
+     QXLRect dirty;
+     int notify;
+-- 
+1.7.12
+
diff --git a/0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch b/0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch
new file mode 100644
index 0000000..4427f78
--- /dev/null
+++ b/0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch
@@ -0,0 +1,65 @@
+From 44eab2c48c8b090fff8b7ded39b40dae6c6ce003 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?S=C3=B8ren=20Sandmann=20Pedersen?= <ssp@redhat.com>
+Date: Tue, 4 Sep 2012 10:14:48 -0400
+Subject: [PATCH 213/215] qxl: Add set_client_capabilities() interface to
+ QXLInterface
+
+This new interface lets spice server inform the guest whether
+
+(a) a client is connected
+(b) what capabilities the client has
+
+There is a fixed number (464) of bits reserved for capabilities, and
+when the capabilities bits change, the QXL_INTERRUPT_CLIENT interrupt
+is generated.
+
+Signed-off-by: Soren Sandmann <ssp@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/qxl.c | 23 +++++++++++++++++++++++
+ 1 file changed, 23 insertions(+)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 8725f67..2aa5848 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -944,6 +944,26 @@ static void interface_async_complete(QXLInstance *sin, uint64_t cookie_token)
+     }
+ }
+ 
++#if SPICE_SERVER_VERSION >= 0x000b04
++
++/* called from spice server thread context only */
++static void interface_set_client_capabilities(QXLInstance *sin,
++                                              uint8_t client_present,
++                                              uint8_t caps[58])
++{
++    PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
++
++    qxl->shadow_rom.client_present = client_present;
++    memcpy(qxl->shadow_rom.client_capabilities, caps, sizeof(caps));
++    qxl->rom->client_present = client_present;
++    memcpy(qxl->rom->client_capabilities, caps, sizeof(caps));
++    qxl_rom_set_dirty(qxl);
++
++    qxl_send_events(qxl, QXL_INTERRUPT_CLIENT);
++}
++
++#endif
++
+ static const QXLInterface qxl_interface = {
+     .base.type               = SPICE_INTERFACE_QXL,
+     .base.description        = "qxl gpu",
+@@ -965,6 +985,9 @@ static const QXLInterface qxl_interface = {
+     .flush_resources         = interface_flush_resources,
+     .async_complete          = interface_async_complete,
+     .update_area_complete    = interface_update_area_complete,
++#if SPICE_SERVER_VERSION >= 0x000b04
++    .set_client_capabilities = interface_set_client_capabilities,
++#endif
+ };
+ 
+ static void qxl_enter_vga_mode(PCIQXLDevice *d)
+-- 
+1.7.12
+
diff --git a/0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch b/0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch
new file mode 100644
index 0000000..cfae0f3
--- /dev/null
+++ b/0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch
@@ -0,0 +1,32 @@
+From 666f88952fe1bcf84ba857f417a5d25f86bbf38b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?S=C3=B8ren=20Sandmann=20Pedersen?= <ssp@redhat.com>
+Date: Tue, 4 Sep 2012 10:14:49 -0400
+Subject: [PATCH 214/215] Remove #ifdef QXL_COMMAND_FLAG_COMPAT_16BPP
+
+We require spice >= 0.8 now, so this flag is always present.
+
+Signed-off-by: Soren Sandmann <ssp@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/qxl.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index 2aa5848..b726c19 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -1359,11 +1359,9 @@ static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm)
+ 
+     d->mode = QXL_MODE_COMPAT;
+     d->cmdflags = QXL_COMMAND_FLAG_COMPAT;
+-#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */
+     if (mode->bits == 16) {
+         d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP;
+     }
+-#endif
+     d->shadow_rom.mode = cpu_to_le32(modenr);
+     d->rom->mode = cpu_to_le32(modenr);
+     qxl_rom_set_dirty(d);
+-- 
+1.7.12
+
diff --git a/0215-qxl-dont-update-invalid-area.patch b/0215-qxl-dont-update-invalid-area.patch
new file mode 100644
index 0000000..8739272
--- /dev/null
+++ b/0215-qxl-dont-update-invalid-area.patch
@@ -0,0 +1,41 @@
+From 6f1652c4412ab60c7f456100143c519d124a895c Mon Sep 17 00:00:00 2001
+From: Dunrong Huang <riegamaths@gmail.com>
+Date: Fri, 31 Aug 2012 00:44:44 +0800
+Subject: [PATCH 215/215] qxl: dont update invalid area
+
+This patch fixes the following error:
+
+$ ~/usr/bin/qemu-system-x86_64 -enable-kvm -m 1024 -spice port=5900,disable-ticketing -vga qxl -cdrom ~/Images/linuxmint-13-mate-dvd-32bit.iso
+(/home/mathslinux/usr/bin/qemu-system-x86_64:10068): SpiceWorker-CRITICAL **: red_worker.c:4599:red_update_area: condition `area->left >= 0 && area->top >= 0 && area->left < area->right && area->top < area->bottom' failed
+Aborted
+
+spice server terminates QEMU process if we pass invalid area to it,
+so dont update those invalid areas.
+
+Signed-off-by: Dunrong Huang <riegamaths@gmail.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/qxl.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/hw/qxl.c b/hw/qxl.c
+index b726c19..045432e 100644
+--- a/hw/qxl.c
++++ b/hw/qxl.c
+@@ -1470,6 +1470,13 @@ async_common:
+             return;
+         }
+ 
++        if (update.left < 0 || update.top < 0 || update.left >= update.right ||
++            update.top >= update.bottom) {
++            qxl_set_guest_bug(d, "QXL_IO_UPDATE_AREA: "
++                              "invalid area(%d,%d,%d,%d)\n", update.left,
++                              update.right, update.top, update.bottom);
++            break;
++        }
+         if (async == QXL_ASYNC) {
+             cookie = qxl_cookie_new(QXL_COOKIE_TYPE_IO,
+                                     QXL_IO_UPDATE_AREA_ASYNC);
+-- 
+1.7.12
+
diff --git a/0301-usb-controllers-do-not-need-to-check-for-babble-them.patch b/0301-usb-controllers-do-not-need-to-check-for-babble-them.patch
new file mode 100644
index 0000000..a1cfa08
--- /dev/null
+++ b/0301-usb-controllers-do-not-need-to-check-for-babble-them.patch
@@ -0,0 +1,56 @@
+From d69c3f589874de55e2eae03110a0c696485b8fa7 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 17 Aug 2012 11:39:16 +0200
+Subject: [PATCH 301/366] usb: controllers do not need to check for babble
+ themselves
+
+If an (emulated) usb-device tries to write more data to a packet then
+its iov len, this will trigger an assert in usb_packet_copy(), and if
+a driver somehow circumvents that check and writes more data to the
+iov then there is space, we have a much bigger problem then not correctly
+reporting babble to the guest.
+
+In practice babble will only happen with (real) redirected devices, and there
+both the usb-host os and the qemu usb-device code already check for it.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 4 ----
+ hw/usb/hcd-uhci.c | 5 -----
+ 2 files changed, 9 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 017342b..9523247 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -1481,10 +1481,6 @@ static void ehci_execute_complete(EHCIQueue *q)
+             assert(0);
+             break;
+         }
+-    } else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) {
+-        p->usb_status = USB_RET_BABBLE;
+-        q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
+-        ehci_raise_irq(q->ehci, USBSTS_ERRINT);
+     } else {
+         // TODO check 4.12 for splits
+ 
+diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
+index b0db921..c7c8786 100644
+--- a/hw/usb/hcd-uhci.c
++++ b/hw/usb/hcd-uhci.c
+@@ -729,11 +729,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
+         *int_mask |= 0x01;
+ 
+     if (pid == USB_TOKEN_IN) {
+-        if (len > max_len) {
+-            ret = USB_RET_BABBLE;
+-            goto out;
+-        }
+-
+         if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
+             *int_mask |= 0x02;
+             /* short packet: do not update QH */
+-- 
+1.7.12
+
diff --git a/0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch b/0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch
new file mode 100644
index 0000000..e40f57d
--- /dev/null
+++ b/0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch
@@ -0,0 +1,35 @@
+From ae5b888748b713d018ca7b339262b32bf88ec1be Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 12:33:44 +0200
+Subject: [PATCH 302/366] usb-core: Don't set packet state to complete on a
+ nak
+
+This way the hcd can re-use the same packet to retry without needing
+to re-init it.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/core.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/hw/usb/core.c b/hw/usb/core.c
+index 2da38e7..be6d936 100644
+--- a/hw/usb/core.c
++++ b/hw/usb/core.c
+@@ -399,8 +399,10 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
+              * otherwise packets can complete out of order!
+              */
+             assert(!p->ep->pipeline);
+-            p->result = ret;
+-            usb_packet_set_state(p, USB_PACKET_COMPLETE);
++            if (ret != USB_RET_NAK) {
++                p->result = ret;
++                usb_packet_set_state(p, USB_PACKET_COMPLETE);
++            }
+         }
+     } else {
+         ret = USB_RET_ASYNC;
+-- 
+1.7.12
+
diff --git a/0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch b/0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch
new file mode 100644
index 0000000..c3ed784
--- /dev/null
+++ b/0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch
@@ -0,0 +1,52 @@
+From d42de93ca42a6d3b5101ec3db474f52a87a07a63 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 28 Aug 2012 09:43:18 +0200
+Subject: [PATCH 303/366] usb-core: Add a usb_ep_find_packet_by_id() helper
+ function
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb.h      |  2 ++
+ hw/usb/core.c | 15 +++++++++++++++
+ 2 files changed, 17 insertions(+)
+
+diff --git a/hw/usb.h b/hw/usb.h
+index b8fceec..684e3f4 100644
+--- a/hw/usb.h
++++ b/hw/usb.h
+@@ -377,6 +377,8 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
+                                 uint16_t raw);
+ int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
+ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
++USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
++                                    uint64_t id);
+ 
+ void usb_attach(USBPort *port);
+ void usb_detach(USBPort *port);
+diff --git a/hw/usb/core.c b/hw/usb/core.c
+index be6d936..fe431d0 100644
+--- a/hw/usb/core.c
++++ b/hw/usb/core.c
+@@ -726,3 +726,18 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
+     struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
+     uep->pipeline = enabled;
+ }
++
++USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
++                                    uint64_t id)
++{
++    struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
++    USBPacket *p;
++
++    while ((p = QTAILQ_FIRST(&uep->queue)) != NULL) {
++        if (p->id == id) {
++            return p;
++        }
++    }
++
++    return NULL;
++}
+-- 
+1.7.12
+
diff --git a/0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch b/0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch
new file mode 100644
index 0000000..ffcd31a
--- /dev/null
+++ b/0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch
@@ -0,0 +1,32 @@
+From d0c16c3cd8dc1f2dc5ca6dae0d09e5f066332531 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 12:48:49 +0200
+Subject: [PATCH 304/366] usb-core: Allow the first packet of a pipelined ep
+ to complete immediately
+
+This can happen with usb-redir live-migration when the packet gets re-queued
+after the migration and the original queuing from the migration source side
+has already finished.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/core.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/usb/core.c b/hw/usb/core.c
+index fe431d0..b9f1f7a 100644
+--- a/hw/usb/core.c
++++ b/hw/usb/core.c
+@@ -398,7 +398,7 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
+              * When pipelining is enabled usb-devices must always return async,
+              * otherwise packets can complete out of order!
+              */
+-            assert(!p->ep->pipeline);
++            assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue));
+             if (ret != USB_RET_NAK) {
+                 p->result = ret;
+                 usb_packet_set_state(p, USB_PACKET_COMPLETE);
+-- 
+1.7.12
+
diff --git a/0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch b/0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch
new file mode 100644
index 0000000..6d8c7e9
--- /dev/null
+++ b/0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch
@@ -0,0 +1,121 @@
+From eccc0da01324939a52a74c763ade93e4a38d7328 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 29 Aug 2012 10:12:52 +0200
+Subject: [PATCH 305/366] Revert "ehci: don't flush cache on doorbell rings."
+
+This reverts commit 9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0, which got
+added to fix an issue where the real, underlying cause was not stopping
+the ep queue on an error.
+
+Now that the underlying cause is fixed by the "usb: Halt ep queue and
+cancel pending packets on a packet error" patch, the "don't flush" fix
+is no longer needed.
+
+Not only is it not needed, it causes us to see cancellations (unlinks)
+done by the Linux EHCI driver too late, which in combination with the new
+usb-core packet-id generation where qtd addresses are used as ids, causes
+duplicate ids for in flight packets.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 35 ++++++-----------------------------
+ 1 file changed, 6 insertions(+), 29 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 9523247..e7c36f4 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -365,7 +365,6 @@ struct EHCIQueue {
+     uint32_t seen;
+     uint64_t ts;
+     int async;
+-    int revalidate;
+ 
+     /* cached data from guest - needs to be flushed
+      * when guest removes an entry (doorbell, handshake sequence)
+@@ -805,18 +804,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
+     return NULL;
+ }
+ 
+-static void ehci_queues_tag_unused_async(EHCIState *ehci)
+-{
+-    EHCIQueue *q;
+-
+-    QTAILQ_FOREACH(q, &ehci->aqueues, next) {
+-        if (!q->seen) {
+-            q->revalidate = 1;
+-        }
+-    }
+-}
+-
+-static void ehci_queues_rip_unused(EHCIState *ehci, int async)
++static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
+ {
+     EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+     uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
+@@ -828,7 +816,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async)
+             q->ts = ehci->last_run_ns;
+             continue;
+         }
+-        if (ehci->last_run_ns < q->ts + maxage) {
++        if (!flush && ehci->last_run_ns < q->ts + maxage) {
+             continue;
+         }
+         ehci_free_queue(q);
+@@ -1684,7 +1672,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
+         ehci_set_usbsts(ehci, USBSTS_REC);
+     }
+ 
+-    ehci_queues_rip_unused(ehci, async);
++    ehci_queues_rip_unused(ehci, async, 0);
+ 
+     /*  Find the head of the list (4.9.1.1) */
+     for(i = 0; i < MAX_QH; i++) {
+@@ -1769,7 +1757,6 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
+     EHCIPacket *p;
+     uint32_t entry, devaddr;
+     EHCIQueue *q;
+-    EHCIqh qh;
+ 
+     entry = ehci_get_fetch_addr(ehci, async);
+     q = ehci_find_queue_by_qh(ehci, entry, async);
+@@ -1787,17 +1774,7 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
+     }
+ 
+     get_dwords(ehci, NLPTR_GET(q->qhaddr),
+-               (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
+-    if (q->revalidate && (q->qh.epchar      != qh.epchar ||
+-                          q->qh.epcap       != qh.epcap  ||
+-                          q->qh.current_qtd != qh.current_qtd)) {
+-        ehci_free_queue(q);
+-        q = ehci_alloc_queue(ehci, entry, async);
+-        q->seen++;
+-        p = NULL;
+-    }
+-    q->qh = qh;
+-    q->revalidate = 0;
++               (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
+     ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
+ 
+     devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
+@@ -2306,7 +2283,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
+          */
+         if (ehci->usbcmd & USBCMD_IAAD) {
+             /* Remove all unseen qhs from the async qhs queue */
+-            ehci_queues_tag_unused_async(ehci);
++            ehci_queues_rip_unused(ehci, async, 1);
+             DPRINTF("ASYNC: doorbell request acknowledged\n");
+             ehci->usbcmd &= ~USBCMD_IAAD;
+             ehci_raise_irq(ehci, USBSTS_IAA);
+@@ -2359,7 +2336,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
+         ehci_set_fetch_addr(ehci, async,entry);
+         ehci_set_state(ehci, async, EST_FETCHENTRY);
+         ehci_advance_state(ehci, async);
+-        ehci_queues_rip_unused(ehci, async);
++        ehci_queues_rip_unused(ehci, async, 0);
+         break;
+ 
+     default:
+-- 
+1.7.12
+
diff --git a/0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch b/0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch
new file mode 100644
index 0000000..7af3a27
--- /dev/null
+++ b/0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch
@@ -0,0 +1,84 @@
+From 1ee48073d05a8a0dfe08ad2853be125a87f176de Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 29 Aug 2012 10:37:37 +0200
+Subject: [PATCH 306/366] ehci: Validate qh is not changed unexpectedly by the
+ guest
+
+-combine the qh check with the check for devaddr changes
+-also ensure that p gets set to NULL when the queue gets cancelled on
+ devaddr change, which was not done properly before this patch
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 39 ++++++++++++++++++++++++++++-----------
+ 1 file changed, 28 insertions(+), 11 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index e7c36f4..35eb441 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -780,6 +780,14 @@ static void ehci_cancel_queue(EHCIQueue *q)
+     } while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
+ }
+ 
++static void ehci_reset_queue(EHCIQueue *q)
++{
++    trace_usb_ehci_queue_action(q, "reset");
++    ehci_cancel_queue(q);
++    q->dev = NULL;
++    q->qtdaddr = 0;
++}
++
+ static void ehci_free_queue(EHCIQueue *q)
+ {
+     EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
+@@ -1755,8 +1763,9 @@ out:
+ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
+ {
+     EHCIPacket *p;
+-    uint32_t entry, devaddr;
++    uint32_t entry, devaddr, endp;
+     EHCIQueue *q;
++    EHCIqh qh;
+ 
+     entry = ehci_get_fetch_addr(ehci, async);
+     q = ehci_find_queue_by_qh(ehci, entry, async);
+@@ -1774,17 +1783,25 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
+     }
+ 
+     get_dwords(ehci, NLPTR_GET(q->qhaddr),
+-               (uint32_t *) &q->qh, sizeof(EHCIqh) >> 2);
+-    ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
++               (uint32_t *) &qh, sizeof(EHCIqh) >> 2);
++    ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh);
++
++    /*
++     * The overlay area of the qh should never be changed by the guest,
++     * except when idle, in which case the reset is a nop.
++     */
++    devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR);
++    endp    = get_field(qh.epchar, QH_EPCHAR_EP);
++    if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) ||
++        (endp    != get_field(q->qh.epchar, QH_EPCHAR_EP)) ||
++        (memcmp(&qh.current_qtd, &q->qh.current_qtd,
++                                 9 * sizeof(uint32_t)) != 0) ||
++        (q->dev != NULL && q->dev->addr != devaddr)) {
++        ehci_reset_queue(q);
++        p = NULL;
++    }
++    q->qh = qh;
+ 
+-    devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
+-    if (q->dev != NULL && q->dev->addr != devaddr) {
+-        if (!QTAILQ_EMPTY(&q->packets)) {
+-            /* should not happen (guest bug) */
+-            ehci_cancel_queue(q);
+-        }
+-        q->dev = NULL;
+-    }
+     if (q->dev == NULL) {
+         q->dev = ehci_find_device(q->ehci, devaddr);
+     }
+-- 
+1.7.12
+
diff --git a/0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch b/0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch
new file mode 100644
index 0000000..edfb6e0
--- /dev/null
+++ b/0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch
@@ -0,0 +1,33 @@
+From faeefe2a1fdb5895bffb1380b318f3cc396cb057 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 28 Aug 2012 16:21:12 +0200
+Subject: [PATCH 307/366] ehci: Update copyright headers to reflect recent
+ work
+
+Update copyright headers to reflect all the work Gerd and I have been doing
+on the EHCI emulation.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 35eb441..78a248f 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -2,6 +2,11 @@
+  * QEMU USB EHCI Emulation
+  *
+  * Copyright(c) 2008  Emutex Ltd. (address@hidden)
++ * Copyright(c) 2011-2012 Red Hat, Inc.
++ *
++ * Red Hat Authors:
++ * Gerd Hoffmann <kraxel@redhat.com>
++ * Hans de Goede <hdegoede@redhat.com>
+  *
+  * EHCI project was started by Mark Burkley, with contributions by
+  * Niels de Vos.  David S. Ahern continued working on it.  Kevin Wolf,
+-- 
+1.7.12
+
diff --git a/0308-ehci-Properly-cleanup-packets-on-cancel.patch b/0308-ehci-Properly-cleanup-packets-on-cancel.patch
new file mode 100644
index 0000000..03b9ce4
--- /dev/null
+++ b/0308-ehci-Properly-cleanup-packets-on-cancel.patch
@@ -0,0 +1,26 @@
+From 95a792a46e8daae4bfb0997f6340bcd8dddea06c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 30 Aug 2012 15:00:33 +0200
+Subject: [PATCH 308/366] ehci: Properly cleanup packets on cancel
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 78a248f..4fe85c8 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -747,6 +747,8 @@ static void ehci_free_packet(EHCIPacket *p)
+     trace_usb_ehci_packet_action(p->queue, p, "free");
+     if (p->async == EHCI_ASYNC_INFLIGHT) {
+         usb_cancel_packet(&p->packet);
++        usb_packet_unmap(&p->packet, &p->sgl);
++        qemu_sglist_destroy(&p->sgl);
+     }
+     QTAILQ_REMOVE(&p->queue->packets, p, next);
+     usb_packet_cleanup(&p->packet);
+-- 
+1.7.12
+
diff --git a/0309-ehci-Properly-report-completed-but-not-yet-processed.patch b/0309-ehci-Properly-report-completed-but-not-yet-processed.patch
new file mode 100644
index 0000000..445b322
--- /dev/null
+++ b/0309-ehci-Properly-report-completed-but-not-yet-processed.patch
@@ -0,0 +1,49 @@
+From 584606864261092041b46806030e4889b747ad41 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 30 Aug 2012 15:18:24 +0200
+Subject: [PATCH 309/366] ehci: Properly report completed but not yet
+ processed packets to the guest
+
+Reported packets which have completed before being cancelled as such to the
+host. Note that the new code path this patch adds is untested since it I've
+been unable to actually trigger the race which needs this code path.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 4fe85c8..0a6c9ef 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -489,6 +489,9 @@ static const char *ehci_mmio_names[] = {
+     [CONFIGFLAG]        = "CONFIGFLAG",
+ };
+ 
++static int ehci_state_executing(EHCIQueue *q);
++static int ehci_state_writeback(EHCIQueue *q);
++
+ static const char *nr2str(const char **n, size_t len, uint32_t nr)
+ {
+     if (nr < len && n[nr] != NULL) {
+@@ -750,6 +753,16 @@ static void ehci_free_packet(EHCIPacket *p)
+         usb_packet_unmap(&p->packet, &p->sgl);
+         qemu_sglist_destroy(&p->sgl);
+     }
++    if (p->async == EHCI_ASYNC_FINISHED) {
++        int state = ehci_get_state(p->queue->ehci, p->queue->async);
++        /* This is a normal, but rare condition (cancel racing completion) */
++        fprintf(stderr, "EHCI: Warning packet completed but not processed\n");
++        ehci_state_executing(p->queue);
++        ehci_state_writeback(p->queue);
++        ehci_set_state(p->queue->ehci, p->queue->async, state);
++        /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */
++        return;
++    }
+     QTAILQ_REMOVE(&p->queue->packets, p, next);
+     usb_packet_cleanup(&p->packet);
+     g_free(p);
+-- 
+1.7.12
+
diff --git a/0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch b/0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch
new file mode 100644
index 0000000..bcb596c
--- /dev/null
+++ b/0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch
@@ -0,0 +1,47 @@
+From 6f009493144d09e417997caa13b62a38798dc206 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 31 Aug 2012 10:31:54 +0200
+Subject: [PATCH 310/366] ehci: check for EHCI_ASYNC_FINISHED first in
+ ehci_free_packet
+
+Otherwise we'll see the packet free twice in the trace log even though
+it actually happens only once.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 0a6c9ef..23221d0 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -747,12 +747,6 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
+ 
+ static void ehci_free_packet(EHCIPacket *p)
+ {
+-    trace_usb_ehci_packet_action(p->queue, p, "free");
+-    if (p->async == EHCI_ASYNC_INFLIGHT) {
+-        usb_cancel_packet(&p->packet);
+-        usb_packet_unmap(&p->packet, &p->sgl);
+-        qemu_sglist_destroy(&p->sgl);
+-    }
+     if (p->async == EHCI_ASYNC_FINISHED) {
+         int state = ehci_get_state(p->queue->ehci, p->queue->async);
+         /* This is a normal, but rare condition (cancel racing completion) */
+@@ -763,6 +757,12 @@ static void ehci_free_packet(EHCIPacket *p)
+         /* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */
+         return;
+     }
++    trace_usb_ehci_packet_action(p->queue, p, "free");
++    if (p->async == EHCI_ASYNC_INFLIGHT) {
++        usb_cancel_packet(&p->packet);
++        usb_packet_unmap(&p->packet, &p->sgl);
++        qemu_sglist_destroy(&p->sgl);
++    }
+     QTAILQ_REMOVE(&p->queue->packets, p, next);
+     usb_packet_cleanup(&p->packet);
+     g_free(p);
+-- 
+1.7.12
+
diff --git a/0311-ehci-trace-guest-bugs.patch b/0311-ehci-trace-guest-bugs.patch
new file mode 100644
index 0000000..0abd96e
--- /dev/null
+++ b/0311-ehci-trace-guest-bugs.patch
@@ -0,0 +1,106 @@
+From 044dcae94243d03739c309935a68b646424a4d4e Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 31 Aug 2012 10:44:21 +0200
+Subject: [PATCH 311/366] ehci: trace guest bugs
+
+make qemu_queue_{cancel,reset} return the number of packets released,
+so the caller can figure whenever there have been active packets even
+though there shouldn't have been any.  Add tracepoint to log this.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 26 ++++++++++++++++++++------
+ trace-events      |  1 +
+ 2 files changed, 21 insertions(+), 6 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 23221d0..4564615 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -716,6 +716,12 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
+                         (bool)(sitd->results & SITD_RESULTS_ACTIVE));
+ }
+ 
++static void ehci_trace_guest_bug(EHCIState *s, const char *message)
++{
++    trace_usb_ehci_guest_bug(message);
++    fprintf(stderr, "ehci warning: %s\n", message);
++}
++
+ static inline bool ehci_enabled(EHCIState *s)
+ {
+     return s->usbcmd & USBCMD_RUNSTOP;
+@@ -785,27 +791,33 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
+     return q;
+ }
+ 
+-static void ehci_cancel_queue(EHCIQueue *q)
++static int ehci_cancel_queue(EHCIQueue *q)
+ {
+     EHCIPacket *p;
++    int packets = 0;
+ 
+     p = QTAILQ_FIRST(&q->packets);
+     if (p == NULL) {
+-        return;
++        return 0;
+     }
+ 
+     trace_usb_ehci_queue_action(q, "cancel");
+     do {
+         ehci_free_packet(p);
++        packets++;
+     } while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
++    return packets;
+ }
+ 
+-static void ehci_reset_queue(EHCIQueue *q)
++static int ehci_reset_queue(EHCIQueue *q)
+ {
++    int packets;
++
+     trace_usb_ehci_queue_action(q, "reset");
+-    ehci_cancel_queue(q);
++    packets = ehci_cancel_queue(q);
+     q->dev = NULL;
+     q->qtdaddr = 0;
++    return packets;
+ }
+ 
+ static void ehci_free_queue(EHCIQueue *q)
+@@ -1817,7 +1829,9 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
+         (memcmp(&qh.current_qtd, &q->qh.current_qtd,
+                                  9 * sizeof(uint32_t)) != 0) ||
+         (q->dev != NULL && q->dev->addr != devaddr)) {
+-        ehci_reset_queue(q);
++        if (ehci_reset_queue(q) > 0) {
++            ehci_trace_guest_bug(ehci, "guest updated active QH");
++        }
+         p = NULL;
+     }
+     q->qh = qh;
+@@ -1979,8 +1993,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
+             (!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) ||
+             (!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) ||
+             p->qtd.bufptr[0] != qtd.bufptr[0]) {
+-            /* guest bug: guest updated active QH or qTD underneath us */
+             ehci_cancel_queue(q);
++            ehci_trace_guest_bug(q->ehci, "guest updated active QH or qTD");
+             p = NULL;
+         } else {
+             p->qtd = qtd;
+diff --git a/trace-events b/trace-events
+index 8fcbc50..5112a47 100644
+--- a/trace-events
++++ b/trace-events
+@@ -263,6 +263,7 @@ usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t l
+ usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
+ usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
+ usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x"
++usb_ehci_guest_bug(const char *reason) "%s"
+ 
+ # hw/usb/hcd-uhci.c
+ usb_uhci_reset(void) "=== RESET ==="
+-- 
+1.7.12
+
diff --git a/0312-ehci-add-doorbell-trace-events.patch b/0312-ehci-add-doorbell-trace-events.patch
new file mode 100644
index 0000000..817132d
--- /dev/null
+++ b/0312-ehci-add-doorbell-trace-events.patch
@@ -0,0 +1,48 @@
+From 77e3a7e6bf15a85040101e7c0990dd82abdc9e9c Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 31 Aug 2012 12:41:43 +0200
+Subject: [PATCH 312/366] ehci: add doorbell trace events
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 3 ++-
+ trace-events      | 2 ++
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 4564615..398f5e0 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -1241,6 +1241,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+              */
+             s->async_stepdown = 0;
+             qemu_bh_schedule(s->async_bh);
++            trace_usb_ehci_doorbell_ring();
+         }
+ 
+         if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
+@@ -2335,7 +2336,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
+         if (ehci->usbcmd & USBCMD_IAAD) {
+             /* Remove all unseen qhs from the async qhs queue */
+             ehci_queues_rip_unused(ehci, async, 1);
+-            DPRINTF("ASYNC: doorbell request acknowledged\n");
++            trace_usb_ehci_doorbell_ack();
+             ehci->usbcmd &= ~USBCMD_IAAD;
+             ehci_raise_irq(ehci, USBSTS_IAA);
+         }
+diff --git a/trace-events b/trace-events
+index 5112a47..10bc04e 100644
+--- a/trace-events
++++ b/trace-events
+@@ -264,6 +264,8 @@ usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
+ usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
+ usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x"
+ usb_ehci_guest_bug(const char *reason) "%s"
++usb_ehci_doorbell_ring(void) ""
++usb_ehci_doorbell_ack(void) ""
+ 
+ # hw/usb/hcd-uhci.c
+ usb_uhci_reset(void) "=== RESET ==="
+-- 
+1.7.12
+
diff --git a/0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch b/0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch
new file mode 100644
index 0000000..0587c98
--- /dev/null
+++ b/0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch
@@ -0,0 +1,86 @@
+From cbbfcc647e26708cdd78c8ddf28d99f8a1e1e6b0 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 10:22:16 +0200
+Subject: [PATCH 313/366] ehci: Add some additional ehci_trace_guest_bug()
+ calls
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 398f5e0..5a88268 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -820,12 +820,16 @@ static int ehci_reset_queue(EHCIQueue *q)
+     return packets;
+ }
+ 
+-static void ehci_free_queue(EHCIQueue *q)
++static void ehci_free_queue(EHCIQueue *q, const char *warn)
+ {
+     EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
++    int cancelled;
+ 
+     trace_usb_ehci_queue_action(q, "free");
+-    ehci_cancel_queue(q);
++    cancelled = ehci_cancel_queue(q);
++    if (warn && cancelled > 0) {
++        ehci_trace_guest_bug(q->ehci, warn);
++    }
+     QTAILQ_REMOVE(head, q, next);
+     g_free(q);
+ }
+@@ -847,6 +851,7 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
+ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
+ {
+     EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
++    const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL;
+     uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
+     EHCIQueue *q, *tmp;
+ 
+@@ -859,7 +864,7 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
+         if (!flush && ehci->last_run_ns < q->ts + maxage) {
+             continue;
+         }
+-        ehci_free_queue(q);
++        ehci_free_queue(q, warn);
+     }
+ }
+ 
+@@ -872,17 +877,18 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
+         if (q->dev != dev) {
+             continue;
+         }
+-        ehci_free_queue(q);
++        ehci_free_queue(q, NULL);
+     }
+ }
+ 
+ static void ehci_queues_rip_all(EHCIState *ehci, int async)
+ {
+     EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
++    const char *warn = async ? "guest stopped busy async schedule" : NULL;
+     EHCIQueue *q, *tmp;
+ 
+     QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
+-        ehci_free_queue(q);
++        ehci_free_queue(q, warn);
+     }
+ }
+ 
+@@ -1549,7 +1555,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
+ 
+     p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
+     if (p->tbytes > BUFF_SIZE) {
+-        fprintf(stderr, "Request for more bytes than allowed\n");
++        ehci_trace_guest_bug(p->queue->ehci,
++                             "guest requested more bytes than allowed");
+         return USB_RET_PROCERR;
+     }
+ 
+-- 
+1.7.12
+
diff --git a/0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch b/0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch
new file mode 100644
index 0000000..3c82869
--- /dev/null
+++ b/0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch
@@ -0,0 +1,117 @@
+From 68851693ee59d3f17c14983902076a5ef49e2e80 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 11:01:13 +0200
+Subject: [PATCH 314/366] ehci: Fix memory leak in handling of NAK-ed packets
+
+Currently each time we try to execute a NAK-ed packet we redo
+ehci_init_transfer, and usb_packet_map, re-allocing (without freeing) the
+sg list every time.
+
+This patch fixes this, it does this by introducing another async state, so
+that we also properly cleanup a NAK-ed packet on cancel.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 38 +++++++++++++++++++++++++++-----------
+ 1 file changed, 27 insertions(+), 11 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 5a88268..d87aca8 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -345,6 +345,7 @@ typedef struct EHCIState EHCIState;
+ 
+ enum async_state {
+     EHCI_ASYNC_NONE = 0,
++    EHCI_ASYNC_INITIALIZED,
+     EHCI_ASYNC_INFLIGHT,
+     EHCI_ASYNC_FINISHED,
+ };
+@@ -764,6 +765,10 @@ static void ehci_free_packet(EHCIPacket *p)
+         return;
+     }
+     trace_usb_ehci_packet_action(p->queue, p, "free");
++    if (p->async == EHCI_ASYNC_INITIALIZED) {
++        usb_packet_unmap(&p->packet, &p->sgl);
++        qemu_sglist_destroy(&p->sgl);
++    }
+     if (p->async == EHCI_ASYNC_INFLIGHT) {
+         usb_cancel_packet(&p->packet);
+         usb_packet_unmap(&p->packet, &p->sgl);
+@@ -1485,8 +1490,8 @@ static void ehci_execute_complete(EHCIQueue *q)
+ 
+     assert(p != NULL);
+     assert(p->qtdaddr == q->qtdaddr);
+-    assert(p->async != EHCI_ASYNC_INFLIGHT);
+-    p->async = EHCI_ASYNC_NONE;
++    assert(p->async == EHCI_ASYNC_INITIALIZED ||
++           p->async == EHCI_ASYNC_FINISHED);
+ 
+     DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
+             q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
+@@ -1531,6 +1536,7 @@ static void ehci_execute_complete(EHCIQueue *q)
+     ehci_finish_transfer(q, p->usb_status);
+     usb_packet_unmap(&p->packet, &p->sgl);
+     qemu_sglist_destroy(&p->sgl);
++    p->async = EHCI_ASYNC_NONE;
+ 
+     q->qh.token ^= QTD_TOKEN_DTOGGLE;
+     q->qh.token &= ~QTD_TOKEN_ACTIVE;
+@@ -1548,6 +1554,9 @@ static int ehci_execute(EHCIPacket *p, const char *action)
+     int ret;
+     int endp;
+ 
++    assert(p->async == EHCI_ASYNC_NONE ||
++           p->async == EHCI_ASYNC_INITIALIZED);
++
+     if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
+         fprintf(stderr, "Attempting to execute inactive qtd\n");
+         return USB_RET_PROCERR;
+@@ -1576,15 +1585,18 @@ static int ehci_execute(EHCIPacket *p, const char *action)
+         break;
+     }
+ 
+-    if (ehci_init_transfer(p) != 0) {
+-        return USB_RET_PROCERR;
+-    }
+-
+     endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
+     ep = usb_ep_get(p->queue->dev, p->pid, endp);
+ 
+-    usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
+-    usb_packet_map(&p->packet, &p->sgl);
++    if (p->async == EHCI_ASYNC_NONE) {
++        if (ehci_init_transfer(p) != 0) {
++            return USB_RET_PROCERR;
++        }
++
++        usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
++        usb_packet_map(&p->packet, &p->sgl);
++        p->async = EHCI_ASYNC_INITIALIZED;
++    }
+ 
+     trace_usb_ehci_packet_action(p->queue, p, action);
+     ret = usb_handle_packet(p->queue->dev, &p->packet);
+@@ -2021,11 +2033,15 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
+     } else if (p != NULL) {
+         switch (p->async) {
+         case EHCI_ASYNC_NONE:
++            /* Should never happen packet should at least be initialized */
++            assert(0);
++            break;
++        case EHCI_ASYNC_INITIALIZED:
+             /* Previously nacked packet (likely interrupt ep) */
+-           ehci_set_state(q->ehci, q->async, EST_EXECUTE);
+-           break;
++            ehci_set_state(q->ehci, q->async, EST_EXECUTE);
++            break;
+         case EHCI_ASYNC_INFLIGHT:
+-            /* Unfinyshed async handled packet, go horizontal */
++            /* Unfinished async handled packet, go horizontal */
+             ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+             break;
+         case EHCI_ASYNC_FINISHED:
+-- 
+1.7.12
+
diff --git a/0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch b/0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch
new file mode 100644
index 0000000..a52d3db
--- /dev/null
+++ b/0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch
@@ -0,0 +1,54 @@
+From 1599d0b01712fb16c3ad291b653a31f768f7f5ef Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 11:35:58 +0200
+Subject: [PATCH 315/366] ehci: Handle USB_RET_PROCERR in ehci_fill_queue
+
+USB_RET_PROCERR can be triggered by the guest (by for example requesting more
+then BUFFSIZE bytes), so don't assert on it.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index d87aca8..2534394 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -2076,7 +2076,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
+     return again;
+ }
+ 
+-static void ehci_fill_queue(EHCIPacket *p)
++static int ehci_fill_queue(EHCIPacket *p)
+ {
+     EHCIQueue *q = p->queue;
+     EHCIqtd qtd = p->qtd;
+@@ -2100,9 +2100,13 @@ static void ehci_fill_queue(EHCIPacket *p)
+         p->qtdaddr = qtdaddr;
+         p->qtd = qtd;
+         p->usb_status = ehci_execute(p, "queue");
++        if (p->usb_status == USB_RET_PROCERR) {
++            break;
++        }
+         assert(p->usb_status == USB_RET_ASYNC);
+         p->async = EHCI_ASYNC_INFLIGHT;
+     }
++    return p->usb_status;
+ }
+ 
+ static int ehci_state_execute(EHCIQueue *q)
+@@ -2144,8 +2148,7 @@ static int ehci_state_execute(EHCIQueue *q)
+         trace_usb_ehci_packet_action(p->queue, p, "async");
+         p->async = EHCI_ASYNC_INFLIGHT;
+         ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+-        again = 1;
+-        ehci_fill_queue(p);
++        again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1;
+         goto out;
+     }
+ 
+-- 
+1.7.12
+
diff --git a/0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch b/0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch
new file mode 100644
index 0000000..8bd379d
--- /dev/null
+++ b/0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch
@@ -0,0 +1,35 @@
+From d17b1ad80cba3354b3eca5b8464bf7bb3f8e95c1 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 12:17:48 +0200
+Subject: [PATCH 316/366] ehci: Correct a comment in fetchqtd packet
+ processing
+
+Since my previous comment said "Should never happen", I tried changing the
+next line to an assert(0), which did not go well, which as the new comments
+explains is logical if you think about it for a moment.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 2534394..2f3e9c0 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -2045,7 +2045,10 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
+             ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
+             break;
+         case EHCI_ASYNC_FINISHED:
+-            /* Should never happen, as this case is caught by fetchqh */
++            /*
++             * We get here when advqueue moves to a packet which is already
++             * finished, which can happen with packets queued up by fill_queue
++             */
+             ehci_set_state(q->ehci, q->async, EST_EXECUTING);
+             break;
+         }
+-- 
+1.7.12
+
diff --git a/0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch b/0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch
new file mode 100644
index 0000000..01ce1e1
--- /dev/null
+++ b/0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch
@@ -0,0 +1,44 @@
+From a15db9c6d428a756f6ad4055334e476cd90b31f6 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 17 Aug 2012 17:27:08 +0200
+Subject: [PATCH 317/366] usb-redir: Never return USB_RET_NAK for async
+ handled packets
+
+USB_RET_NAK is not a valid response for async handled packets (and will
+trigger an assert as such).
+
+Also drop the warning when receiving a status of cancelled for packets not
+cancelled by qemu itself, this can happen when a device gets unredirected
+by the usbredir-host while transfers are pending.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 1460515..9ba964e 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -1055,11 +1055,14 @@ static int usbredir_handle_status(USBRedirDevice *dev,
+     case usb_redir_stall:
+         return USB_RET_STALL;
+     case usb_redir_cancelled:
+-        WARNING("returning cancelled packet to HC?\n");
+-        return USB_RET_NAK;
++        /*
++         * When the usbredir-host unredirects a device, it will report a status
++         * of cancelled for all pending packets, followed by a disconnect msg.
++         */
++        return USB_RET_IOERROR;
+     case usb_redir_inval:
+         WARNING("got invalid param error from usb-host?\n");
+-        return USB_RET_NAK;
++        return USB_RET_IOERROR;
+     case usb_redir_babble:
+         return USB_RET_BABBLE;
+     case usb_redir_ioerror:
+-- 
+1.7.12
+
diff --git a/0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch b/0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch
new file mode 100644
index 0000000..4994684
--- /dev/null
+++ b/0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch
@@ -0,0 +1,181 @@
+From acf269a7bbed7c969eb9be02943f7e4b52196a8f Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 23 Aug 2012 16:37:19 +0200
+Subject: [PATCH 318/366] usb-redir: Don't delay handling of open events to a
+ bottom half
+
+There is no need for this, and doing so means that a backend trying to
+write immediately after an open event will see qemu_chr_be_can_write
+returning 0, which not all backends handle well as there is no wakeup
+mechanism to detect when the frontend does become writable.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 100 +++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 53 insertions(+), 47 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 9ba964e..8ac8637 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -79,8 +79,8 @@ struct USBRedirDevice {
+     /* Data passed from chardev the fd_read cb to the usbredirparser read cb */
+     const uint8_t *read_buf;
+     int read_buf_size;
+-    /* For async handling of open/close */
+-    QEMUBH *open_close_bh;
++    /* For async handling of close */
++    QEMUBH *chardev_close_bh;
+     /* To delay the usb attach in case of quick chardev close + open */
+     QEMUTimer *attach_timer;
+     int64_t next_attach_time;
+@@ -794,18 +794,11 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+  * from within the USBDevice data / control packet callbacks and doing a
+  * usb_detach from within these callbacks is not a good idea.
+  *
+- * So we use a bh handler to take care of close events. We also handle
+- * open events from this callback to make sure that a close directly followed
+- * by an open gets handled in the right order.
++ * So we use a bh handler to take care of close events.
+  */
+-static void usbredir_open_close_bh(void *opaque)
++static void usbredir_chardev_close_bh(void *opaque)
+ {
+     USBRedirDevice *dev = opaque;
+-    uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
+-    char version[32];
+-
+-    strcpy(version, "qemu usb-redir guest ");
+-    pstrcat(version, sizeof(version), qemu_get_version());
+ 
+     usbredir_device_disconnect(dev);
+ 
+@@ -813,36 +806,47 @@ static void usbredir_open_close_bh(void *opaque)
+         usbredirparser_destroy(dev->parser);
+         dev->parser = NULL;
+     }
++}
+ 
+-    if (dev->cs->opened) {
+-        dev->parser = qemu_oom_check(usbredirparser_create());
+-        dev->parser->priv = dev;
+-        dev->parser->log_func = usbredir_log;
+-        dev->parser->read_func = usbredir_read;
+-        dev->parser->write_func = usbredir_write;
+-        dev->parser->hello_func = usbredir_hello;
+-        dev->parser->device_connect_func = usbredir_device_connect;
+-        dev->parser->device_disconnect_func = usbredir_device_disconnect;
+-        dev->parser->interface_info_func = usbredir_interface_info;
+-        dev->parser->ep_info_func = usbredir_ep_info;
+-        dev->parser->configuration_status_func = usbredir_configuration_status;
+-        dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
+-        dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
+-        dev->parser->interrupt_receiving_status_func =
+-            usbredir_interrupt_receiving_status;
+-        dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
+-        dev->parser->control_packet_func = usbredir_control_packet;
+-        dev->parser->bulk_packet_func = usbredir_bulk_packet;
+-        dev->parser->iso_packet_func = usbredir_iso_packet;
+-        dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
+-        dev->read_buf = NULL;
+-        dev->read_buf_size = 0;
++static void usbredir_chardev_open(USBRedirDevice *dev)
++{
++    uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
++    char version[32];
+ 
+-        usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+-        usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+-        usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
+-        usbredirparser_do_write(dev->parser);
+-    }
++    /* Make sure any pending closes are handled (no-op if none pending) */
++    usbredir_chardev_close_bh(dev);
++    qemu_bh_cancel(dev->chardev_close_bh);
++
++    strcpy(version, "qemu usb-redir guest ");
++    pstrcat(version, sizeof(version), qemu_get_version());
++
++    dev->parser = qemu_oom_check(usbredirparser_create());
++    dev->parser->priv = dev;
++    dev->parser->log_func = usbredir_log;
++    dev->parser->read_func = usbredir_read;
++    dev->parser->write_func = usbredir_write;
++    dev->parser->hello_func = usbredir_hello;
++    dev->parser->device_connect_func = usbredir_device_connect;
++    dev->parser->device_disconnect_func = usbredir_device_disconnect;
++    dev->parser->interface_info_func = usbredir_interface_info;
++    dev->parser->ep_info_func = usbredir_ep_info;
++    dev->parser->configuration_status_func = usbredir_configuration_status;
++    dev->parser->alt_setting_status_func = usbredir_alt_setting_status;
++    dev->parser->iso_stream_status_func = usbredir_iso_stream_status;
++    dev->parser->interrupt_receiving_status_func =
++        usbredir_interrupt_receiving_status;
++    dev->parser->bulk_streams_status_func = usbredir_bulk_streams_status;
++    dev->parser->control_packet_func = usbredir_control_packet;
++    dev->parser->bulk_packet_func = usbredir_bulk_packet;
++    dev->parser->iso_packet_func = usbredir_iso_packet;
++    dev->parser->interrupt_packet_func = usbredir_interrupt_packet;
++    dev->read_buf = NULL;
++    dev->read_buf_size = 0;
++
++    usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
++    usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
++    usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
++    usbredirparser_do_write(dev->parser);
+ }
+ 
+ static void usbredir_do_attach(void *opaque)
+@@ -866,13 +870,13 @@ static int usbredir_chardev_can_read(void *opaque)
+ {
+     USBRedirDevice *dev = opaque;
+ 
+-    if (dev->parser) {
+-        /* usbredir_parser_do_read will consume *all* data we give it */
+-        return 1024 * 1024;
+-    } else {
+-        /* usbredir_open_close_bh hasn't handled the open event yet */
++    if (!dev->parser) {
++        WARNING("chardev_can_read called on non open chardev!\n");
+         return 0;
+     }
++
++    /* usbredir_parser_do_read will consume *all* data we give it */
++    return 1024 * 1024;
+ }
+ 
+ static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size)
+@@ -896,8 +900,10 @@ static void usbredir_chardev_event(void *opaque, int event)
+ 
+     switch (event) {
+     case CHR_EVENT_OPENED:
++        usbredir_chardev_open(dev);
++        break;
+     case CHR_EVENT_CLOSED:
+-        qemu_bh_schedule(dev->open_close_bh);
++        qemu_bh_schedule(dev->chardev_close_bh);
+         break;
+     }
+ }
+@@ -945,7 +951,7 @@ static int usbredir_initfn(USBDevice *udev)
+         }
+     }
+ 
+-    dev->open_close_bh = qemu_bh_new(usbredir_open_close_bh, dev);
++    dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
+     dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+ 
+     QTAILQ_INIT(&dev->asyncq);
+@@ -984,7 +990,7 @@ static void usbredir_handle_destroy(USBDevice *udev)
+     qemu_chr_fe_close(dev->cs);
+     qemu_chr_delete(dev->cs);
+     /* Note must be done after qemu_chr_close, as that causes a close event */
+-    qemu_bh_delete(dev->open_close_bh);
++    qemu_bh_delete(dev->chardev_close_bh);
+ 
+     qemu_del_timer(dev->attach_timer);
+     qemu_free_timer(dev->attach_timer);
+-- 
+1.7.12
+
diff --git a/0319-usb-redir-Get-rid-of-async-struct-get-member.patch b/0319-usb-redir-Get-rid-of-async-struct-get-member.patch
new file mode 100644
index 0000000..daf27ed
--- /dev/null
+++ b/0319-usb-redir-Get-rid-of-async-struct-get-member.patch
@@ -0,0 +1,73 @@
+From 5f83bc6b7bf54271bc1e3460b2e6f790407964e0 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 27 Aug 2012 16:33:08 +0200
+Subject: [PATCH 319/366] usb-redir: Get rid of async-struct get member
+
+This is a preparation patch for completely getting rid of the async-packet
+struct in usb-redir, instead relying on the (new) per ep queues in the
+qemu usb core.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 8ac8637..4121983 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -1,7 +1,7 @@
+ /*
+  * USB redirector usb-guest
+  *
+- * Copyright (c) 2011 Red Hat, Inc.
++ * Copyright (c) 2011-2012 Red Hat, Inc.
+  *
+  * Red Hat Authors:
+  * Hans de Goede <hdegoede@redhat.com>
+@@ -99,7 +99,6 @@ struct AsyncURB {
+     USBRedirDevice *dev;
+     USBPacket *packet;
+     uint32_t packet_id;
+-    int get;
+     union {
+         struct usb_redir_control_packet_header control_packet;
+         struct usb_redir_bulk_packet_header bulk_packet;
+@@ -682,7 +681,6 @@ static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
+ 
+     DPRINTF("get config id %u\n", aurb->packet_id);
+ 
+-    aurb->get = 1;
+     usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
+     usbredirparser_do_write(dev->parser);
+     return USB_RET_ASYNC;
+@@ -731,7 +729,6 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
+     DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
+ 
+     get_alt.interface = interface;
+-    aurb->get = 1;
+     usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
+                                         &get_alt);
+     usbredirparser_do_write(dev->parser);
+@@ -1253,7 +1250,7 @@ static void usbredir_configuration_status(void *priv, uint32_t id,
+         return;
+     }
+     if (aurb->packet) {
+-        if (aurb->get) {
++        if (dev->dev.setup_buf[0] & USB_DIR_IN) {
+             dev->dev.data_buf[0] = config_status->configuration;
+             len = 1;
+         }
+@@ -1281,7 +1278,7 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id,
+         return;
+     }
+     if (aurb->packet) {
+-        if (aurb->get) {
++        if (dev->dev.setup_buf[0] & USB_DIR_IN) {
+             dev->dev.data_buf[0] = alt_setting_status->alt;
+             len = 1;
+         }
+-- 
+1.7.12
+
diff --git a/0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch b/0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch
new file mode 100644
index 0000000..ea5b5ed
--- /dev/null
+++ b/0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch
@@ -0,0 +1,107 @@
+From 3240adfcb041efcb11b4cbf4b08dc86be52b8ffc Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 28 Aug 2012 09:05:38 +0200
+Subject: [PATCH 320/366] usb-redir: Get rid of local shadow copy of packet
+ headers
+
+The shadow copy only serves as an extra check (besides the packet-id) to
+ensure the packet we get back is a reply to the packet we think it is.
+
+This check has never triggered in all the time usb-redir is in use now,
+and since the verified data in the returned packet-header is not used
+otherwise, removing the check does not open any possibilities for the
+usbredirhost to confuse us.
+
+This is a preparation patch for completely getting rid of the async-packet
+struct in usb-redir, instead relying on the (new) per ep queues in the
+qemu usb core.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 27 ---------------------------
+ 1 file changed, 27 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 4121983..816a19a 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -99,11 +99,6 @@ struct AsyncURB {
+     USBRedirDevice *dev;
+     USBPacket *packet;
+     uint32_t packet_id;
+-    union {
+-        struct usb_redir_control_packet_header control_packet;
+-        struct usb_redir_bulk_packet_header bulk_packet;
+-        struct usb_redir_interrupt_packet_header interrupt_packet;
+-    };
+     QTAILQ_ENTRY(AsyncURB)next;
+ };
+ 
+@@ -510,7 +505,6 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+     bulk_packet.endpoint  = ep;
+     bulk_packet.length    = p->iov.size;
+     bulk_packet.stream_id = 0;
+-    aurb->bulk_packet = bulk_packet;
+ 
+     if (ep & USB_DIR_IN) {
+         usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
+@@ -591,7 +585,6 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+ 
+         interrupt_packet.endpoint  = ep;
+         interrupt_packet.length    = p->iov.size;
+-        aurb->interrupt_packet     = interrupt_packet;
+ 
+         usb_packet_copy(p, buf, p->iov.size);
+         usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
+@@ -772,7 +765,6 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+     control_packet.value       = value;
+     control_packet.index       = index;
+     control_packet.length      = length;
+-    aurb->control_packet       = control_packet;
+ 
+     if (control_packet.requesttype & USB_DIR_IN) {
+         usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
+@@ -1353,14 +1345,6 @@ static void usbredir_control_packet(void *priv, uint32_t id,
+         return;
+     }
+ 
+-    aurb->control_packet.status = control_packet->status;
+-    aurb->control_packet.length = control_packet->length;
+-    if (memcmp(&aurb->control_packet, control_packet,
+-               sizeof(*control_packet))) {
+-        ERROR("return control packet mismatch, please report this!\n");
+-        len = USB_RET_NAK;
+-    }
+-
+     if (aurb->packet) {
+         len = usbredir_handle_status(dev, control_packet->status, len);
+         if (len > 0) {
+@@ -1398,12 +1382,6 @@ static void usbredir_bulk_packet(void *priv, uint32_t id,
+         return;
+     }
+ 
+-    if (aurb->bulk_packet.endpoint != bulk_packet->endpoint ||
+-            aurb->bulk_packet.stream_id != bulk_packet->stream_id) {
+-        ERROR("return bulk packet mismatch, please report this!\n");
+-        len = USB_RET_NAK;
+-    }
+-
+     if (aurb->packet) {
+         len = usbredir_handle_status(dev, bulk_packet->status, len);
+         if (len > 0) {
+@@ -1482,11 +1460,6 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
+             return;
+         }
+ 
+-        if (aurb->interrupt_packet.endpoint != interrupt_packet->endpoint) {
+-            ERROR("return int packet mismatch, please report this!\n");
+-            len = USB_RET_NAK;
+-        }
+-
+         if (aurb->packet) {
+             aurb->packet->result = usbredir_handle_status(dev,
+                                                interrupt_packet->status, len);
+-- 
+1.7.12
+
diff --git a/0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch b/0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch
new file mode 100644
index 0000000..3852c31
--- /dev/null
+++ b/0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch
@@ -0,0 +1,38 @@
+From 1b1c99626404328b571e1f6c18b50f9bed9c2e2c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 28 Aug 2012 09:08:45 +0200
+Subject: [PATCH 321/366] usb-redir: Get rid of unused async-struct dev member
+
+This is a preparation patch for completely getting rid of the async-packet
+struct in usb-redir, instead relying on the (new) per ep queues in the
+qemu usb core.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 816a19a..4a23b82 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -96,7 +96,6 @@ struct USBRedirDevice {
+ };
+ 
+ struct AsyncURB {
+-    USBRedirDevice *dev;
+     USBPacket *packet;
+     uint32_t packet_id;
+     QTAILQ_ENTRY(AsyncURB)next;
+@@ -255,7 +254,6 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
+ static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
+ {
+     AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB));
+-    aurb->dev = dev;
+     aurb->packet = p;
+     aurb->packet_id = dev->packet_id;
+     QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
+-- 
+1.7.12
+
diff --git a/0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch b/0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch
new file mode 100644
index 0000000..0bf0d67
--- /dev/null
+++ b/0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch
@@ -0,0 +1,499 @@
+From 69642884acc4f5d5b92e16d94fdae50765d1eba7 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 28 Aug 2012 11:30:13 +0200
+Subject: [PATCH 322/366] usb-redir: Move to core packet id and queue handling
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 226 ++++++++++++++++++++++--------------------------------
+ 1 file changed, 92 insertions(+), 134 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 4a23b82..1ce994c 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -43,7 +43,7 @@
+ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
+ #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
+ 
+-typedef struct AsyncURB AsyncURB;
++typedef struct Cancelled Cancelled;
+ typedef struct USBRedirDevice USBRedirDevice;
+ 
+ /* Struct to hold buffered packets (iso or int input packets) */
+@@ -86,8 +86,7 @@ struct USBRedirDevice {
+     int64_t next_attach_time;
+     struct usbredirparser *parser;
+     struct endp_data endpoint[MAX_ENDPOINTS];
+-    uint32_t packet_id;
+-    QTAILQ_HEAD(, AsyncURB) asyncq;
++    QTAILQ_HEAD(, Cancelled) cancelled;
+     /* Data for device filtering */
+     struct usb_redir_device_connect_header device_info;
+     struct usb_redir_interface_info_header interface_info;
+@@ -95,10 +94,9 @@ struct USBRedirDevice {
+     int filter_rules_count;
+ };
+ 
+-struct AsyncURB {
+-    USBPacket *packet;
+-    uint32_t packet_id;
+-    QTAILQ_ENTRY(AsyncURB)next;
++struct Cancelled {
++    uint64_t id;
++    QTAILQ_ENTRY(Cancelled)next;
+ };
+ 
+ static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
+@@ -248,57 +246,58 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
+ }
+ 
+ /*
+- * Async and buffered packets helpers
++ * Cancelled and buffered packets helpers
+  */
+ 
+-static AsyncURB *async_alloc(USBRedirDevice *dev, USBPacket *p)
++static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
+ {
+-    AsyncURB *aurb = (AsyncURB *) g_malloc0(sizeof(AsyncURB));
+-    aurb->packet = p;
+-    aurb->packet_id = dev->packet_id;
+-    QTAILQ_INSERT_TAIL(&dev->asyncq, aurb, next);
+-    dev->packet_id++;
++    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
++    Cancelled *c;
+ 
+-    return aurb;
+-}
++    DPRINTF("cancel packet id %"PRIu64"\n", p->id);
+ 
+-static void async_free(USBRedirDevice *dev, AsyncURB *aurb)
+-{
+-    QTAILQ_REMOVE(&dev->asyncq, aurb, next);
+-    g_free(aurb);
++    c = g_malloc0(sizeof(Cancelled));
++    c->id = p->id;
++    QTAILQ_INSERT_TAIL(&dev->cancelled, c, next);
++
++    usbredirparser_send_cancel_data_packet(dev->parser, p->id);
++    usbredirparser_do_write(dev->parser);
+ }
+ 
+-static AsyncURB *async_find(USBRedirDevice *dev, uint32_t packet_id)
++static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id)
+ {
+-    AsyncURB *aurb;
++    Cancelled *c;
++
++    if (!dev->dev.attached) {
++        return 1; /* Treat everything as cancelled after a disconnect */
++    }
+ 
+-    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+-        if (aurb->packet_id == packet_id) {
+-            return aurb;
++    QTAILQ_FOREACH(c, &dev->cancelled, next) {
++        if (c->id == id) {
++            QTAILQ_REMOVE(&dev->cancelled, c, next);
++            g_free(c);
++            return 1;
+         }
+     }
+-    DPRINTF("could not find async urb for packet_id %u\n", packet_id);
+-    return NULL;
++    return 0;
+ }
+ 
+-static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
++static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
++    uint8_t ep, uint64_t id)
+ {
+-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+-    AsyncURB *aurb;
+-
+-    QTAILQ_FOREACH(aurb, &dev->asyncq, next) {
+-        if (p != aurb->packet) {
+-            continue;
+-        }
++    USBPacket *p;
+ 
+-        DPRINTF("async cancel id %u\n", aurb->packet_id);
+-        usbredirparser_send_cancel_data_packet(dev->parser, aurb->packet_id);
+-        usbredirparser_do_write(dev->parser);
++    if (usbredir_is_cancelled(dev, id)) {
++        return NULL;
++    }
+ 
+-        /* Mark it as dead */
+-        aurb->packet = NULL;
+-        break;
++    p = usb_ep_find_packet_by_id(&dev->dev,
++                            (ep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT,
++                            ep & 0x0f, id);
++    if (p == NULL) {
++        ERROR("could not find packet with id %"PRIu64"\n", id);
+     }
++    return p;
+ }
+ 
+ static void bufp_alloc(USBRedirDevice *dev,
+@@ -494,24 +493,22 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
+ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+                                       uint8_t ep)
+ {
+-    AsyncURB *aurb = async_alloc(dev, p);
+     struct usb_redir_bulk_packet_header bulk_packet;
+ 
+-    DPRINTF("bulk-out ep %02X len %zd id %u\n", ep,
+-            p->iov.size, aurb->packet_id);
++    DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id);
+ 
+     bulk_packet.endpoint  = ep;
+     bulk_packet.length    = p->iov.size;
+     bulk_packet.stream_id = 0;
+ 
+     if (ep & USB_DIR_IN) {
+-        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
++        usbredirparser_send_bulk_packet(dev->parser, p->id,
+                                         &bulk_packet, NULL, 0);
+     } else {
+         uint8_t buf[p->iov.size];
+         usb_packet_copy(p, buf, p->iov.size);
+         usbredir_log_data(dev, "bulk data out:", buf, p->iov.size);
+-        usbredirparser_send_bulk_packet(dev->parser, aurb->packet_id,
++        usbredirparser_send_bulk_packet(dev->parser, p->id,
+                                         &bulk_packet, buf, p->iov.size);
+     }
+     usbredirparser_do_write(dev->parser);
+@@ -574,19 +571,18 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+         return len;
+     } else {
+         /* Output interrupt endpoint, normal async operation */
+-        AsyncURB *aurb = async_alloc(dev, p);
+         struct usb_redir_interrupt_packet_header interrupt_packet;
+         uint8_t buf[p->iov.size];
+ 
+-        DPRINTF("interrupt-out ep %02X len %zd id %u\n", ep, p->iov.size,
+-                aurb->packet_id);
++        DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep,
++                p->iov.size, p->id);
+ 
+         interrupt_packet.endpoint  = ep;
+         interrupt_packet.length    = p->iov.size;
+ 
+         usb_packet_copy(p, buf, p->iov.size);
+         usbredir_log_data(dev, "interrupt data out:", buf, p->iov.size);
+-        usbredirparser_send_interrupt_packet(dev->parser, aurb->packet_id,
++        usbredirparser_send_interrupt_packet(dev->parser, p->id,
+                                         &interrupt_packet, buf, p->iov.size);
+         usbredirparser_do_write(dev->parser);
+         return USB_RET_ASYNC;
+@@ -640,10 +636,9 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
+                                 int config)
+ {
+     struct usb_redir_set_configuration_header set_config;
+-    AsyncURB *aurb = async_alloc(dev, p);
+     int i;
+ 
+-    DPRINTF("set config %d id %u\n", config, aurb->packet_id);
++    DPRINTF("set config %d id %"PRIu64"\n", config, p->id);
+ 
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         switch (dev->endpoint[i].type) {
+@@ -660,19 +655,16 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
+     }
+ 
+     set_config.configuration = config;
+-    usbredirparser_send_set_configuration(dev->parser, aurb->packet_id,
+-                                          &set_config);
++    usbredirparser_send_set_configuration(dev->parser, p->id, &set_config);
+     usbredirparser_do_write(dev->parser);
+     return USB_RET_ASYNC;
+ }
+ 
+ static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
+ {
+-    AsyncURB *aurb = async_alloc(dev, p);
+-
+-    DPRINTF("get config id %u\n", aurb->packet_id);
++    DPRINTF("get config id %"PRIu64"\n", p->id);
+ 
+-    usbredirparser_send_get_configuration(dev->parser, aurb->packet_id);
++    usbredirparser_send_get_configuration(dev->parser, p->id);
+     usbredirparser_do_write(dev->parser);
+     return USB_RET_ASYNC;
+ }
+@@ -681,11 +673,9 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
+                                    int interface, int alt)
+ {
+     struct usb_redir_set_alt_setting_header set_alt;
+-    AsyncURB *aurb = async_alloc(dev, p);
+     int i;
+ 
+-    DPRINTF("set interface %d alt %d id %u\n", interface, alt,
+-            aurb->packet_id);
++    DPRINTF("set interface %d alt %d id %"PRIu64"\n", interface, alt, p->id);
+ 
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         if (dev->endpoint[i].interface == interface) {
+@@ -705,8 +695,7 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
+ 
+     set_alt.interface = interface;
+     set_alt.alt = alt;
+-    usbredirparser_send_set_alt_setting(dev->parser, aurb->packet_id,
+-                                        &set_alt);
++    usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt);
+     usbredirparser_do_write(dev->parser);
+     return USB_RET_ASYNC;
+ }
+@@ -715,13 +704,11 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
+                                    int interface)
+ {
+     struct usb_redir_get_alt_setting_header get_alt;
+-    AsyncURB *aurb = async_alloc(dev, p);
+ 
+-    DPRINTF("get interface %d id %u\n", interface, aurb->packet_id);
++    DPRINTF("get interface %d id %"PRIu64"\n", interface, p->id);
+ 
+     get_alt.interface = interface;
+-    usbredirparser_send_get_alt_setting(dev->parser, aurb->packet_id,
+-                                        &get_alt);
++    usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt);
+     usbredirparser_do_write(dev->parser);
+     return USB_RET_ASYNC;
+ }
+@@ -731,7 +718,6 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+ {
+     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+     struct usb_redir_control_packet_header control_packet;
+-    AsyncURB *aurb;
+ 
+     /* Special cases for certain standard device requests */
+     switch (request) {
+@@ -749,13 +735,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+         return usbredir_get_interface(dev, p, index);
+     }
+ 
+-    /* "Normal" ctrl requests */
+-    aurb = async_alloc(dev, p);
+-
+-    /* Note request is (bRequestType << 8) | bRequest */
+-    DPRINTF("ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %u\n",
+-            request >> 8, request & 0xff, value, index, length,
+-            aurb->packet_id);
++    /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */
++    DPRINTF(
++        "ctrl-out type 0x%x req 0x%x val 0x%x index %d len %d id %"PRIu64"\n",
++        request >> 8, request & 0xff, value, index, length, p->id);
+ 
+     control_packet.request     = request & 0xFF;
+     control_packet.requesttype = request >> 8;
+@@ -765,11 +748,11 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+     control_packet.length      = length;
+ 
+     if (control_packet.requesttype & USB_DIR_IN) {
+-        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
++        usbredirparser_send_control_packet(dev->parser, p->id,
+                                            &control_packet, NULL, 0);
+     } else {
+         usbredir_log_data(dev, "ctrl data out:", data, length);
+-        usbredirparser_send_control_packet(dev->parser, aurb->packet_id,
++        usbredirparser_send_control_packet(dev->parser, p->id,
+                                            &control_packet, data, length);
+     }
+     usbredirparser_do_write(dev->parser);
+@@ -941,7 +924,7 @@ static int usbredir_initfn(USBDevice *udev)
+     dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
+     dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+ 
+-    QTAILQ_INIT(&dev->asyncq);
++    QTAILQ_INIT(&dev->cancelled);
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         QTAILQ_INIT(&dev->endpoint[i].bufpq);
+     }
+@@ -959,11 +942,12 @@ static int usbredir_initfn(USBDevice *udev)
+ 
+ static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
+ {
+-    AsyncURB *aurb, *next_aurb;
++    Cancelled *c, *next_c;
+     int i;
+ 
+-    QTAILQ_FOREACH_SAFE(aurb, &dev->asyncq, next, next_aurb) {
+-        async_free(dev, aurb);
++    QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) {
++        QTAILQ_REMOVE(&dev->cancelled, c, next);
++        g_free(c);
+     }
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         usbredir_free_bufpq(dev, I2EP(i));
+@@ -1229,33 +1213,28 @@ static void usbredir_configuration_status(void *priv, uint32_t id,
+     struct usb_redir_configuration_status_header *config_status)
+ {
+     USBRedirDevice *dev = priv;
+-    AsyncURB *aurb;
++    USBPacket *p;
+     int len = 0;
+ 
+     DPRINTF("set config status %d config %d id %u\n", config_status->status,
+             config_status->configuration, id);
+ 
+-    aurb = async_find(dev, id);
+-    if (!aurb) {
+-        return;
+-    }
+-    if (aurb->packet) {
++    p = usbredir_find_packet_by_id(dev, 0, id);
++    if (p) {
+         if (dev->dev.setup_buf[0] & USB_DIR_IN) {
+             dev->dev.data_buf[0] = config_status->configuration;
+             len = 1;
+         }
+-        aurb->packet->result =
+-            usbredir_handle_status(dev, config_status->status, len);
+-        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
++        p->result = usbredir_handle_status(dev, config_status->status, len);
++        usb_generic_async_ctrl_complete(&dev->dev, p);
+     }
+-    async_free(dev, aurb);
+ }
+ 
+ static void usbredir_alt_setting_status(void *priv, uint32_t id,
+     struct usb_redir_alt_setting_status_header *alt_setting_status)
+ {
+     USBRedirDevice *dev = priv;
+-    AsyncURB *aurb;
++    USBPacket *p;
+     int len = 0;
+ 
+     DPRINTF("alt status %d intf %d alt %d id: %u\n",
+@@ -1263,20 +1242,16 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id,
+             alt_setting_status->interface,
+             alt_setting_status->alt, id);
+ 
+-    aurb = async_find(dev, id);
+-    if (!aurb) {
+-        return;
+-    }
+-    if (aurb->packet) {
++    p = usbredir_find_packet_by_id(dev, 0, id);
++    if (p) {
+         if (dev->dev.setup_buf[0] & USB_DIR_IN) {
+             dev->dev.data_buf[0] = alt_setting_status->alt;
+             len = 1;
+         }
+-        aurb->packet->result =
++        p->result =
+             usbredir_handle_status(dev, alt_setting_status->status, len);
+-        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
++        usb_generic_async_ctrl_complete(&dev->dev, p);
+     }
+-    async_free(dev, aurb);
+ }
+ 
+ static void usbredir_iso_stream_status(void *priv, uint32_t id,
+@@ -1331,19 +1306,14 @@ static void usbredir_control_packet(void *priv, uint32_t id,
+     uint8_t *data, int data_len)
+ {
+     USBRedirDevice *dev = priv;
++    USBPacket *p;
+     int len = control_packet->length;
+-    AsyncURB *aurb;
+ 
+     DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
+             len, id);
+ 
+-    aurb = async_find(dev, id);
+-    if (!aurb) {
+-        free(data);
+-        return;
+-    }
+-
+-    if (aurb->packet) {
++    p = usbredir_find_packet_by_id(dev, 0, id);
++    if (p) {
+         len = usbredir_handle_status(dev, control_packet->status, len);
+         if (len > 0) {
+             usbredir_log_data(dev, "ctrl data in:", data, data_len);
+@@ -1355,10 +1325,9 @@ static void usbredir_control_packet(void *priv, uint32_t id,
+                 len = USB_RET_STALL;
+             }
+         }
+-        aurb->packet->result = len;
+-        usb_generic_async_ctrl_complete(&dev->dev, aurb->packet);
++        p->result = len;
++        usb_generic_async_ctrl_complete(&dev->dev, p);
+     }
+-    async_free(dev, aurb);
+     free(data);
+ }
+ 
+@@ -1369,33 +1338,27 @@ static void usbredir_bulk_packet(void *priv, uint32_t id,
+     USBRedirDevice *dev = priv;
+     uint8_t ep = bulk_packet->endpoint;
+     int len = bulk_packet->length;
+-    AsyncURB *aurb;
++    USBPacket *p;
+ 
+     DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
+             ep, len, id);
+ 
+-    aurb = async_find(dev, id);
+-    if (!aurb) {
+-        free(data);
+-        return;
+-    }
+-
+-    if (aurb->packet) {
++    p = usbredir_find_packet_by_id(dev, ep, id);
++    if (p) {
+         len = usbredir_handle_status(dev, bulk_packet->status, len);
+         if (len > 0) {
+             usbredir_log_data(dev, "bulk data in:", data, data_len);
+-            if (data_len <= aurb->packet->iov.size) {
+-                usb_packet_copy(aurb->packet, data, data_len);
++            if (data_len <= p->iov.size) {
++                usb_packet_copy(p, data, data_len);
+             } else {
+                 ERROR("bulk buffer too small (%d > %zd)\n", data_len,
+-                      aurb->packet->iov.size);
++                      p->iov.size);
+                 len = USB_RET_STALL;
+             }
+         }
+-        aurb->packet->result = len;
+-        usb_packet_complete(&dev->dev, aurb->packet);
++        p->result = len;
++        usb_packet_complete(&dev->dev, p);
+     }
+-    async_free(dev, aurb);
+     free(data);
+ }
+ 
+@@ -1453,17 +1416,12 @@ static void usbredir_interrupt_packet(void *priv, uint32_t id,
+     } else {
+         int len = interrupt_packet->length;
+ 
+-        AsyncURB *aurb = async_find(dev, id);
+-        if (!aurb) {
+-            return;
+-        }
+-
+-        if (aurb->packet) {
+-            aurb->packet->result = usbredir_handle_status(dev,
++        USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
++        if (p) {
++            p->result = usbredir_handle_status(dev,
+                                                interrupt_packet->status, len);
+-            usb_packet_complete(&dev->dev, aurb->packet);
++            usb_packet_complete(&dev->dev, p);
+         }
+-        async_free(dev, aurb);
+     }
+ }
+ 
+-- 
+1.7.12
+
diff --git a/0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch b/0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch
new file mode 100644
index 0000000..20a9db2
--- /dev/null
+++ b/0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch
@@ -0,0 +1,34 @@
+From bd0bc4416beb7ecef0baf2424250c07b9ef15fb6 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 28 Aug 2012 11:33:47 +0200
+Subject: [PATCH 323/366] usb-redir: Return babble when getting more bulk data
+ then requested
+
+Babble is the appropriate error in this case (rather then signalling a stall).
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 1ce994c..60b8f3e 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -1351,9 +1351,9 @@ static void usbredir_bulk_packet(void *priv, uint32_t id,
+             if (data_len <= p->iov.size) {
+                 usb_packet_copy(p, data, data_len);
+             } else {
+-                ERROR("bulk buffer too small (%d > %zd)\n", data_len,
+-                      p->iov.size);
+-                len = USB_RET_STALL;
++                ERROR("bulk got more data then requested (%d > %zd)\n",
++                      data_len, p->iov.size);
++                len = USB_RET_BABBLE;
+             }
+         }
+         p->result = len;
+-- 
+1.7.12
+
diff --git a/0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch b/0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch
new file mode 100644
index 0000000..e6296ec
--- /dev/null
+++ b/0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch
@@ -0,0 +1,232 @@
+From 5ea5581f88d2e4bba876c2ee2544cf3641a64eca Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Fri, 31 Aug 2012 13:41:38 +0200
+Subject: [PATCH 324/366] usb-redir: Convert to new libusbredirparser 0.5 API
+
+This gives us support for 64 bit ids which is needed for using XHCI with
+the new hcd generated ids.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ configure         |  2 +-
+ hw/usb/redirect.c | 62 +++++++++++++++++++++++++++----------------------------
+ 2 files changed, 32 insertions(+), 32 deletions(-)
+
+diff --git a/configure b/configure
+index 0bfef84..25c406f 100755
+--- a/configure
++++ b/configure
+@@ -2765,7 +2765,7 @@ fi
+ 
+ # check for usbredirparser for usb network redirection support
+ if test "$usb_redir" != "no" ; then
+-    if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then
++    if $pkg_config --atleast-version=0.5 libusbredirparser >/dev/null 2>&1 ; then
+         usb_redir="yes"
+         usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
+         usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 60b8f3e..789ef51 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -107,27 +107,27 @@ static void usbredir_interface_info(void *priv,
+     struct usb_redir_interface_info_header *interface_info);
+ static void usbredir_ep_info(void *priv,
+     struct usb_redir_ep_info_header *ep_info);
+-static void usbredir_configuration_status(void *priv, uint32_t id,
++static void usbredir_configuration_status(void *priv, uint64_t id,
+     struct usb_redir_configuration_status_header *configuration_status);
+-static void usbredir_alt_setting_status(void *priv, uint32_t id,
++static void usbredir_alt_setting_status(void *priv, uint64_t id,
+     struct usb_redir_alt_setting_status_header *alt_setting_status);
+-static void usbredir_iso_stream_status(void *priv, uint32_t id,
++static void usbredir_iso_stream_status(void *priv, uint64_t id,
+     struct usb_redir_iso_stream_status_header *iso_stream_status);
+-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
++static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
+     struct usb_redir_interrupt_receiving_status_header
+     *interrupt_receiving_status);
+-static void usbredir_bulk_streams_status(void *priv, uint32_t id,
++static void usbredir_bulk_streams_status(void *priv, uint64_t id,
+     struct usb_redir_bulk_streams_status_header *bulk_streams_status);
+-static void usbredir_control_packet(void *priv, uint32_t id,
++static void usbredir_control_packet(void *priv, uint64_t id,
+     struct usb_redir_control_packet_header *control_packet,
+     uint8_t *data, int data_len);
+-static void usbredir_bulk_packet(void *priv, uint32_t id,
++static void usbredir_bulk_packet(void *priv, uint64_t id,
+     struct usb_redir_bulk_packet_header *bulk_packet,
+     uint8_t *data, int data_len);
+-static void usbredir_iso_packet(void *priv, uint32_t id,
++static void usbredir_iso_packet(void *priv, uint64_t id,
+     struct usb_redir_iso_packet_header *iso_packet,
+     uint8_t *data, int data_len);
+-static void usbredir_interrupt_packet(void *priv, uint32_t id,
++static void usbredir_interrupt_packet(void *priv, uint64_t id,
+     struct usb_redir_interrupt_packet_header *interrupt_header,
+     uint8_t *data, int data_len);
+ 
+@@ -815,6 +815,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
+ 
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
++    usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+     usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
+     usbredirparser_do_write(dev->parser);
+ }
+@@ -1209,15 +1210,15 @@ static void usbredir_ep_info(void *priv,
+     }
+ }
+ 
+-static void usbredir_configuration_status(void *priv, uint32_t id,
++static void usbredir_configuration_status(void *priv, uint64_t id,
+     struct usb_redir_configuration_status_header *config_status)
+ {
+     USBRedirDevice *dev = priv;
+     USBPacket *p;
+     int len = 0;
+ 
+-    DPRINTF("set config status %d config %d id %u\n", config_status->status,
+-            config_status->configuration, id);
++    DPRINTF("set config status %d config %d id %"PRIu64"\n",
++            config_status->status, config_status->configuration, id);
+ 
+     p = usbredir_find_packet_by_id(dev, 0, id);
+     if (p) {
+@@ -1230,16 +1231,15 @@ static void usbredir_configuration_status(void *priv, uint32_t id,
+     }
+ }
+ 
+-static void usbredir_alt_setting_status(void *priv, uint32_t id,
++static void usbredir_alt_setting_status(void *priv, uint64_t id,
+     struct usb_redir_alt_setting_status_header *alt_setting_status)
+ {
+     USBRedirDevice *dev = priv;
+     USBPacket *p;
+     int len = 0;
+ 
+-    DPRINTF("alt status %d intf %d alt %d id: %u\n",
+-            alt_setting_status->status,
+-            alt_setting_status->interface,
++    DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n",
++            alt_setting_status->status, alt_setting_status->interface,
+             alt_setting_status->alt, id);
+ 
+     p = usbredir_find_packet_by_id(dev, 0, id);
+@@ -1254,13 +1254,13 @@ static void usbredir_alt_setting_status(void *priv, uint32_t id,
+     }
+ }
+ 
+-static void usbredir_iso_stream_status(void *priv, uint32_t id,
++static void usbredir_iso_stream_status(void *priv, uint64_t id,
+     struct usb_redir_iso_stream_status_header *iso_stream_status)
+ {
+     USBRedirDevice *dev = priv;
+     uint8_t ep = iso_stream_status->endpoint;
+ 
+-    DPRINTF("iso status %d ep %02X id %u\n", iso_stream_status->status,
++    DPRINTF("iso status %d ep %02X id %"PRIu64"\n", iso_stream_status->status,
+             ep, id);
+ 
+     if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].iso_started) {
+@@ -1274,14 +1274,14 @@ static void usbredir_iso_stream_status(void *priv, uint32_t id,
+     }
+ }
+ 
+-static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
++static void usbredir_interrupt_receiving_status(void *priv, uint64_t id,
+     struct usb_redir_interrupt_receiving_status_header
+     *interrupt_receiving_status)
+ {
+     USBRedirDevice *dev = priv;
+     uint8_t ep = interrupt_receiving_status->endpoint;
+ 
+-    DPRINTF("interrupt recv status %d ep %02X id %u\n",
++    DPRINTF("interrupt recv status %d ep %02X id %"PRIu64"\n",
+             interrupt_receiving_status->status, ep, id);
+ 
+     if (!dev->dev.attached || !dev->endpoint[EP2I(ep)].interrupt_started) {
+@@ -1296,12 +1296,12 @@ static void usbredir_interrupt_receiving_status(void *priv, uint32_t id,
+     }
+ }
+ 
+-static void usbredir_bulk_streams_status(void *priv, uint32_t id,
++static void usbredir_bulk_streams_status(void *priv, uint64_t id,
+     struct usb_redir_bulk_streams_status_header *bulk_streams_status)
+ {
+ }
+ 
+-static void usbredir_control_packet(void *priv, uint32_t id,
++static void usbredir_control_packet(void *priv, uint64_t id,
+     struct usb_redir_control_packet_header *control_packet,
+     uint8_t *data, int data_len)
+ {
+@@ -1309,7 +1309,7 @@ static void usbredir_control_packet(void *priv, uint32_t id,
+     USBPacket *p;
+     int len = control_packet->length;
+ 
+-    DPRINTF("ctrl-in status %d len %d id %u\n", control_packet->status,
++    DPRINTF("ctrl-in status %d len %d id %"PRIu64"\n", control_packet->status,
+             len, id);
+ 
+     p = usbredir_find_packet_by_id(dev, 0, id);
+@@ -1331,7 +1331,7 @@ static void usbredir_control_packet(void *priv, uint32_t id,
+     free(data);
+ }
+ 
+-static void usbredir_bulk_packet(void *priv, uint32_t id,
++static void usbredir_bulk_packet(void *priv, uint64_t id,
+     struct usb_redir_bulk_packet_header *bulk_packet,
+     uint8_t *data, int data_len)
+ {
+@@ -1340,8 +1340,8 @@ static void usbredir_bulk_packet(void *priv, uint32_t id,
+     int len = bulk_packet->length;
+     USBPacket *p;
+ 
+-    DPRINTF("bulk-in status %d ep %02X len %d id %u\n", bulk_packet->status,
+-            ep, len, id);
++    DPRINTF("bulk-in status %d ep %02X len %d id %"PRIu64"\n",
++            bulk_packet->status, ep, len, id);
+ 
+     p = usbredir_find_packet_by_id(dev, ep, id);
+     if (p) {
+@@ -1362,15 +1362,15 @@ static void usbredir_bulk_packet(void *priv, uint32_t id,
+     free(data);
+ }
+ 
+-static void usbredir_iso_packet(void *priv, uint32_t id,
++static void usbredir_iso_packet(void *priv, uint64_t id,
+     struct usb_redir_iso_packet_header *iso_packet,
+     uint8_t *data, int data_len)
+ {
+     USBRedirDevice *dev = priv;
+     uint8_t ep = iso_packet->endpoint;
+ 
+-    DPRINTF2("iso-in status %d ep %02X len %d id %u\n", iso_packet->status, ep,
+-             data_len, id);
++    DPRINTF2("iso-in status %d ep %02X len %d id %"PRIu64"\n",
++             iso_packet->status, ep, data_len, id);
+ 
+     if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_ISOC) {
+         ERROR("received iso packet for non iso endpoint %02X\n", ep);
+@@ -1388,14 +1388,14 @@ static void usbredir_iso_packet(void *priv, uint32_t id,
+     bufp_alloc(dev, data, data_len, iso_packet->status, ep);
+ }
+ 
+-static void usbredir_interrupt_packet(void *priv, uint32_t id,
++static void usbredir_interrupt_packet(void *priv, uint64_t id,
+     struct usb_redir_interrupt_packet_header *interrupt_packet,
+     uint8_t *data, int data_len)
+ {
+     USBRedirDevice *dev = priv;
+     uint8_t ep = interrupt_packet->endpoint;
+ 
+-    DPRINTF("interrupt-in status %d ep %02X len %d id %u\n",
++    DPRINTF("interrupt-in status %d ep %02X len %d id %"PRIu64"\n",
+             interrupt_packet->status, ep, data_len, id);
+ 
+     if (dev->endpoint[EP2I(ep)].type != USB_ENDPOINT_XFER_INT) {
+-- 
+1.7.12
+
diff --git a/0325-usb-redir-Set-ep-max_packet_size-if-available.patch b/0325-usb-redir-Set-ep-max_packet_size-if-available.patch
new file mode 100644
index 0000000..d9e15a5
--- /dev/null
+++ b/0325-usb-redir-Set-ep-max_packet_size-if-available.patch
@@ -0,0 +1,39 @@
+From 7c936f9e6b07f4468f49886b4e785e4120a08e11 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 11:49:07 +0200
+Subject: [PATCH 325/366] usb-redir: Set ep max_packet_size if available
+
+This is needed for usb-redir to work properly with the xhci emulation.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 789ef51..ac8bfd2 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -815,6 +815,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
+ 
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
++    usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+     usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
+     usbredirparser_do_write(dev->parser);
+@@ -1207,6 +1208,10 @@ static void usbredir_ep_info(void *priv,
+                             i & 0x0f);
+         usb_ep->type = dev->endpoint[i].type;
+         usb_ep->ifnum = dev->endpoint[i].interface;
++        if (usbredirparser_peer_has_cap(dev->parser,
++                                     usb_redir_cap_ep_info_max_packet_size)) {
++            usb_ep->max_packet_size = ep_info->max_packet_size[i];
++        }
+     }
+ }
+ 
+-- 
+1.7.12
+
diff --git a/0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch b/0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch
new file mode 100644
index 0000000..d4cde52
--- /dev/null
+++ b/0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch
@@ -0,0 +1,59 @@
+From 4ba71f5871be4d92f36097bc431fc303e1e42a7a Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 11:53:28 +0200
+Subject: [PATCH 326/366] usb-redir: Add a usbredir_reject_device helper
+ function
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 21 +++++++++++----------
+ 1 file changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index ac8bfd2..357f307 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -821,16 +821,21 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
+     usbredirparser_do_write(dev->parser);
+ }
+ 
++static void usbredir_reject_device(USBRedirDevice *dev)
++{
++    usbredir_device_disconnect(dev);
++    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
++        usbredirparser_send_filter_reject(dev->parser);
++        usbredirparser_do_write(dev->parser);
++    }
++}
++
+ static void usbredir_do_attach(void *opaque)
+ {
+     USBRedirDevice *dev = opaque;
+ 
+     if (usb_device_attach(&dev->dev) != 0) {
+-        usbredir_device_disconnect(dev);
+-        if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+-            usbredirparser_send_filter_reject(dev->parser);
+-            usbredirparser_do_write(dev->parser);
+-        }
++        usbredir_reject_device(dev);
+     }
+ }
+ 
+@@ -1013,11 +1018,7 @@ static int usbredir_check_filter(USBRedirDevice *dev)
+     return 0;
+ 
+ error:
+-    usbredir_device_disconnect(dev);
+-    if (usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_filter)) {
+-        usbredirparser_send_filter_reject(dev->parser);
+-        usbredirparser_do_write(dev->parser);
+-    }
++    usbredir_reject_device(dev);
+     return -1;
+ }
+ 
+-- 
+1.7.12
+
diff --git a/0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch b/0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch
new file mode 100644
index 0000000..f3ea67a
--- /dev/null
+++ b/0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch
@@ -0,0 +1,43 @@
+From af1ecda005c043dd8186725ae7a93ea66ebfe96f Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 12:04:49 +0200
+Subject: [PATCH 327/366] usb-redir: Ensure our peer has the necessary caps
+ when redirecting to XHCI
+
+In order for redirection to work properly when redirecting to an emulated
+XHCI controller, the usb-redir-host must support both
+usb_redir_cap_ep_info_max_packet_size and usb_redir_cap_64bits_ids,
+reject any devices redirected to an XHCI controller when these are not
+supported.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 357f307..5b0f834 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -834,6 +834,17 @@ static void usbredir_do_attach(void *opaque)
+ {
+     USBRedirDevice *dev = opaque;
+ 
++    /* In order to work properly with XHCI controllers we need these caps */
++    if ((dev->dev.port->speedmask & USB_SPEED_MASK_SUPER) && !(
++        usbredirparser_peer_has_cap(dev->parser,
++                                    usb_redir_cap_ep_info_max_packet_size) &&
++        usbredirparser_peer_has_cap(dev->parser,
++                                    usb_redir_cap_64bits_ids))) {
++        ERROR("usb-redir-host lacks capabilities needed for use with XHCI\n");
++        usbredir_reject_device(dev);
++        return;
++    }
++
+     if (usb_device_attach(&dev->dev) != 0) {
+         usbredir_reject_device(dev);
+     }
+-- 
+1.7.12
+
diff --git a/0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch b/0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch
new file mode 100644
index 0000000..48de611
--- /dev/null
+++ b/0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch
@@ -0,0 +1,28 @@
+From 30b6793253b39f347e1f74791118403451b9886c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Mon, 3 Sep 2012 13:44:04 +0200
+Subject: [PATCH 328/366] usb-redir: Enable pipelining for bulk endpoints
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/redirect.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 5b0f834..9cbcddb 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -1224,6 +1224,9 @@ static void usbredir_ep_info(void *priv,
+                                      usb_redir_cap_ep_info_max_packet_size)) {
+             usb_ep->max_packet_size = ep_info->max_packet_size[i];
+         }
++        if (ep_info->type[i] == usb_redir_type_bulk) {
++            usb_ep->pipeline = true;
++        }
+     }
+ }
+ 
+-- 
+1.7.12
+
diff --git a/0329-Better-name-usb-braille-device.patch b/0329-Better-name-usb-braille-device.patch
new file mode 100644
index 0000000..b9cf4e6
--- /dev/null
+++ b/0329-Better-name-usb-braille-device.patch
@@ -0,0 +1,30 @@
+From f83e26f01f1489af484f69bdeb2d01d4896de6ec Mon Sep 17 00:00:00 2001
+From: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Date: Thu, 23 Aug 2012 09:59:27 +0200
+Subject: [PATCH 329/366] Better name usb braille device
+
+Windows users need to know that they have to use the Baum driver to make
+the qemu braille device work.
+
+Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/dev-serial.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
+index 8fc9bdd..0ddfab6 100644
+--- a/hw/usb/dev-serial.c
++++ b/hw/usb/dev-serial.c
+@@ -113,7 +113,7 @@ enum {
+ static const USBDescStrings desc_strings = {
+     [STR_MANUFACTURER]    = "QEMU",
+     [STR_PRODUCT_SERIAL]  = "QEMU USB SERIAL",
+-    [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
++    [STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE",
+     [STR_SERIALNUMBER]    = "1",
+ };
+ 
+-- 
+1.7.12
+
diff --git a/0330-usb-audio-fix-usb-version.patch b/0330-usb-audio-fix-usb-version.patch
new file mode 100644
index 0000000..c79c18f
--- /dev/null
+++ b/0330-usb-audio-fix-usb-version.patch
@@ -0,0 +1,29 @@
+From 77824f2b7aba75a3f31a996634dc836fe26ee2c5 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 28 Aug 2012 16:43:34 +0200
+Subject: [PATCH 330/366] usb-audio: fix usb version
+
+usb-audio is a full speed (1.1) device,
+but bcdUSB claims it is usb 2.0.  Fix it.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/dev-audio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
+index 79b75fb..2594c78 100644
+--- a/hw/usb/dev-audio.c
++++ b/hw/usb/dev-audio.c
+@@ -217,7 +217,7 @@ static const USBDescIface desc_iface[] = {
+ };
+ 
+ static const USBDescDevice desc_device = {
+-    .bcdUSB                        = 0x0200,
++    .bcdUSB                        = 0x0100,
+     .bMaxPacketSize0               = 64,
+     .bNumConfigurations            = 1,
+     .confs = (USBDescConfig[]) {
+-- 
+1.7.12
+
diff --git a/0331-xhci-rip-out-background-transfer-code.patch b/0331-xhci-rip-out-background-transfer-code.patch
new file mode 100644
index 0000000..4ccdfd5
--- /dev/null
+++ b/0331-xhci-rip-out-background-transfer-code.patch
@@ -0,0 +1,324 @@
+From f019cdb4b17a0615e897ddac37f2ffa8866a4979 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 17 Aug 2012 14:05:21 +0200
+Subject: [PATCH 331/366] xhci: rip out background transfer code
+
+original xhci code (the one which used libusb directly) used to use
+'background transfers' for iso streams.  In upstream qemu the iso
+stream buffering is handled by usb-host & usb-redir, so we will
+never ever need this.  It has been left in as reference, but is dead
+code anyway.  Rip it out.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 223 +-----------------------------------------------------
+ 1 file changed, 4 insertions(+), 219 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 3eb27fa..c0a2476 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -45,8 +45,6 @@
+ #define MAXPORTS (USB2_PORTS+USB3_PORTS)
+ 
+ #define TD_QUEUE 24
+-#define BG_XFERS 8
+-#define BG_PKTS 8
+ 
+ /* Very pessimistic, let's hope it's enough for all cases */
+ #define EV_QUEUE (((3*TD_QUEUE)+16)*MAXSLOTS)
+@@ -311,13 +309,11 @@ typedef struct XHCITransfer {
+     bool running_retry;
+     bool cancelled;
+     bool complete;
+-    bool backgrounded;
+     unsigned int iso_pkts;
+     unsigned int slotid;
+     unsigned int epid;
+     bool in_xfer;
+     bool iso_xfer;
+-    bool bg_xfer;
+ 
+     unsigned int trb_count;
+     unsigned int trb_alloced;
+@@ -340,14 +336,9 @@ typedef struct XHCIEPContext {
+     unsigned int comp_xfer;
+     XHCITransfer transfers[TD_QUEUE];
+     XHCITransfer *retry;
+-    bool bg_running;
+-    bool bg_updating;
+-    unsigned int next_bg;
+-    XHCITransfer bg_transfers[BG_XFERS];
+     EPType type;
+     dma_addr_t pctx;
+     unsigned int max_psize;
+-    bool has_bg;
+     uint32_t state;
+ } XHCIEPContext;
+ 
+@@ -866,10 +857,6 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
+     epctx->pctx = pctx;
+     epctx->max_psize = ctx[1]>>16;
+     epctx->max_psize *= 1+((ctx[1]>>8)&0xff);
+-    epctx->has_bg = false;
+-    if (epctx->type == ET_ISO_IN) {
+-        epctx->has_bg = true;
+-    }
+     DPRINTF("xhci: endpoint %d.%d max transaction (burst) size is %d\n",
+             epid/2, epid%2, epctx->max_psize);
+     for (i = 0; i < ARRAY_SIZE(epctx->transfers); i++) {
+@@ -916,9 +903,6 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
+             t->running_retry = 0;
+             epctx->retry = NULL;
+         }
+-        if (t->backgrounded) {
+-            t->backgrounded = 0;
+-        }
+         if (t->trbs) {
+             g_free(t->trbs);
+         }
+@@ -932,25 +916,6 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
+         t->data_length = t->data_alloced = 0;
+         xferi = (xferi + 1) % TD_QUEUE;
+     }
+-    if (epctx->has_bg) {
+-        xferi = epctx->next_bg;
+-        for (i = 0; i < BG_XFERS; i++) {
+-            XHCITransfer *t = &epctx->bg_transfers[xferi];
+-            if (t->running_async) {
+-                usb_cancel_packet(&t->packet);
+-                t->running_async = 0;
+-                t->cancelled = 1;
+-                DPRINTF("xhci: cancelling bg transfer %d, waiting for it to complete...\n", i);
+-                killed++;
+-            }
+-            if (t->data) {
+-                g_free(t->data);
+-            }
+-
+-            t->data = NULL;
+-            xferi = (xferi + 1) % BG_XFERS;
+-        }
+-    }
+     return killed;
+ }
+ 
+@@ -1231,160 +1196,6 @@ static void xhci_stall_ep(XHCITransfer *xfer)
+ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
+                        XHCIEPContext *epctx);
+ 
+-static void xhci_bg_update(XHCIState *xhci, XHCIEPContext *epctx)
+-{
+-    if (epctx->bg_updating) {
+-        return;
+-    }
+-    DPRINTF("xhci_bg_update(%p, %p)\n", xhci, epctx);
+-    assert(epctx->has_bg);
+-    DPRINTF("xhci: fg=%d bg=%d\n", epctx->comp_xfer, epctx->next_bg);
+-    epctx->bg_updating = 1;
+-    while (epctx->transfers[epctx->comp_xfer].backgrounded &&
+-           epctx->bg_transfers[epctx->next_bg].complete) {
+-        XHCITransfer *fg = &epctx->transfers[epctx->comp_xfer];
+-        XHCITransfer *bg = &epctx->bg_transfers[epctx->next_bg];
+-#if 0
+-        DPRINTF("xhci: completing fg %d from bg %d.%d (stat: %d)\n",
+-                epctx->comp_xfer, epctx->next_bg, bg->cur_pkt,
+-                bg->usbxfer->iso_packet_desc[bg->cur_pkt].status
+-               );
+-#endif
+-        assert(epctx->type == ET_ISO_IN);
+-        assert(bg->iso_xfer);
+-        assert(bg->in_xfer);
+-        uint8_t *p = bg->data + bg->cur_pkt * bg->pktsize;
+-#if 0
+-        int len = bg->usbxfer->iso_packet_desc[bg->cur_pkt].actual_length;
+-        fg->status = libusb_to_ccode(bg->usbxfer->iso_packet_desc[bg->cur_pkt].status);
+-#else
+-        int len = 0;
+-        FIXME();
+-#endif
+-        fg->complete = 1;
+-        fg->backgrounded = 0;
+-
+-        if (fg->status == CC_STALL_ERROR) {
+-            xhci_stall_ep(fg);
+-        }
+-
+-        xhci_xfer_data(fg, p, len, 1, 0, 1);
+-
+-        epctx->comp_xfer++;
+-        if (epctx->comp_xfer == TD_QUEUE) {
+-            epctx->comp_xfer = 0;
+-        }
+-        DPRINTF("next fg xfer: %d\n", epctx->comp_xfer);
+-        bg->cur_pkt++;
+-        if (bg->cur_pkt == bg->pkts) {
+-            bg->complete = 0;
+-            if (xhci_submit(xhci, bg, epctx) < 0) {
+-                fprintf(stderr, "xhci: bg resubmit failed\n");
+-            }
+-            epctx->next_bg++;
+-            if (epctx->next_bg == BG_XFERS) {
+-                epctx->next_bg = 0;
+-            }
+-            DPRINTF("next bg xfer: %d\n", epctx->next_bg);
+-
+-        xhci_kick_ep(xhci, fg->slotid, fg->epid);
+-        }
+-    }
+-    epctx->bg_updating = 0;
+-}
+-
+-#if 0
+-static void xhci_xfer_cb(struct libusb_transfer *transfer)
+-{
+-    XHCIState *xhci;
+-    XHCITransfer *xfer;
+-
+-    xfer = (XHCITransfer *)transfer->user_data;
+-    xhci = xfer->xhci;
+-
+-    DPRINTF("xhci_xfer_cb(slot=%d, ep=%d, status=%d)\n", xfer->slotid,
+-            xfer->epid, transfer->status);
+-
+-    assert(xfer->slotid >= 1 && xfer->slotid <= MAXSLOTS);
+-    assert(xfer->epid >= 1 && xfer->epid <= 31);
+-
+-    if (xfer->cancelled) {
+-        DPRINTF("xhci: transfer cancelled, not reporting anything\n");
+-        xfer->running = 0;
+-        return;
+-    }
+-
+-    XHCIEPContext *epctx;
+-    XHCISlot *slot;
+-    slot = &xhci->slots[xfer->slotid-1];
+-    assert(slot->eps[xfer->epid-1]);
+-    epctx = slot->eps[xfer->epid-1];
+-
+-    if (xfer->bg_xfer) {
+-        DPRINTF("xhci: background transfer, updating\n");
+-        xfer->complete = 1;
+-        xfer->running = 0;
+-        xhci_bg_update(xhci, epctx);
+-        return;
+-    }
+-
+-    if (xfer->iso_xfer) {
+-        transfer->status = transfer->iso_packet_desc[0].status;
+-        transfer->actual_length = transfer->iso_packet_desc[0].actual_length;
+-    }
+-
+-    xfer->status = libusb_to_ccode(transfer->status);
+-
+-    xfer->complete = 1;
+-    xfer->running = 0;
+-
+-    if (transfer->status == LIBUSB_TRANSFER_STALL)
+-        xhci_stall_ep(xhci, epctx, xfer);
+-
+-    DPRINTF("xhci: transfer actual length = %d\n", transfer->actual_length);
+-
+-    if (xfer->in_xfer) {
+-        if (xfer->epid == 1) {
+-            xhci_xfer_data(xhci, xfer, xfer->data + 8,
+-                           transfer->actual_length, 1, 0, 1);
+-        } else {
+-            xhci_xfer_data(xhci, xfer, xfer->data,
+-                           transfer->actual_length, 1, 0, 1);
+-        }
+-    } else {
+-        xhci_xfer_data(xhci, xfer, NULL, transfer->actual_length, 0, 0, 1);
+-    }
+-
+-    xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
+-}
+-
+-static int xhci_hle_control(XHCIState *xhci, XHCITransfer *xfer,
+-                            uint8_t bmRequestType, uint8_t bRequest,
+-                            uint16_t wValue, uint16_t wIndex, uint16_t wLength)
+-{
+-    uint16_t type_req = (bmRequestType << 8) | bRequest;
+-
+-    switch (type_req) {
+-        case 0x0000 | USB_REQ_SET_CONFIGURATION:
+-            DPRINTF("xhci: HLE switch configuration\n");
+-            return xhci_switch_config(xhci, xfer->slotid, wValue) == 0;
+-        case 0x0100 | USB_REQ_SET_INTERFACE:
+-            DPRINTF("xhci: HLE set interface altsetting\n");
+-            return xhci_set_iface_alt(xhci, xfer->slotid, wIndex, wValue) == 0;
+-        case 0x0200 | USB_REQ_CLEAR_FEATURE:
+-            if (wValue == 0) { // endpoint halt
+-                DPRINTF("xhci: HLE clear halt\n");
+-                return xhci_clear_halt(xhci, xfer->slotid, wIndex);
+-            }
+-        case 0x0000 | USB_REQ_SET_ADDRESS:
+-            fprintf(stderr, "xhci: warn: illegal SET_ADDRESS request\n");
+-            return 0;
+-        default:
+-            return 0;
+-    }
+-}
+-#endif
+-
+ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
+ {
+     USBEndpoint *ep;
+@@ -1559,9 +1370,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
+         xfer->data_alloced = xfer->data_length;
+     }
+     if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
+-        if (!xfer->bg_xfer) {
+-            xfer->pkts = 1;
+-        }
++        xfer->pkts = 1;
+     } else {
+         xfer->pkts = 0;
+     }
+@@ -1620,32 +1429,8 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
+ 
+     trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length);
+ 
+-    if (!epctx->has_bg) {
+-        xfer->data_length = length;
+-        xfer->backgrounded = 0;
+-        return xhci_submit(xhci, xfer, epctx);
+-    } else {
+-        if (!epctx->bg_running) {
+-            for (i = 0; i < BG_XFERS; i++) {
+-                XHCITransfer *t = &epctx->bg_transfers[i];
+-                t->xhci = xhci;
+-                t->epid = xfer->epid;
+-                t->slotid = xfer->slotid;
+-                t->pkts = BG_PKTS;
+-                t->pktsize = epctx->max_psize;
+-                t->data_length = t->pkts * t->pktsize;
+-                t->bg_xfer = 1;
+-                if (xhci_submit(xhci, t, epctx) < 0) {
+-                    fprintf(stderr, "xhci: bg submit failed\n");
+-                    return -1;
+-                }
+-            }
+-            epctx->bg_running = 1;
+-        }
+-        xfer->backgrounded = 1;
+-        xhci_bg_update(xhci, epctx);
+-        return 0;
+-    }
++    xfer->data_length = length;
++    return xhci_submit(xhci, xfer, epctx);
+ }
+ 
+ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
+@@ -1695,7 +1480,7 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
+ 
+     while (1) {
+         XHCITransfer *xfer = &epctx->transfers[epctx->next_xfer];
+-        if (xfer->running_async || xfer->running_retry || xfer->backgrounded) {
++        if (xfer->running_async || xfer->running_retry) {
+             break;
+         }
+         length = xhci_ring_chain_length(xhci, &epctx->ring);
+-- 
+1.7.12
+
diff --git a/0332-xhci-drop-buffering.patch b/0332-xhci-drop-buffering.patch
new file mode 100644
index 0000000..1097664
--- /dev/null
+++ b/0332-xhci-drop-buffering.patch
@@ -0,0 +1,372 @@
+From ad14386b3f1ccd445afd30d30a343cb9fcf6c0a7 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 17 Aug 2012 11:04:36 +0200
+Subject: [PATCH 332/366] xhci: drop buffering
+
+This patch splits the xhci_xfer_data function into three.
+The xhci_xfer_data function used to do does two things:
+
+  (1) copy transfer data between guest memory and a temporary buffer.
+  (2) report transfer results to the guest using events.
+
+Now we three functions to handle this:
+
+  (1) xhci_xfer_map creates a scatter list for the transfer and
+      uses that (instead of the temporary buffer) to build a
+      USBPacket.
+  (2) xhci_xfer_unmap undoes the mapping.
+  (3) xhci_xfer_report sends out events.
+
+The patch also fixes reporting of transaction errors which must be
+reported unconditinally, not only in case the guest asks for it
+using the ISP flag.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 183 +++++++++++++++++++++---------------------------------
+ trace-events      |   2 +-
+ 2 files changed, 72 insertions(+), 113 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index c0a2476..c858b6d 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -305,6 +305,7 @@ typedef struct XHCIState XHCIState;
+ typedef struct XHCITransfer {
+     XHCIState *xhci;
+     USBPacket packet;
++    QEMUSGList sgl;
+     bool running_async;
+     bool running_retry;
+     bool cancelled;
+@@ -319,10 +320,6 @@ typedef struct XHCITransfer {
+     unsigned int trb_alloced;
+     XHCITRB *trbs;
+ 
+-    unsigned int data_length;
+-    unsigned int data_alloced;
+-    uint8_t *data;
+-
+     TRBCCode status;
+ 
+     unsigned int pkts;
+@@ -906,14 +903,9 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
+         if (t->trbs) {
+             g_free(t->trbs);
+         }
+-        if (t->data) {
+-            g_free(t->data);
+-        }
+ 
+         t->trbs = NULL;
+-        t->data = NULL;
+         t->trb_count = t->trb_alloced = 0;
+-        t->data_length = t->data_alloced = 0;
+         xferi = (xferi + 1) % TD_QUEUE;
+     }
+     return killed;
+@@ -1072,24 +1064,13 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
+     return CC_SUCCESS;
+ }
+ 
+-static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
+-                          unsigned int length, bool in_xfer, bool out_xfer,
+-                          bool report)
++static int xhci_xfer_map(XHCITransfer *xfer)
+ {
+-    int i;
+-    uint32_t edtla = 0;
+-    unsigned int transferred = 0;
+-    unsigned int left = length;
+-    bool reported = 0;
+-    bool shortpkt = 0;
+-    XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
++    int in_xfer = (xfer->packet.pid == USB_TOKEN_IN);
+     XHCIState *xhci = xfer->xhci;
++    int i;
+ 
+-    DPRINTF("xhci_xfer_data(len=%d, in_xfer=%d, out_xfer=%d, report=%d)\n",
+-            length, in_xfer, out_xfer, report);
+-
+-    assert(!(in_xfer && out_xfer));
+-
++    pci_dma_sglist_init(&xfer->sgl, &xhci->pci_dev, xfer->trb_count);
+     for (i = 0; i < xfer->trb_count; i++) {
+         XHCITRB *trb = &xfer->trbs[i];
+         dma_addr_t addr;
+@@ -1099,54 +1080,70 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
+         case TR_DATA:
+             if ((!(trb->control & TRB_TR_DIR)) != (!in_xfer)) {
+                 fprintf(stderr, "xhci: data direction mismatch for TR_DATA\n");
+-                xhci_die(xhci);
+-                return transferred;
++                goto err;
+             }
+             /* fallthrough */
+         case TR_NORMAL:
+         case TR_ISOCH:
+             addr = xhci_mask64(trb->parameter);
+             chunk = trb->status & 0x1ffff;
++            if (trb->control & TRB_TR_IDT) {
++                if (chunk > 8 || in_xfer) {
++                    fprintf(stderr, "xhci: invalid immediate data TRB\n");
++                    goto err;
++                }
++                qemu_sglist_add(&xfer->sgl, trb->addr, chunk);
++            } else {
++                qemu_sglist_add(&xfer->sgl, addr, chunk);
++            }
++            break;
++        }
++    }
++
++    usb_packet_map(&xfer->packet, &xfer->sgl);
++    return 0;
++
++err:
++    qemu_sglist_destroy(&xfer->sgl);
++    xhci_die(xhci);
++    return -1;
++}
++
++static void xhci_xfer_unmap(XHCITransfer *xfer)
++{
++    usb_packet_unmap(&xfer->packet, &xfer->sgl);
++    qemu_sglist_destroy(&xfer->sgl);
++}
++
++static void xhci_xfer_report(XHCITransfer *xfer)
++{
++    uint32_t edtla = 0;
++    unsigned int left;
++    bool reported = 0;
++    bool shortpkt = 0;
++    XHCIEvent event = {ER_TRANSFER, CC_SUCCESS};
++    XHCIState *xhci = xfer->xhci;
++    int i;
++
++    left = xfer->packet.result < 0 ? 0 : xfer->packet.result;
++
++    for (i = 0; i < xfer->trb_count; i++) {
++        XHCITRB *trb = &xfer->trbs[i];
++        unsigned int chunk = 0;
++
++        switch (TRB_TYPE(*trb)) {
++        case TR_DATA:
++        case TR_NORMAL:
++        case TR_ISOCH:
++            chunk = trb->status & 0x1ffff;
+             if (chunk > left) {
+                 chunk = left;
+-                shortpkt = 1;
+-            }
+-            if (in_xfer || out_xfer) {
+-                if (trb->control & TRB_TR_IDT) {
+-                    uint64_t idata;
+-                    if (chunk > 8 || in_xfer) {
+-                        fprintf(stderr, "xhci: invalid immediate data TRB\n");
+-                        xhci_die(xhci);
+-                        return transferred;
+-                    }
+-                    idata = le64_to_cpu(trb->parameter);
+-                    memcpy(data, &idata, chunk);
+-                } else {
+-                    DPRINTF("xhci_xfer_data: r/w(%d) %d bytes at "
+-                            DMA_ADDR_FMT "\n", in_xfer, chunk, addr);
+-                    if (in_xfer) {
+-                        pci_dma_write(&xhci->pci_dev, addr, data, chunk);
+-                    } else {
+-                        pci_dma_read(&xhci->pci_dev, addr, data, chunk);
+-                    }
+-#ifdef DEBUG_DATA
+-                    unsigned int count = chunk;
+-                    int i;
+-                    if (count > 16) {
+-                        count = 16;
+-                    }
+-                    DPRINTF(" ::");
+-                    for (i = 0; i < count; i++) {
+-                        DPRINTF(" %02x", data[i]);
+-                    }
+-                    DPRINTF("\n");
+-#endif
++                if (xfer->status == CC_SUCCESS) {
++                    shortpkt = 1;
+                 }
+             }
+             left -= chunk;
+-            data += chunk;
+             edtla += chunk;
+-            transferred += chunk;
+             break;
+         case TR_STATUS:
+             reported = 0;
+@@ -1154,8 +1151,9 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
+             break;
+         }
+ 
+-        if (report && !reported && (trb->control & TRB_TR_IOC ||
+-            (shortpkt && (trb->control & TRB_TR_ISP)))) {
++        if (!reported && ((trb->control & TRB_TR_IOC) ||
++                          (shortpkt && (trb->control & TRB_TR_ISP)) ||
++                          (xfer->status != CC_SUCCESS))) {
+             event.slotid = xfer->slotid;
+             event.epid = xfer->epid;
+             event.length = (trb->status & 0x1ffff) - chunk;
+@@ -1175,9 +1173,11 @@ static int xhci_xfer_data(XHCITransfer *xfer, uint8_t *data,
+             }
+             xhci_event(xhci, &event);
+             reported = 1;
++            if (xfer->status != CC_SUCCESS) {
++                return;
++            }
+         }
+     }
+-    return transferred;
+ }
+ 
+ static void xhci_stall_ep(XHCITransfer *xfer)
+@@ -1204,7 +1204,7 @@ static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
+     dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
+     ep = usb_ep_get(dev, dir, xfer->epid >> 1);
+     usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
+-    usb_packet_addbuf(&xfer->packet, xfer->data, xfer->data_length);
++    xhci_xfer_map(xfer);
+     DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
+             xfer->packet.pid, dev->addr, ep->nr);
+     return 0;
+@@ -1230,12 +1230,13 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
+         xfer->running_async = 0;
+         xfer->running_retry = 0;
+         xfer->complete = 1;
++        xhci_xfer_unmap(xfer);
+     }
+ 
+     if (ret >= 0) {
+-        xfer->status = CC_SUCCESS;
+-        xhci_xfer_data(xfer, xfer->data, ret, xfer->in_xfer, 0, 1);
+         trace_usb_xhci_xfer_success(xfer, ret);
++        xfer->status = CC_SUCCESS;
++        xhci_xfer_report(xfer);
+         return 0;
+     }
+ 
+@@ -1244,12 +1245,12 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
+     switch (ret) {
+     case USB_RET_NODEV:
+         xfer->status = CC_USB_TRANSACTION_ERROR;
+-        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
++        xhci_xfer_report(xfer);
+         xhci_stall_ep(xfer);
+         break;
+     case USB_RET_STALL:
+         xfer->status = CC_STALL_ERROR;
+-        xhci_xfer_data(xfer, xfer->data, 0, xfer->in_xfer, 0, 1);
++        xhci_xfer_report(xfer);
+         xhci_stall_ep(xfer);
+         break;
+     default:
+@@ -1279,8 +1280,7 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+     trb_setup = &xfer->trbs[0];
+     trb_status = &xfer->trbs[xfer->trb_count-1];
+ 
+-    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid,
+-                              trb_setup->parameter >> 48);
++    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+ 
+     /* at most one Event Data TRB allowed after STATUS */
+     if (TRB_TYPE(*trb_status) == TR_EVDATA && xfer->trb_count > 2) {
+@@ -1311,18 +1311,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+     bmRequestType = trb_setup->parameter;
+     wLength = trb_setup->parameter >> 48;
+ 
+-    if (xfer->data && xfer->data_alloced < wLength) {
+-        xfer->data_alloced = 0;
+-        g_free(xfer->data);
+-        xfer->data = NULL;
+-    }
+-    if (!xfer->data) {
+-        DPRINTF("xhci: alloc %d bytes data\n", wLength);
+-        xfer->data = g_malloc(wLength+1);
+-        xfer->data_alloced = wLength;
+-    }
+-    xfer->data_length = wLength;
+-
+     port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+     dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
+     if (!dev) {
+@@ -1336,9 +1324,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+ 
+     xhci_setup_packet(xfer, dev);
+     xfer->packet.parameter = trb_setup->parameter;
+-    if (!xfer->in_xfer) {
+-        xhci_xfer_data(xfer, xfer->data, wLength, 0, 1, 0);
+-    }
+ 
+     ret = usb_handle_packet(dev, &xfer->packet);
+ 
+@@ -1359,16 +1344,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
+ 
+     xfer->in_xfer = epctx->type>>2;
+ 
+-    if (xfer->data && xfer->data_alloced < xfer->data_length) {
+-        xfer->data_alloced = 0;
+-        g_free(xfer->data);
+-        xfer->data = NULL;
+-    }
+-    if (!xfer->data && xfer->data_length) {
+-        DPRINTF("xhci: alloc %d bytes data\n", xfer->data_length);
+-        xfer->data = g_malloc(xfer->data_length);
+-        xfer->data_alloced = xfer->data_length;
+-    }
+     if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
+         xfer->pkts = 1;
+     } else {
+@@ -1402,9 +1377,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
+         return -1;
+     }
+ 
+-    if (!xfer->in_xfer) {
+-        xhci_xfer_data(xfer, xfer->data, xfer->data_length, 0, 1, 0);
+-    }
+     ret = usb_handle_packet(dev, &xfer->packet);
+ 
+     xhci_complete_packet(xfer, ret);
+@@ -1416,20 +1388,7 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
+ 
+ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+ {
+-    int i;
+-    unsigned int length = 0;
+-    XHCITRB *trb;
+-
+-    for (i = 0; i < xfer->trb_count; i++) {
+-        trb = &xfer->trbs[i];
+-        if (TRB_TYPE(*trb) == TR_NORMAL || TRB_TYPE(*trb) == TR_ISOCH) {
+-            length += trb->status & 0x1ffff;
+-        }
+-    }
+-
+-    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid, length);
+-
+-    xfer->data_length = length;
++    trace_usb_xhci_xfer_start(xfer, xfer->slotid, xfer->epid);
+     return xhci_submit(xhci, xfer, epctx);
+ }
+ 
+diff --git a/trace-events b/trace-events
+index 10bc04e..c83d65e 100644
+--- a/trace-events
++++ b/trace-events
+@@ -326,7 +326,7 @@ usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+ usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+ usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+ usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+-usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t length) "%p: slotid %d, epid %d, length %d"
++usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d"
+ usb_xhci_xfer_async(void *xfer) "%p"
+ usb_xhci_xfer_nak(void *xfer) "%p"
+ usb_xhci_xfer_retry(void *xfer) "%p"
+-- 
+1.7.12
+
diff --git a/0333-xhci-move-device-lookup-into-xhci_setup_packet.patch b/0333-xhci-move-device-lookup-into-xhci_setup_packet.patch
new file mode 100644
index 0000000..3cde29a
--- /dev/null
+++ b/0333-xhci-move-device-lookup-into-xhci_setup_packet.patch
@@ -0,0 +1,155 @@
+From 76d49f5c5abc30f33f5b9288df68d53dba9e10f0 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 24 Aug 2012 14:21:39 +0200
+Subject: [PATCH 333/366] xhci: move device lookup into xhci_setup_packet
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 74 ++++++++++++++++++++++++++++---------------------------
+ 1 file changed, 38 insertions(+), 36 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index c858b6d..303e1ac 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -1196,13 +1196,38 @@ static void xhci_stall_ep(XHCITransfer *xfer)
+ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer,
+                        XHCIEPContext *epctx);
+ 
+-static int xhci_setup_packet(XHCITransfer *xfer, USBDevice *dev)
++static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
+ {
++    if (!(port->portsc & PORTSC_PED)) {
++        return NULL;
++    }
++    return usb_find_device(&port->port, addr);
++}
++
++static int xhci_setup_packet(XHCITransfer *xfer)
++{
++    XHCIState *xhci = xfer->xhci;
++    XHCIPort *port;
++    USBDevice *dev;
+     USBEndpoint *ep;
+     int dir;
+ 
+     dir = xfer->in_xfer ? USB_TOKEN_IN : USB_TOKEN_OUT;
+-    ep = usb_ep_get(dev, dir, xfer->epid >> 1);
++
++    if (xfer->packet.ep) {
++        ep = xfer->packet.ep;
++        dev = ep->dev;
++    } else {
++        port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
++        dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
++        if (!dev) {
++            fprintf(stderr, "xhci: slot %d port %d has no device\n",
++                    xfer->slotid, xhci->slots[xfer->slotid-1].port);
++            return -1;
++        }
++        ep = usb_ep_get(dev, dir, xfer->epid >> 1);
++    }
++
+     usb_packet_setup(&xfer->packet, dir, ep, xfer->trbs[0].addr);
+     xhci_xfer_map(xfer);
+     DPRINTF("xhci: setup packet pid 0x%x addr %d ep %d\n",
+@@ -1260,21 +1285,11 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
+     return 0;
+ }
+ 
+-static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
+-{
+-    if (!(port->portsc & PORTSC_PED)) {
+-        return NULL;
+-    }
+-    return usb_find_device(&port->port, addr);
+-}
+-
+ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+ {
+     XHCITRB *trb_setup, *trb_status;
+     uint8_t bmRequestType;
+     uint16_t wLength;
+-    XHCIPort *port;
+-    USBDevice *dev;
+     int ret;
+ 
+     trb_setup = &xfer->trbs[0];
+@@ -1311,21 +1326,15 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+     bmRequestType = trb_setup->parameter;
+     wLength = trb_setup->parameter >> 48;
+ 
+-    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+-    dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
+-    if (!dev) {
+-        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
+-                xhci->slots[xfer->slotid-1].port);
+-        return -1;
+-    }
+-
+     xfer->in_xfer = bmRequestType & USB_DIR_IN;
+     xfer->iso_xfer = false;
+ 
+-    xhci_setup_packet(xfer, dev);
++    if (xhci_setup_packet(xfer) < 0) {
++        return -1;
++    }
+     xfer->packet.parameter = trb_setup->parameter;
+ 
+-    ret = usb_handle_packet(dev, &xfer->packet);
++    ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ 
+     xhci_complete_packet(xfer, ret);
+     if (!xfer->running_async && !xfer->running_retry) {
+@@ -1336,8 +1345,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+ 
+ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+ {
+-    XHCIPort *port;
+-    USBDevice *dev;
+     int ret;
+ 
+     DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
+@@ -1350,16 +1357,6 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
+         xfer->pkts = 0;
+     }
+ 
+-    port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1];
+-    dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr);
+-    if (!dev) {
+-        fprintf(stderr, "xhci: slot %d port %d has no device\n", xfer->slotid,
+-                xhci->slots[xfer->slotid-1].port);
+-        return -1;
+-    }
+-
+-    xhci_setup_packet(xfer, dev);
+-
+     switch(epctx->type) {
+     case ET_INTR_OUT:
+     case ET_INTR_IN:
+@@ -1377,7 +1374,10 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
+         return -1;
+     }
+ 
+-    ret = usb_handle_packet(dev, &xfer->packet);
++    if (xhci_setup_packet(xfer) < 0) {
++        return -1;
++    }
++    ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ 
+     xhci_complete_packet(xfer, ret);
+     if (!xfer->running_async && !xfer->running_retry) {
+@@ -1420,7 +1420,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
+ 
+         trace_usb_xhci_xfer_retry(xfer);
+         assert(xfer->running_retry);
+-        xhci_setup_packet(xfer, xfer->packet.ep->dev);
++        if (xhci_setup_packet(xfer) < 0) {
++            return;
++        }
+         result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+         if (result == USB_RET_NAK) {
+             return;
+-- 
+1.7.12
+
diff --git a/0334-xhci-implement-mfindex.patch b/0334-xhci-implement-mfindex.patch
new file mode 100644
index 0000000..9f00140
--- /dev/null
+++ b/0334-xhci-implement-mfindex.patch
@@ -0,0 +1,142 @@
+From 6a1c694cd009fb7e4656e1c9a18756da6f89be14 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 21 Aug 2012 12:32:58 +0200
+Subject: [PATCH 334/366] xhci: implement mfindex
+
+Implement mfindex register and mfindex wrap event.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 46 insertions(+), 7 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 303e1ac..9077cb3 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -380,8 +380,6 @@ struct XHCIState {
+     XHCISlot slots[MAXSLOTS];
+ 
+     /* Runtime Registers */
+-    uint32_t mfindex;
+-    /* note: we only support one interrupter */
+     uint32_t iman;
+     uint32_t imod;
+     uint32_t erstsz;
+@@ -390,6 +388,9 @@ struct XHCIState {
+     uint32_t erdp_low;
+     uint32_t erdp_high;
+ 
++    int64_t mfindex_start;
++    QEMUTimer *mfwrap_timer;
++
+     dma_addr_t er_start;
+     uint32_t er_size;
+     bool er_pcs;
+@@ -410,6 +411,11 @@ typedef struct XHCIEvRingSeg {
+     uint32_t rsvd;
+ } XHCIEvRingSeg;
+ 
++static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
++                         unsigned int epid);
++static void xhci_event(XHCIState *xhci, XHCIEvent *event);
++static void xhci_write_event(XHCIState *xhci, XHCIEvent *event);
++
+ static const char *TRBType_names[] = {
+     [TRB_RESERVED]                     = "TRB_RESERVED",
+     [TR_NORMAL]                        = "TR_NORMAL",
+@@ -462,8 +468,36 @@ static const char *trb_name(XHCITRB *trb)
+                        ARRAY_SIZE(TRBType_names));
+ }
+ 
+-static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+-                         unsigned int epid);
++static uint64_t xhci_mfindex_get(XHCIState *xhci)
++{
++    int64_t now = qemu_get_clock_ns(vm_clock);
++    return (now - xhci->mfindex_start) / 125000;
++}
++
++static void xhci_mfwrap_update(XHCIState *xhci)
++{
++    const uint32_t bits = USBCMD_RS | USBCMD_EWE;
++    uint32_t mfindex, left;
++    int64_t now;
++
++    if ((xhci->usbcmd & bits) == bits) {
++        now = qemu_get_clock_ns(vm_clock);
++        mfindex = ((now - xhci->mfindex_start) / 125000) & 0x3fff;
++        left = 0x4000 - mfindex;
++        qemu_mod_timer(xhci->mfwrap_timer, now + left * 125000);
++    } else {
++        qemu_del_timer(xhci->mfwrap_timer);
++    }
++}
++
++static void xhci_mfwrap_timer(void *opaque)
++{
++    XHCIState *xhci = opaque;
++    XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS };
++
++    xhci_event(xhci, &wrap);
++    xhci_mfwrap_update(xhci);
++}
+ 
+ static inline dma_addr_t xhci_addr64(uint32_t low, uint32_t high)
+ {
+@@ -793,6 +827,7 @@ static void xhci_run(XHCIState *xhci)
+ {
+     trace_usb_xhci_run();
+     xhci->usbsts &= ~USBSTS_HCH;
++    xhci->mfindex_start = qemu_get_clock_ns(vm_clock);
+ }
+ 
+ static void xhci_stop(XHCIState *xhci)
+@@ -2050,7 +2085,6 @@ static void xhci_reset(DeviceState *dev)
+         xhci_update_port(xhci, xhci->ports + i, 0);
+     }
+ 
+-    xhci->mfindex = 0;
+     xhci->iman = 0;
+     xhci->imod = 0;
+     xhci->erstsz = 0;
+@@ -2064,6 +2098,9 @@ static void xhci_reset(DeviceState *dev)
+     xhci->er_full = 0;
+     xhci->ev_buffer_put = 0;
+     xhci->ev_buffer_get = 0;
++
++    xhci->mfindex_start = qemu_get_clock_ns(vm_clock);
++    xhci_mfwrap_update(xhci);
+ }
+ 
+ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+@@ -2266,6 +2303,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+             xhci_stop(xhci);
+         }
+         xhci->usbcmd = val & 0xc0f;
++        xhci_mfwrap_update(xhci);
+         if (val & USBCMD_HCRST) {
+             xhci_reset(&xhci->pci_dev.qdev);
+         }
+@@ -2317,8 +2355,7 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+ 
+     switch (reg) {
+     case 0x00: /* MFINDEX */
+-        fprintf(stderr, "xhci_runtime_read: MFINDEX not yet implemented\n");
+-        ret = xhci->mfindex;
++        ret = xhci_mfindex_get(xhci) & 0x3fff;
+         break;
+     case 0x20: /* IMAN */
+         ret = xhci->iman;
+@@ -2618,6 +2655,8 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
+ 
+     usb_xhci_init(xhci, &dev->qdev);
+ 
++    xhci->mfwrap_timer = qemu_new_timer_ns(vm_clock, xhci_mfwrap_timer, xhci);
++
+     xhci->irq = xhci->pci_dev.irq[0];
+ 
+     memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci,
+-- 
+1.7.12
+
diff --git a/0335-xhci-iso-xfer-support.patch b/0335-xhci-iso-xfer-support.patch
new file mode 100644
index 0000000..ae88244
--- /dev/null
+++ b/0335-xhci-iso-xfer-support.patch
@@ -0,0 +1,238 @@
+From 9f7a9361a816479ca109e1747e45d40135fad3b0 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 24 Aug 2012 14:13:08 +0200
+Subject: [PATCH 335/366] xhci: iso xfer support
+
+Add support for iso transfers.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 101 insertions(+), 16 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 9077cb3..3c61bb8 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -325,9 +325,15 @@ typedef struct XHCITransfer {
+     unsigned int pkts;
+     unsigned int pktsize;
+     unsigned int cur_pkt;
++
++    uint64_t mfindex_kick;
+ } XHCITransfer;
+ 
+ typedef struct XHCIEPContext {
++    XHCIState *xhci;
++    unsigned int slotid;
++    unsigned int epid;
++
+     XHCIRing ring;
+     unsigned int next_xfer;
+     unsigned int comp_xfer;
+@@ -337,6 +343,11 @@ typedef struct XHCIEPContext {
+     dma_addr_t pctx;
+     unsigned int max_psize;
+     uint32_t state;
++
++    /* iso xfer scheduling */
++    unsigned int interval;
++    int64_t mfindex_last;
++    QEMUTimer *kick_timer;
+ } XHCIEPContext;
+ 
+ typedef struct XHCISlot {
+@@ -856,6 +867,12 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
+     epctx->state = state;
+ }
+ 
++static void xhci_ep_kick_timer(void *opaque)
++{
++    XHCIEPContext *epctx = opaque;
++    xhci_kick_ep(epctx->xhci, epctx->slotid, epctx->epid);
++}
++
+ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
+                                unsigned int epid, dma_addr_t pctx,
+                                uint32_t *ctx)
+@@ -877,6 +894,9 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
+ 
+     epctx = g_malloc(sizeof(XHCIEPContext));
+     memset(epctx, 0, sizeof(XHCIEPContext));
++    epctx->xhci = xhci;
++    epctx->slotid = slotid;
++    epctx->epid = epid;
+ 
+     slot->eps[epid-1] = epctx;
+ 
+@@ -895,6 +915,10 @@ static TRBCCode xhci_enable_ep(XHCIState *xhci, unsigned int slotid,
+         usb_packet_init(&epctx->transfers[i].packet);
+     }
+ 
++    epctx->interval = 1 << (ctx[0] >> 16) & 0xff;
++    epctx->mfindex_last = 0;
++    epctx->kick_timer = qemu_new_timer_ns(vm_clock, xhci_ep_kick_timer, epctx);
++
+     epctx->state = EP_RUNNING;
+     ctx[0] &= ~EP_STATE_MASK;
+     ctx[0] |= EP_RUNNING;
+@@ -934,6 +958,7 @@ static int xhci_ep_nuke_xfers(XHCIState *xhci, unsigned int slotid,
+         if (t->running_retry) {
+             t->running_retry = 0;
+             epctx->retry = NULL;
++            qemu_del_timer(epctx->kick_timer);
+         }
+         if (t->trbs) {
+             g_free(t->trbs);
+@@ -969,6 +994,7 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
+ 
+     xhci_set_ep_state(xhci, epctx, EP_DISABLED);
+ 
++    qemu_free_timer(epctx->kick_timer);
+     g_free(epctx);
+     slot->eps[epid-1] = NULL;
+ 
+@@ -1378,29 +1404,70 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+     return 0;
+ }
+ 
++static void xhci_calc_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
++                               XHCIEPContext *epctx, uint64_t mfindex)
++{
++    if (xfer->trbs[0].control & TRB_TR_SIA) {
++        uint64_t asap = ((mfindex + epctx->interval - 1) &
++                         ~(epctx->interval-1));
++        if (asap >= epctx->mfindex_last &&
++            asap <= epctx->mfindex_last + epctx->interval * 4) {
++            xfer->mfindex_kick = epctx->mfindex_last + epctx->interval;
++        } else {
++            xfer->mfindex_kick = asap;
++        }
++    } else {
++        xfer->mfindex_kick = (xfer->trbs[0].control >> TRB_TR_FRAMEID_SHIFT)
++            & TRB_TR_FRAMEID_MASK;
++        xfer->mfindex_kick |= mfindex & ~0x3fff;
++        if (xfer->mfindex_kick < mfindex) {
++            xfer->mfindex_kick += 0x4000;
++        }
++    }
++}
++
++static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
++                                XHCIEPContext *epctx, uint64_t mfindex)
++{
++    if (xfer->mfindex_kick > mfindex) {
++        qemu_mod_timer(epctx->kick_timer, qemu_get_clock_ns(vm_clock) +
++                       (xfer->mfindex_kick - mfindex) * 125000);
++        xfer->running_retry = 1;
++    } else {
++        epctx->mfindex_last = xfer->mfindex_kick;
++        qemu_del_timer(epctx->kick_timer);
++        xfer->running_retry = 0;
++    }
++}
++
++
+ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx)
+ {
++    uint64_t mfindex;
+     int ret;
+ 
+     DPRINTF("xhci_submit(slotid=%d,epid=%d)\n", xfer->slotid, xfer->epid);
+ 
+     xfer->in_xfer = epctx->type>>2;
+ 
+-    if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) {
+-        xfer->pkts = 1;
+-    } else {
+-        xfer->pkts = 0;
+-    }
+-
+     switch(epctx->type) {
+     case ET_INTR_OUT:
+     case ET_INTR_IN:
+     case ET_BULK_OUT:
+     case ET_BULK_IN:
++        xfer->pkts = 0;
++        xfer->iso_xfer = false;
+         break;
+     case ET_ISO_OUT:
+     case ET_ISO_IN:
+-        FIXME();
++        xfer->pkts = 1;
++        xfer->iso_xfer = true;
++        mfindex = xhci_mfindex_get(xhci);
++        xhci_calc_iso_kick(xhci, xfer, epctx, mfindex);
++        xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
++        if (xfer->running_retry) {
++            return -1;
++        }
+         break;
+     default:
+         fprintf(stderr, "xhci: unknown or unhandled EP "
+@@ -1430,6 +1497,7 @@ static int xhci_fire_transfer(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext
+ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid)
+ {
+     XHCIEPContext *epctx;
++    uint64_t mfindex;
+     int length;
+     int i;
+ 
+@@ -1449,20 +1517,35 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
+     }
+ 
+     if (epctx->retry) {
+-        /* retry nak'ed transfer */
+         XHCITransfer *xfer = epctx->retry;
+         int result;
+ 
+         trace_usb_xhci_xfer_retry(xfer);
+         assert(xfer->running_retry);
+-        if (xhci_setup_packet(xfer) < 0) {
+-            return;
+-        }
+-        result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+-        if (result == USB_RET_NAK) {
+-            return;
++        if (xfer->iso_xfer) {
++            /* retry delayed iso transfer */
++            mfindex = xhci_mfindex_get(xhci);
++            xhci_check_iso_kick(xhci, xfer, epctx, mfindex);
++            if (xfer->running_retry) {
++                return;
++            }
++            if (xhci_setup_packet(xfer) < 0) {
++                return;
++            }
++            result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
++            assert(result != USB_RET_NAK);
++            xhci_complete_packet(xfer, result);
++        } else {
++            /* retry nak'ed transfer */
++            if (xhci_setup_packet(xfer) < 0) {
++                return;
++            }
++            result = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
++            if (result == USB_RET_NAK) {
++                return;
++            }
++            xhci_complete_packet(xfer, result);
+         }
+-        xhci_complete_packet(xfer, result);
+         assert(!xfer->running_retry);
+         epctx->retry = NULL;
+     }
+@@ -1514,7 +1597,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
+             if (xhci_fire_transfer(xhci, xfer, epctx) >= 0) {
+                 epctx->next_xfer = (epctx->next_xfer + 1) % TD_QUEUE;
+             } else {
+-                fprintf(stderr, "xhci: error firing data transfer\n");
++                if (!xfer->iso_xfer) {
++                    fprintf(stderr, "xhci: error firing data transfer\n");
++                }
+             }
+         }
+ 
+-- 
+1.7.12
+
diff --git a/0336-xhci-trace-cc-codes-in-cleartext.patch b/0336-xhci-trace-cc-codes-in-cleartext.patch
new file mode 100644
index 0000000..096d194
--- /dev/null
+++ b/0336-xhci-trace-cc-codes-in-cleartext.patch
@@ -0,0 +1,100 @@
+From 530efbfebc3b33f6b109e52d57a25e9b7fe2b588 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Mon, 27 Aug 2012 16:09:20 +0200
+Subject: [PATCH 336/366] xhci: trace cc codes in cleartext
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
+ trace-events      |  2 +-
+ 2 files changed, 48 insertions(+), 2 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 3c61bb8..ab32a7b 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -465,6 +465,45 @@ static const char *TRBType_names[] = {
+     [CR_VENDOR_NEC_CHALLENGE_RESPONSE] = "CR_VENDOR_NEC_CHALLENGE_RESPONSE",
+ };
+ 
++static const char *TRBCCode_names[] = {
++    [CC_INVALID]                       = "CC_INVALID",
++    [CC_SUCCESS]                       = "CC_SUCCESS",
++    [CC_DATA_BUFFER_ERROR]             = "CC_DATA_BUFFER_ERROR",
++    [CC_BABBLE_DETECTED]               = "CC_BABBLE_DETECTED",
++    [CC_USB_TRANSACTION_ERROR]         = "CC_USB_TRANSACTION_ERROR",
++    [CC_TRB_ERROR]                     = "CC_TRB_ERROR",
++    [CC_STALL_ERROR]                   = "CC_STALL_ERROR",
++    [CC_RESOURCE_ERROR]                = "CC_RESOURCE_ERROR",
++    [CC_BANDWIDTH_ERROR]               = "CC_BANDWIDTH_ERROR",
++    [CC_NO_SLOTS_ERROR]                = "CC_NO_SLOTS_ERROR",
++    [CC_INVALID_STREAM_TYPE_ERROR]     = "CC_INVALID_STREAM_TYPE_ERROR",
++    [CC_SLOT_NOT_ENABLED_ERROR]        = "CC_SLOT_NOT_ENABLED_ERROR",
++    [CC_EP_NOT_ENABLED_ERROR]          = "CC_EP_NOT_ENABLED_ERROR",
++    [CC_SHORT_PACKET]                  = "CC_SHORT_PACKET",
++    [CC_RING_UNDERRUN]                 = "CC_RING_UNDERRUN",
++    [CC_RING_OVERRUN]                  = "CC_RING_OVERRUN",
++    [CC_VF_ER_FULL]                    = "CC_VF_ER_FULL",
++    [CC_PARAMETER_ERROR]               = "CC_PARAMETER_ERROR",
++    [CC_BANDWIDTH_OVERRUN]             = "CC_BANDWIDTH_OVERRUN",
++    [CC_CONTEXT_STATE_ERROR]           = "CC_CONTEXT_STATE_ERROR",
++    [CC_NO_PING_RESPONSE_ERROR]        = "CC_NO_PING_RESPONSE_ERROR",
++    [CC_EVENT_RING_FULL_ERROR]         = "CC_EVENT_RING_FULL_ERROR",
++    [CC_INCOMPATIBLE_DEVICE_ERROR]     = "CC_INCOMPATIBLE_DEVICE_ERROR",
++    [CC_MISSED_SERVICE_ERROR]          = "CC_MISSED_SERVICE_ERROR",
++    [CC_COMMAND_RING_STOPPED]          = "CC_COMMAND_RING_STOPPED",
++    [CC_COMMAND_ABORTED]               = "CC_COMMAND_ABORTED",
++    [CC_STOPPED]                       = "CC_STOPPED",
++    [CC_STOPPED_LENGTH_INVALID]        = "CC_STOPPED_LENGTH_INVALID",
++    [CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR]
++    = "CC_MAX_EXIT_LATENCY_TOO_LARGE_ERROR",
++    [CC_ISOCH_BUFFER_OVERRUN]          = "CC_ISOCH_BUFFER_OVERRUN",
++    [CC_EVENT_LOST_ERROR]              = "CC_EVENT_LOST_ERROR",
++    [CC_UNDEFINED_ERROR]               = "CC_UNDEFINED_ERROR",
++    [CC_INVALID_STREAM_ID_ERROR]       = "CC_INVALID_STREAM_ID_ERROR",
++    [CC_SECONDARY_BANDWIDTH_ERROR]     = "CC_SECONDARY_BANDWIDTH_ERROR",
++    [CC_SPLIT_TRANSACTION_ERROR]       = "CC_SPLIT_TRANSACTION_ERROR",
++};
++
+ static const char *lookup_name(uint32_t index, const char **list, uint32_t llen)
+ {
+     if (index >= llen || list[index] == NULL) {
+@@ -479,6 +518,12 @@ static const char *trb_name(XHCITRB *trb)
+                        ARRAY_SIZE(TRBType_names));
+ }
+ 
++static const char *event_name(XHCIEvent *event)
++{
++    return lookup_name(event->ccode, TRBCCode_names,
++                       ARRAY_SIZE(TRBCCode_names));
++}
++
+ static uint64_t xhci_mfindex_get(XHCIState *xhci)
+ {
+     int64_t now = qemu_get_clock_ns(vm_clock);
+@@ -574,7 +619,8 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
+     ev_trb.control = cpu_to_le32(ev_trb.control);
+ 
+     trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb),
+-                               ev_trb.parameter, ev_trb.status, ev_trb.control);
++                               event_name(event), ev_trb.parameter,
++                               ev_trb.status, ev_trb.control);
+ 
+     addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
+     pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE);
+diff --git a/trace-events b/trace-events
+index c83d65e..27d59cd 100644
+--- a/trace-events
++++ b/trace-events
+@@ -313,7 +313,7 @@ usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
+ usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
+ usb_xhci_irq_intx(uint32_t level) "level %d"
+ usb_xhci_irq_msi(uint32_t nr) "nr %d"
+-usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
++usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+ usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+ usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
+ usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
+-- 
+1.7.12
+
diff --git a/0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch b/0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch
new file mode 100644
index 0000000..c36d7d8
--- /dev/null
+++ b/0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch
@@ -0,0 +1,39 @@
+From 53adf697ca70ee8298b6abeb05ebf5a0ebebdc1c Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Wed, 29 Aug 2012 12:54:59 +0200
+Subject: [PATCH 337/366] xhci: add trace_usb_xhci_ep_set_dequeue
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 2 +-
+ trace-events      | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index ab32a7b..5cdaf76 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -1145,7 +1145,7 @@ static TRBCCode xhci_set_ep_dequeue(XHCIState *xhci, unsigned int slotid,
+         return CC_TRB_ERROR;
+     }
+ 
+-    DPRINTF("xhci_set_ep_dequeue(%d, %d, %016"PRIx64")\n", slotid, epid, pdequeue);
++    trace_usb_xhci_ep_set_dequeue(slotid, epid, pdequeue);
+     dequeue = xhci_mask64(pdequeue);
+ 
+     slot = &xhci->slots[slotid-1];
+diff --git a/trace-events b/trace-events
+index 27d59cd..a894689 100644
+--- a/trace-events
++++ b/trace-events
+@@ -323,6 +323,7 @@ usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d"
+ usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
+ usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+ usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
++usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64
+ usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+ usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+ usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+-- 
+1.7.12
+
diff --git a/0338-xhci-fix-runtime-write-tracepoint.patch b/0338-xhci-fix-runtime-write-tracepoint.patch
new file mode 100644
index 0000000..a2eebfd
--- /dev/null
+++ b/0338-xhci-fix-runtime-write-tracepoint.patch
@@ -0,0 +1,26 @@
+From 9c1e6303c5c86f20ed083a0ed9c829c08fd2ac06 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 30 Aug 2012 12:42:32 +0200
+Subject: [PATCH 338/366] xhci: fix runtime write tracepoint
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 5cdaf76..e8d2372 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -2520,7 +2520,7 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+ 
+ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+ {
+-    trace_usb_xhci_runtime_read(reg, val);
++    trace_usb_xhci_runtime_write(reg, val);
+ 
+     switch (reg) {
+     case 0x20: /* IMAN */
+-- 
+1.7.12
+
diff --git a/0339-xhci-update-register-layout.patch b/0339-xhci-update-register-layout.patch
new file mode 100644
index 0000000..7fc8af1
--- /dev/null
+++ b/0339-xhci-update-register-layout.patch
@@ -0,0 +1,63 @@
+From 56a4ea65ecc929a355b0cc243099d2e3a9862843 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 23 Aug 2012 13:26:25 +0200
+Subject: [PATCH 339/366] xhci: update register layout
+
+Change the register layout to be a bit more sparse and also not depend
+on the number of ports.  Useful when for making the number of ports
+runtime-configurable.
+---
+ hw/usb/hcd-xhci.c | 21 +++++++++++++--------
+ 1 file changed, 13 insertions(+), 8 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index e8d2372..414b633 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -36,13 +36,12 @@
+ #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
+                              __func__, __LINE__); abort(); } while (0)
+ 
+-#define MAXSLOTS 8
+-#define MAXINTRS 1
+-
+ #define USB2_PORTS 4
+ #define USB3_PORTS 4
+ 
+ #define MAXPORTS (USB2_PORTS+USB3_PORTS)
++#define MAXSLOTS MAXPORTS
++#define MAXINTRS 1 /* MAXPORTS */
+ 
+ #define TD_QUEUE 24
+ 
+@@ -53,16 +52,22 @@
+ #define ER_FULL_HACK
+ 
+ #define LEN_CAP         0x40
+-#define OFF_OPER        LEN_CAP
+ #define LEN_OPER        (0x400 + 0x10 * MAXPORTS)
+-#define OFF_RUNTIME     ((OFF_OPER + LEN_OPER + 0x20) & ~0x1f)
+-#define LEN_RUNTIME     (0x20 + MAXINTRS * 0x20)
+-#define OFF_DOORBELL    (OFF_RUNTIME + LEN_RUNTIME)
++#define LEN_RUNTIME     ((MAXINTRS + 1) * 0x20)
+ #define LEN_DOORBELL    ((MAXSLOTS + 1) * 0x20)
+ 
++#define OFF_OPER        LEN_CAP
++#define OFF_RUNTIME     0x1000
++#define OFF_DOORBELL    0x2000
+ /* must be power of 2 */
+-#define LEN_REGS        0x2000
++#define LEN_REGS        0x4000
+ 
++#if (OFF_OPER + LEN_OPER) > OFF_RUNTIME
++#error Increase OFF_RUNTIME
++#endif
++#if (OFF_RUNTIME + LEN_RUNTIME) > OFF_DOORBELL
++#error Increase OFF_DOORBELL
++#endif
+ #if (OFF_DOORBELL + LEN_DOORBELL) > LEN_REGS
+ # error Increase LEN_REGS
+ #endif
+-- 
+1.7.12
+
diff --git a/0340-xhci-update-port-handling.patch b/0340-xhci-update-port-handling.patch
new file mode 100644
index 0000000..289fa52
--- /dev/null
+++ b/0340-xhci-update-port-handling.patch
@@ -0,0 +1,352 @@
+From ed16ece865cb40c9ad8ddf28905c0af3ad80e118 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 28 Aug 2012 13:38:01 +0200
+Subject: [PATCH 340/366] xhci: update port handling
+
+This patch changes the way xhci ports are linked to USBPorts.  The fixed
+1:1 relationship between xhci ports and USBPorts is gone.  Now each
+USBPort represents a physical plug which has usually two xhci ports
+assigned: one usb2 and ond usb3 port.  usb devices show up at one or the
+other, depending on whenever they support superspeed or not.
+
+This patch also makes the number of usb2 and usb3 ports runtime
+configurable by adding 'p2' and 'p3' properties.  It is allowed to
+have different numbers of usb2 and usb3 ports.  Specifying p2=4,p3=2
+will give you an xhci adapter which supports all speeds on physical
+ports 1+2 and usb2 only on ports 3+4.
+---
+ hw/usb/hcd-xhci.c | 137 ++++++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 97 insertions(+), 40 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 414b633..e08312e 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -36,10 +36,10 @@
+ #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \
+                              __func__, __LINE__); abort(); } while (0)
+ 
+-#define USB2_PORTS 4
+-#define USB3_PORTS 4
++#define MAXPORTS_2 8
++#define MAXPORTS_3 8
+ 
+-#define MAXPORTS (USB2_PORTS+USB3_PORTS)
++#define MAXPORTS (MAXPORTS_2+MAXPORTS_3)
+ #define MAXSLOTS MAXPORTS
+ #define MAXINTRS 1 /* MAXPORTS */
+ 
+@@ -300,8 +300,10 @@ typedef struct XHCIRing {
+ } XHCIRing;
+ 
+ typedef struct XHCIPort {
+-    USBPort port;
+     uint32_t portsc;
++    uint32_t portnr;
++    USBPort  *uport;
++    uint32_t speedmask;
+ } XHCIPort;
+ 
+ struct XHCIState;
+@@ -379,9 +381,13 @@ struct XHCIState {
+     qemu_irq irq;
+     MemoryRegion mem;
+     const char *name;
+-    uint32_t msi;
+     unsigned int devaddr;
+ 
++    /* properties */
++    uint32_t numports_2;
++    uint32_t numports_3;
++    uint32_t msi;
++
+     /* Operational Registers */
+     uint32_t usbcmd;
+     uint32_t usbsts;
+@@ -392,8 +398,10 @@ struct XHCIState {
+     uint32_t dcbaap_high;
+     uint32_t config;
+ 
++    USBPort  uports[MAX(MAXPORTS_2, MAXPORTS_3)];
+     XHCIPort ports[MAXPORTS];
+     XHCISlot slots[MAXSLOTS];
++    uint32_t numports;
+ 
+     /* Runtime Registers */
+     uint32_t iman;
+@@ -578,6 +586,28 @@ static inline dma_addr_t xhci_mask64(uint64_t addr)
+     }
+ }
+ 
++static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
++{
++    int index;
++
++    if (!uport->dev) {
++        return NULL;
++    }
++    switch (uport->dev->speed) {
++    case USB_SPEED_LOW:
++    case USB_SPEED_FULL:
++    case USB_SPEED_HIGH:
++        index = uport->index;
++        break;
++    case USB_SPEED_SUPER:
++        index = uport->index + xhci->numports_2;
++        break;
++    default:
++        return NULL;
++    }
++    return &xhci->ports[index];
++}
++
+ static void xhci_irq_update(XHCIState *xhci)
+ {
+     int level = 0;
+@@ -1126,7 +1156,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid,
+         ep |= 0x80;
+     }
+ 
+-    dev = xhci->ports[xhci->slots[slotid-1].port-1].port.dev;
++    dev = xhci->ports[xhci->slots[slotid-1].port-1].uport->dev;
+     if (!dev) {
+         return CC_USB_TRANSACTION_ERROR;
+     }
+@@ -1313,7 +1343,7 @@ static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr)
+     if (!(port->portsc & PORTSC_PED)) {
+         return NULL;
+     }
+-    return usb_find_device(&port->port, addr);
++    return usb_find_device(port->uport, addr);
+ }
+ 
+ static int xhci_setup_packet(XHCITransfer *xfer)
+@@ -1736,9 +1766,9 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
+             ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
+ 
+     port = (slot_ctx[1]>>16) & 0xFF;
+-    dev = xhci->ports[port-1].port.dev;
++    dev = xhci->ports[port-1].uport->dev;
+ 
+-    if (port < 1 || port > MAXPORTS) {
++    if (port < 1 || port > xhci->numports) {
+         fprintf(stderr, "xhci: bad port %d\n", port);
+         return CC_TRB_ERROR;
+     } else if (!dev) {
+@@ -1987,7 +2017,7 @@ static unsigned int xhci_get_slot(XHCIState *xhci, XHCIEvent *event, XHCITRB *tr
+ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
+ {
+     dma_addr_t ctx;
+-    uint8_t bw_ctx[MAXPORTS+1];
++    uint8_t bw_ctx[xhci->numports+1];
+ 
+     DPRINTF("xhci_get_port_bandwidth()\n");
+ 
+@@ -1997,7 +2027,7 @@ static TRBCCode xhci_get_port_bandwidth(XHCIState *xhci, uint64_t pctx)
+ 
+     /* TODO: actually implement real values here */
+     bw_ctx[0] = 0;
+-    memset(&bw_ctx[1], 80, MAXPORTS); /* 80% */
++    memset(&bw_ctx[1], 80, xhci->numports); /* 80% */
+     pci_dma_write(&xhci->pci_dev, ctx, bw_ctx, sizeof(bw_ctx));
+ 
+     return CC_SUCCESS;
+@@ -2167,12 +2197,11 @@ static void xhci_process_commands(XHCIState *xhci)
+ 
+ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
+ {
+-    int nr = port->port.index + 1;
+-
+     port->portsc = PORTSC_PP;
+-    if (port->port.dev && port->port.dev->attached && !is_detach) {
++    if (port->uport->dev && port->uport->dev->attached && !is_detach &&
++        (1 << port->uport->dev->speed) & port->speedmask) {
+         port->portsc |= PORTSC_CCS;
+-        switch (port->port.dev->speed) {
++        switch (port->uport->dev->speed) {
+         case USB_SPEED_LOW:
+             port->portsc |= PORTSC_SPEED_LOW;
+             break;
+@@ -2182,14 +2211,18 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
+         case USB_SPEED_HIGH:
+             port->portsc |= PORTSC_SPEED_HIGH;
+             break;
++        case USB_SPEED_SUPER:
++            port->portsc |= PORTSC_SPEED_SUPER;
++            break;
+         }
+     }
+ 
+     if (xhci_running(xhci)) {
+         port->portsc |= PORTSC_CSC;
+-        XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
++        XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
++                         port->portnr << 24};
+         xhci_event(xhci, &ev);
+-        DPRINTF("xhci: port change event for port %d\n", nr);
++        DPRINTF("xhci: port change event for port %d\n", port->portnr);
+     }
+ }
+ 
+@@ -2217,7 +2250,7 @@ static void xhci_reset(DeviceState *dev)
+         xhci_disable_slot(xhci, i+1);
+     }
+ 
+-    for (i = 0; i < MAXPORTS; i++) {
++    for (i = 0; i < xhci->numports; i++) {
+         xhci_update_port(xhci, xhci->ports + i, 0);
+     }
+ 
+@@ -2248,7 +2281,8 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+         ret = 0x01000000 | LEN_CAP;
+         break;
+     case 0x04: /* HCSPARAMS 1 */
+-        ret = (MAXPORTS<<24) | (MAXINTRS<<8) | MAXSLOTS;
++        ret = ((xhci->numports_2+xhci->numports_3)<<24)
++            | (MAXINTRS<<8) | MAXSLOTS;
+         break;
+     case 0x08: /* HCSPARAMS 2 */
+         ret = 0x0000000f;
+@@ -2278,7 +2312,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+         ret = 0x20425455; /* "USB " */
+         break;
+     case 0x28: /* Supported Protocol:08 */
+-        ret = 0x00000001 | (USB2_PORTS<<8);
++        ret = 0x00000001 | (xhci->numports_2<<8);
+         break;
+     case 0x2c: /* Supported Protocol:0c */
+         ret = 0x00000000; /* reserved */
+@@ -2290,7 +2324,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+         ret = 0x20425455; /* "USB " */
+         break;
+     case 0x38: /* Supported Protocol:08 */
+-        ret = 0x00000000 | (USB2_PORTS+1) | (USB3_PORTS<<8);
++        ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8);
+         break;
+     case 0x3c: /* Supported Protocol:0c */
+         ret = 0x00000000; /* reserved */
+@@ -2309,7 +2343,7 @@ static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg)
+     uint32_t port = reg >> 4;
+     uint32_t ret;
+ 
+-    if (port >= MAXPORTS) {
++    if (port >= xhci->numports) {
+         fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
+         ret = 0;
+         goto out;
+@@ -2342,7 +2376,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+ 
+     trace_usb_xhci_port_write(port, reg & 0x0f, val);
+ 
+-    if (port >= MAXPORTS) {
++    if (port >= xhci->numports) {
+         fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port);
+         return;
+     }
+@@ -2364,7 +2398,7 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         /* write-1-to-start bits */
+         if (val & PORTSC_PR) {
+             DPRINTF("xhci: port %d reset\n", port);
+-            usb_device_reset(xhci->ports[port].port.dev);
++            usb_device_reset(xhci->ports[port].uport->dev);
+             portsc |= PORTSC_PRC | PORTSC_PED;
+         }
+         xhci->ports[port].portsc = portsc;
+@@ -2659,7 +2693,7 @@ static const MemoryRegionOps xhci_mem_ops = {
+ static void xhci_attach(USBPort *usbport)
+ {
+     XHCIState *xhci = usbport->opaque;
+-    XHCIPort *port = &xhci->ports[usbport->index];
++    XHCIPort *port = xhci_lookup_port(xhci, usbport);
+ 
+     xhci_update_port(xhci, port, 0);
+ }
+@@ -2667,7 +2701,7 @@ static void xhci_attach(USBPort *usbport)
+ static void xhci_detach(USBPort *usbport)
+ {
+     XHCIState *xhci = usbport->opaque;
+-    XHCIPort *port = &xhci->ports[usbport->index];
++    XHCIPort *port = xhci_lookup_port(xhci, usbport);
+ 
+     xhci_update_port(xhci, port, 1);
+ }
+@@ -2675,9 +2709,9 @@ static void xhci_detach(USBPort *usbport)
+ static void xhci_wakeup(USBPort *usbport)
+ {
+     XHCIState *xhci = usbport->opaque;
+-    XHCIPort *port = &xhci->ports[usbport->index];
+-    int nr = port->port.index + 1;
+-    XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS, nr << 24};
++    XHCIPort *port = xhci_lookup_port(xhci, usbport);
++    XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
++                     port->portnr << 24};
+     uint32_t pls;
+ 
+     pls = (port->portsc >> PORTSC_PLS_SHIFT) & PORTSC_PLS_MASK;
+@@ -2759,22 +2793,43 @@ static USBBusOps xhci_bus_ops = {
+ 
+ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev)
+ {
+-    int i;
++    XHCIPort *port;
++    int i, usbports, speedmask;
+ 
+     xhci->usbsts = USBSTS_HCH;
+ 
++    if (xhci->numports_2 > MAXPORTS_2) {
++        xhci->numports_2 = MAXPORTS_2;
++    }
++    if (xhci->numports_3 > MAXPORTS_3) {
++        xhci->numports_3 = MAXPORTS_3;
++    }
++    usbports = MAX(xhci->numports_2, xhci->numports_3);
++    xhci->numports = xhci->numports_2 + xhci->numports_3;
++
+     usb_bus_new(&xhci->bus, &xhci_bus_ops, &xhci->pci_dev.qdev);
+ 
+-    for (i = 0; i < MAXPORTS; i++) {
+-        memset(&xhci->ports[i], 0, sizeof(xhci->ports[i]));
+-        usb_register_port(&xhci->bus, &xhci->ports[i].port, xhci, i,
+-                          &xhci_port_ops,
+-                          USB_SPEED_MASK_LOW  |
+-                          USB_SPEED_MASK_FULL |
+-                          USB_SPEED_MASK_HIGH);
+-    }
+-    for (i = 0; i < MAXSLOTS; i++) {
+-        xhci->slots[i].enabled = 0;
++    for (i = 0; i < usbports; i++) {
++        speedmask = 0;
++        if (i < xhci->numports_2) {
++            port = &xhci->ports[i];
++            port->portnr = i + 1;
++            port->uport = &xhci->uports[i];
++            port->speedmask =
++                USB_SPEED_MASK_LOW  |
++                USB_SPEED_MASK_FULL |
++                USB_SPEED_MASK_HIGH;
++            speedmask |= port->speedmask;
++        }
++        if (i < xhci->numports_3) {
++            port = &xhci->ports[i + xhci->numports_2];
++            port->portnr = i + 1 + xhci->numports_2;
++            port->uport = &xhci->uports[i];
++            port->speedmask = USB_SPEED_MASK_SUPER;
++            speedmask |= port->speedmask;
++        }
++        usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i,
++                          &xhci_port_ops, speedmask);
+     }
+ }
+ 
+@@ -2830,6 +2885,8 @@ static const VMStateDescription vmstate_xhci = {
+ 
+ static Property xhci_properties[] = {
+     DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
++    DEFINE_PROP_UINT32("p2",  XHCIState, numports_2, 4),
++    DEFINE_PROP_UINT32("p3",  XHCIState, numports_3, 4),
+     DEFINE_PROP_END_OF_LIST(),
+ };
+ 
+-- 
+1.7.12
+
diff --git a/0341-usb3-superspeed-descriptors.patch b/0341-usb3-superspeed-descriptors.patch
new file mode 100644
index 0000000..05591fe
--- /dev/null
+++ b/0341-usb3-superspeed-descriptors.patch
@@ -0,0 +1,64 @@
+From f25f31e864756f27f6a94ab7e66b20061291ffa5 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 28 Aug 2012 17:28:50 +0200
+Subject: [PATCH 341/366] usb3: superspeed descriptors
+
+Add superspeed descriptor entry to USBDesc,
+advertise superspeed support when present.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/desc.c | 10 +++++++++-
+ hw/usb/desc.h |  1 +
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/desc.c b/hw/usb/desc.c
+index 0a9d3c9..3e8c6cb 100644
+--- a/hw/usb/desc.c
++++ b/hw/usb/desc.c
+@@ -359,6 +359,9 @@ static void usb_desc_setdefaults(USBDevice *dev)
+     case USB_SPEED_HIGH:
+         dev->device = desc->high;
+         break;
++    case USB_SPEED_SUPER:
++        dev->device = desc->super;
++        break;
+     }
+     usb_desc_set_config(dev, 0);
+ }
+@@ -376,6 +379,9 @@ void usb_desc_init(USBDevice *dev)
+     if (desc->high) {
+         dev->speedmask |= USB_SPEED_MASK_HIGH;
+     }
++    if (desc->super) {
++        dev->speedmask |= USB_SPEED_MASK_SUPER;
++    }
+     usb_desc_setdefaults(dev);
+ }
+ 
+@@ -384,7 +390,9 @@ void usb_desc_attach(USBDevice *dev)
+     const USBDesc *desc = usb_device_get_usb_desc(dev);
+ 
+     assert(desc != NULL);
+-    if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
++    if (desc->super && (dev->port->speedmask & USB_SPEED_MASK_SUPER)) {
++        dev->speed = USB_SPEED_SUPER;
++    } else if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
+         dev->speed = USB_SPEED_HIGH;
+     } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
+         dev->speed = USB_SPEED_FULL;
+diff --git a/hw/usb/desc.h b/hw/usb/desc.h
+index 7cf5442..d89fa41 100644
+--- a/hw/usb/desc.h
++++ b/hw/usb/desc.h
+@@ -152,6 +152,7 @@ struct USBDesc {
+     USBDescID                 id;
+     const USBDescDevice       *full;
+     const USBDescDevice       *high;
++    const USBDescDevice       *super;
+     const char* const         *str;
+ };
+ 
+-- 
+1.7.12
+
diff --git a/0342-usb3-superspeed-endpoint-companion.patch b/0342-usb3-superspeed-endpoint-companion.patch
new file mode 100644
index 0000000..7949f8f
--- /dev/null
+++ b/0342-usb3-superspeed-endpoint-companion.patch
@@ -0,0 +1,248 @@
+From 5a02a74430f4628a78f43242afbb1377deea4c80 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 28 Aug 2012 17:28:03 +0200
+Subject: [PATCH 342/366] usb3: superspeed endpoint companion
+
+Add support for building superspeed endpoint companion descriptors,
+create them for superspeed usb devices.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb.h      |  1 +
+ hw/usb/desc.c | 55 ++++++++++++++++++++++++++++++++++++++++---------------
+ hw/usb/desc.h | 26 +++++++++++++++++++++-----
+ 3 files changed, 62 insertions(+), 20 deletions(-)
+
+diff --git a/hw/usb.h b/hw/usb.h
+index 684e3f4..78ffdf4 100644
+--- a/hw/usb.h
++++ b/hw/usb.h
+@@ -137,6 +137,7 @@
+ #define USB_DT_INTERFACE_ASSOC          0x0B
+ #define USB_DT_CS_INTERFACE             0x24
+ #define USB_DT_CS_ENDPOINT              0x25
++#define USB_DT_ENDPOINT_COMPANION       0x30
+ 
+ #define USB_ENDPOINT_XFER_CONTROL	0
+ #define USB_ENDPOINT_XFER_ISOC		1
+diff --git a/hw/usb/desc.c b/hw/usb/desc.c
+index 3e8c6cb..8f5a8e5 100644
+--- a/hw/usb/desc.c
++++ b/hw/usb/desc.c
+@@ -76,7 +76,8 @@ int usb_desc_device_qualifier(const USBDescDevice *dev,
+     return bLength;
+ }
+ 
+-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
++int usb_desc_config(const USBDescConfig *conf, int flags,
++                    uint8_t *dest, size_t len)
+ {
+     uint8_t  bLength = 0x09;
+     uint16_t wTotalLength = 0;
+@@ -99,7 +100,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
+ 
+     /* handle grouped interfaces if any */
+     for (i = 0; i < conf->nif_groups; i++) {
+-        rc = usb_desc_iface_group(&(conf->if_groups[i]),
++        rc = usb_desc_iface_group(&(conf->if_groups[i]), flags,
+                                   dest + wTotalLength,
+                                   len - wTotalLength);
+         if (rc < 0) {
+@@ -110,7 +111,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
+ 
+     /* handle normal (ungrouped / no IAD) interfaces if any */
+     for (i = 0; i < conf->nif; i++) {
+-        rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
++        rc = usb_desc_iface(conf->ifs + i, flags,
++                            dest + wTotalLength, len - wTotalLength);
+         if (rc < 0) {
+             return rc;
+         }
+@@ -122,8 +124,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
+     return wTotalLength;
+ }
+ 
+-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+-                         size_t len)
++int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
++                         uint8_t *dest, size_t len)
+ {
+     int pos = 0;
+     int i = 0;
+@@ -147,7 +149,7 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+ 
+     /* handle associated interfaces in this group */
+     for (i = 0; i < iad->nif; i++) {
+-        int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
++        int rc = usb_desc_iface(&(iad->ifs[i]), flags, dest + pos, len - pos);
+         if (rc < 0) {
+             return rc;
+         }
+@@ -157,7 +159,8 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+     return pos;
+ }
+ 
+-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
++int usb_desc_iface(const USBDescIface *iface, int flags,
++                   uint8_t *dest, size_t len)
+ {
+     uint8_t bLength = 0x09;
+     int i, rc, pos = 0;
+@@ -188,7 +191,7 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
+     }
+ 
+     for (i = 0; i < iface->bNumEndpoints; i++) {
+-        rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
++        rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos);
+         if (rc < 0) {
+             return rc;
+         }
+@@ -198,13 +201,15 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
+     return pos;
+ }
+ 
+-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
++int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
++                      uint8_t *dest, size_t len)
+ {
+     uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
+     uint8_t extralen = ep->extra ? ep->extra[0] : 0;
++    uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0;
+     USBDescriptor *d = (void *)dest;
+ 
+-    if (len < bLength + extralen) {
++    if (len < bLength + extralen + superlen) {
+         return -1;
+     }
+ 
+@@ -224,7 +229,21 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
+         memcpy(dest + bLength, ep->extra, extralen);
+     }
+ 
+-    return bLength + extralen;
++    if (superlen) {
++        USBDescriptor *d = (void *)(dest + bLength + extralen);
++
++        d->bLength                       = 0x06;
++        d->bDescriptorType               = USB_DT_ENDPOINT_COMPANION;
++
++        d->u.super_endpoint.bMaxBurst    = ep->bMaxBurst;
++        d->u.super_endpoint.bmAttributes = ep->bmAttributes_super;
++        d->u.super_endpoint.wBytesPerInterval_lo =
++            usb_lo(ep->wBytesPerInterval);
++        d->u.super_endpoint.wBytesPerInterval_hi =
++            usb_hi(ep->wBytesPerInterval);
++    }
++
++    return bLength + extralen + superlen;
+ }
+ 
+ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
+@@ -509,7 +528,7 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
+     uint8_t buf[256];
+     uint8_t type = value >> 8;
+     uint8_t index = value & 0xff;
+-    int ret = -1;
++    int flags, ret = -1;
+ 
+     if (dev->speed == USB_SPEED_HIGH) {
+         other_dev = usb_device_get_usb_desc(dev)->full;
+@@ -517,6 +536,11 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
+         other_dev = usb_device_get_usb_desc(dev)->high;
+     }
+ 
++    flags = 0;
++    if (dev->device->bcdUSB >= 0x0300) {
++        flags |= USB_DESC_FLAG_SUPER;
++    }
++
+     switch(type) {
+     case USB_DT_DEVICE:
+         ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
+@@ -524,7 +548,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
+         break;
+     case USB_DT_CONFIG:
+         if (index < dev->device->bNumConfigurations) {
+-            ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
++            ret = usb_desc_config(dev->device->confs + index, flags,
++                                  buf, sizeof(buf));
+         }
+         trace_usb_desc_config(dev->addr, index, len, ret);
+         break;
+@@ -532,7 +557,6 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
+         ret = usb_desc_string(dev, index, buf, sizeof(buf));
+         trace_usb_desc_string(dev->addr, index, len, ret);
+         break;
+-
+     case USB_DT_DEVICE_QUALIFIER:
+         if (other_dev != NULL) {
+             ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
+@@ -541,7 +565,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
+         break;
+     case USB_DT_OTHER_SPEED_CONFIG:
+         if (other_dev != NULL && index < other_dev->bNumConfigurations) {
+-            ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
++            ret = usb_desc_config(other_dev->confs + index, flags,
++                                  buf, sizeof(buf));
+             buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
+         }
+         trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
+diff --git a/hw/usb/desc.h b/hw/usb/desc.h
+index d89fa41..4b5e88d 100644
+--- a/hw/usb/desc.h
++++ b/hw/usb/desc.h
+@@ -63,6 +63,12 @@ typedef struct USBDescriptor {
+             uint8_t           bRefresh;        /* only audio ep */
+             uint8_t           bSynchAddress;   /* only audio ep */
+         } endpoint;
++        struct {
++            uint8_t           bMaxBurst;
++            uint8_t           bmAttributes;
++            uint8_t           wBytesPerInterval_lo;
++            uint8_t           wBytesPerInterval_hi;
++        } super_endpoint;
+     } u;
+ } QEMU_PACKED USBDescriptor;
+ 
+@@ -139,6 +145,11 @@ struct USBDescEndpoint {
+ 
+     uint8_t                   is_audio; /* has bRefresh + bSynchAddress */
+     uint8_t                   *extra;
++
++    /* superspeed endpoint companion */
++    uint8_t                   bMaxBurst;
++    uint8_t                   bmAttributes_super;
++    uint16_t                  wBytesPerInterval;
+ };
+ 
+ struct USBDescOther {
+@@ -156,16 +167,21 @@ struct USBDesc {
+     const char* const         *str;
+ };
+ 
++#define USB_DESC_FLAG_SUPER (1 << 1)
++
+ /* generate usb packages from structs */
+ int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
+                     uint8_t *dest, size_t len);
+ int usb_desc_device_qualifier(const USBDescDevice *dev,
+                               uint8_t *dest, size_t len);
+-int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
+-int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
+-                         size_t len);
+-int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
+-int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
++int usb_desc_config(const USBDescConfig *conf, int flags,
++                    uint8_t *dest, size_t len);
++int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
++                         uint8_t *dest, size_t len);
++int usb_desc_iface(const USBDescIface *iface, int flags,
++                   uint8_t *dest, size_t len);
++int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
++                      uint8_t *dest, size_t len);
+ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
+ 
+ /* control message emulation helpers */
+-- 
+1.7.12
+
diff --git a/0343-usb3-bos-decriptor.patch b/0343-usb3-bos-decriptor.patch
new file mode 100644
index 0000000..3158fd0
--- /dev/null
+++ b/0343-usb3-bos-decriptor.patch
@@ -0,0 +1,215 @@
+From 52666569c1aa34531c101e005feccec0899c14e2 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 28 Aug 2012 17:46:29 +0200
+Subject: [PATCH 343/366] usb3: bos decriptor
+
+Add support for creating BOS descriptor and
+device cappability descriptors.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb.h      |   6 ++++
+ hw/usb/desc.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ hw/usb/desc.h |  25 ++++++++++++++
+ trace-events  |   1 +
+ 4 files changed, 141 insertions(+)
+
+diff --git a/hw/usb.h b/hw/usb.h
+index 78ffdf4..48c8926 100644
+--- a/hw/usb.h
++++ b/hw/usb.h
+@@ -135,10 +135,16 @@
+ #define USB_DT_OTHER_SPEED_CONFIG       0x07
+ #define USB_DT_DEBUG                    0x0A
+ #define USB_DT_INTERFACE_ASSOC          0x0B
++#define USB_DT_BOS                      0x0F
++#define USB_DT_DEVICE_CAPABILITY        0x10
+ #define USB_DT_CS_INTERFACE             0x24
+ #define USB_DT_CS_ENDPOINT              0x25
+ #define USB_DT_ENDPOINT_COMPANION       0x30
+ 
++#define USB_DEV_CAP_WIRELESS            0x01
++#define USB_DEV_CAP_USB2_EXT            0x02
++#define USB_DEV_CAP_SUPERSPEED          0x03
++
+ #define USB_ENDPOINT_XFER_CONTROL	0
+ #define USB_ENDPOINT_XFER_ISOC		1
+ #define USB_ENDPOINT_XFER_BULK		2
+diff --git a/hw/usb/desc.c b/hw/usb/desc.c
+index 8f5a8e5..1f12eae 100644
+--- a/hw/usb/desc.c
++++ b/hw/usb/desc.c
+@@ -258,6 +258,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
+     return bLength;
+ }
+ 
++static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len)
++{
++    uint8_t  bLength = 0x07;
++    USBDescriptor *d = (void *)dest;
++
++    if (len < bLength) {
++        return -1;
++    }
++
++    d->bLength                          = bLength;
++    d->bDescriptorType                  = USB_DT_DEVICE_CAPABILITY;
++    d->u.cap.bDevCapabilityType         = USB_DEV_CAP_USB2_EXT;
++
++    d->u.cap.u.usb2_ext.bmAttributes_1  = (1 << 1);  /* LPM */
++    d->u.cap.u.usb2_ext.bmAttributes_2  = 0;
++    d->u.cap.u.usb2_ext.bmAttributes_3  = 0;
++    d->u.cap.u.usb2_ext.bmAttributes_4  = 0;
++
++    return bLength;
++}
++
++static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len)
++{
++    uint8_t  bLength = 0x0a;
++    USBDescriptor *d = (void *)dest;
++
++    if (len < bLength) {
++        return -1;
++    }
++
++    d->bLength                           = bLength;
++    d->bDescriptorType                   = USB_DT_DEVICE_CAPABILITY;
++    d->u.cap.bDevCapabilityType          = USB_DEV_CAP_SUPERSPEED;
++
++    d->u.cap.u.super.bmAttributes        = 0;
++    d->u.cap.u.super.wSpeedsSupported_lo = 0;
++    d->u.cap.u.super.wSpeedsSupported_hi = 0;
++    d->u.cap.u.super.bFunctionalitySupport = 0;
++    d->u.cap.u.super.bU1DevExitLat       = 0x0a;
++    d->u.cap.u.super.wU2DevExitLat_lo    = 0x20;
++    d->u.cap.u.super.wU2DevExitLat_hi    = 0;
++
++    if (desc->full) {
++        d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1);
++        d->u.cap.u.super.bFunctionalitySupport = 1;
++    }
++    if (desc->high) {
++        d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2);
++        if (!d->u.cap.u.super.bFunctionalitySupport) {
++            d->u.cap.u.super.bFunctionalitySupport = 2;
++        }
++    }
++    if (desc->super) {
++        d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3);
++        if (!d->u.cap.u.super.bFunctionalitySupport) {
++            d->u.cap.u.super.bFunctionalitySupport = 3;
++        }
++    }
++
++    return bLength;
++}
++
++static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len)
++{
++    uint8_t  bLength = 0x05;
++    uint16_t wTotalLength = 0;
++    uint8_t  bNumDeviceCaps = 0;
++    USBDescriptor *d = (void *)dest;
++    int rc;
++
++    if (len < bLength) {
++        return -1;
++    }
++
++    d->bLength                      = bLength;
++    d->bDescriptorType              = USB_DT_BOS;
++
++    wTotalLength += bLength;
++
++    if (desc->high != NULL) {
++        rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength,
++                                   len - wTotalLength);
++        if (rc < 0) {
++            return rc;
++        }
++        wTotalLength += rc;
++        bNumDeviceCaps++;
++    }
++
++    if (desc->super != NULL) {
++        rc = usb_desc_cap_super(desc, dest + wTotalLength,
++                                len - wTotalLength);
++        if (rc < 0) {
++            return rc;
++        }
++        wTotalLength += rc;
++        bNumDeviceCaps++;
++    }
++
++    d->u.bos.wTotalLength_lo = usb_lo(wTotalLength);
++    d->u.bos.wTotalLength_hi = usb_hi(wTotalLength);
++    d->u.bos.bNumDeviceCaps  = bNumDeviceCaps;
++    return wTotalLength;
++}
++
+ /* ------------------------------------------------------------------ */
+ 
+ static void usb_desc_ep_init(USBDevice *dev)
+@@ -571,6 +676,10 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
+         }
+         trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
+         break;
++    case USB_DT_BOS:
++        ret = usb_desc_bos(desc, buf, sizeof(buf));
++        trace_usb_desc_bos(dev->addr, len, ret);
++        break;
+ 
+     case USB_DT_DEBUG:
+         /* ignore silently */
+diff --git a/hw/usb/desc.h b/hw/usb/desc.h
+index 4b5e88d..68bb570 100644
+--- a/hw/usb/desc.h
++++ b/hw/usb/desc.h
+@@ -69,6 +69,31 @@ typedef struct USBDescriptor {
+             uint8_t           wBytesPerInterval_lo;
+             uint8_t           wBytesPerInterval_hi;
+         } super_endpoint;
++        struct {
++            uint8_t           wTotalLength_lo;
++            uint8_t           wTotalLength_hi;
++            uint8_t           bNumDeviceCaps;
++        } bos;
++        struct {
++            uint8_t           bDevCapabilityType;
++            union {
++                struct {
++                    uint8_t   bmAttributes_1;
++                    uint8_t   bmAttributes_2;
++                    uint8_t   bmAttributes_3;
++                    uint8_t   bmAttributes_4;
++                } usb2_ext;
++                struct {
++                    uint8_t   bmAttributes;
++                    uint8_t   wSpeedsSupported_lo;
++                    uint8_t   wSpeedsSupported_hi;
++                    uint8_t   bFunctionalitySupport;
++                    uint8_t   bU1DevExitLat;
++                    uint8_t   wU2DevExitLat_lo;
++                    uint8_t   wU2DevExitLat_hi;
++                } super;
++            } u;
++        } cap;
+     } u;
+ } QEMU_PACKED USBDescriptor;
+ 
+diff --git a/trace-events b/trace-events
+index a894689..5bc591a 100644
+--- a/trace-events
++++ b/trace-events
+@@ -340,6 +340,7 @@ usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device quali
+ usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+ usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+ usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
++usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d"
+ usb_set_addr(int addr) "dev %d"
+ usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
+ usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d"
+-- 
+1.7.12
+
diff --git a/0344-usb-storage-usb3-support.patch b/0344-usb-storage-usb3-support.patch
new file mode 100644
index 0000000..b50fcdd
--- /dev/null
+++ b/0344-usb-storage-usb3-support.patch
@@ -0,0 +1,94 @@
+From 4a6f1fdf12d8b03633dad54dadc12781a77fbf6b Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 28 Aug 2012 17:29:15 +0200
+Subject: [PATCH 344/366] usb-storage: usb3 support
+
+Add usb3 descriptors to usb-storage, so it shows up as superspeed
+device when connected to xhci.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/dev-storage.c | 46 +++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 43 insertions(+), 3 deletions(-)
+
+diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
+index ff48d91..e732191 100644
+--- a/hw/usb/dev-storage.c
++++ b/hw/usb/dev-storage.c
+@@ -78,6 +78,7 @@ enum {
+     STR_SERIALNUMBER,
+     STR_CONFIG_FULL,
+     STR_CONFIG_HIGH,
++    STR_CONFIG_SUPER,
+ };
+ 
+ static const USBDescStrings desc_strings = {
+@@ -86,6 +87,7 @@ static const USBDescStrings desc_strings = {
+     [STR_SERIALNUMBER] = "1",
+     [STR_CONFIG_FULL]  = "Full speed config (usb 1.1)",
+     [STR_CONFIG_HIGH]  = "High speed config (usb 2.0)",
++    [STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
+ };
+ 
+ static const USBDescIface desc_iface_full = {
+@@ -158,6 +160,43 @@ static const USBDescDevice desc_device_high = {
+     },
+ };
+ 
++static const USBDescIface desc_iface_super = {
++    .bInterfaceNumber              = 0,
++    .bNumEndpoints                 = 2,
++    .bInterfaceClass               = USB_CLASS_MASS_STORAGE,
++    .bInterfaceSubClass            = 0x06, /* SCSI */
++    .bInterfaceProtocol            = 0x50, /* Bulk */
++    .eps = (USBDescEndpoint[]) {
++        {
++            .bEndpointAddress      = USB_DIR_IN | 0x01,
++            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
++            .wMaxPacketSize        = 1024,
++            .bMaxBurst             = 15,
++        },{
++            .bEndpointAddress      = USB_DIR_OUT | 0x02,
++            .bmAttributes          = USB_ENDPOINT_XFER_BULK,
++            .wMaxPacketSize        = 1024,
++            .bMaxBurst             = 15,
++        },
++    }
++};
++
++static const USBDescDevice desc_device_super = {
++    .bcdUSB                        = 0x0300,
++    .bMaxPacketSize0               = 9,
++    .bNumConfigurations            = 1,
++    .confs = (USBDescConfig[]) {
++        {
++            .bNumInterfaces        = 1,
++            .bConfigurationValue   = 1,
++            .iConfiguration        = STR_CONFIG_SUPER,
++            .bmAttributes          = 0xc0,
++            .nif = 1,
++            .ifs = &desc_iface_super,
++        },
++    },
++};
++
+ static const USBDesc desc = {
+     .id = {
+         .idVendor          = 0x46f4, /* CRC16() of "QEMU" */
+@@ -167,9 +206,10 @@ static const USBDesc desc = {
+         .iProduct          = STR_PRODUCT,
+         .iSerialNumber     = STR_SERIALNUMBER,
+     },
+-    .full = &desc_device_full,
+-    .high = &desc_device_high,
+-    .str  = desc_strings,
++    .full  = &desc_device_full,
++    .high  = &desc_device_high,
++    .super = &desc_device_super,
++    .str   = desc_strings,
+ };
+ 
+ static void usb_msd_copy_data(MSDState *s, USBPacket *p)
+-- 
+1.7.12
+
diff --git a/0345-xhci-fix-cleanup-msi.patch b/0345-xhci-fix-cleanup-msi.patch
new file mode 100644
index 0000000..5f25cbd
--- /dev/null
+++ b/0345-xhci-fix-cleanup-msi.patch
@@ -0,0 +1,96 @@
+From 3ed11ea2c3b6d5db29246b4da105902aa0346d65 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 30 Aug 2012 10:57:12 +0200
+Subject: [PATCH 345/366] xhci: fix & cleanup msi.
+
+Drop custom write_config function which isn't needed any more.
+Make the msi property a bit property so it accepts 'on' & 'off'.
+Enable MSI by default.
+
+TODO: add compat property to disable on old machine types.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 27 +++++++++------------------
+ 1 file changed, 9 insertions(+), 18 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index e08312e..e1d5d2a 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -386,7 +386,7 @@ struct XHCIState {
+     /* properties */
+     uint32_t numports_2;
+     uint32_t numports_3;
+-    uint32_t msi;
++    uint32_t flags;
+ 
+     /* Operational Registers */
+     uint32_t usbcmd;
+@@ -435,6 +435,10 @@ typedef struct XHCIEvRingSeg {
+     uint32_t rsvd;
+ } XHCIEvRingSeg;
+ 
++enum xhci_flags {
++    XHCI_FLAG_USE_MSI = 1,
++};
++
+ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+                          unsigned int epid);
+ static void xhci_event(XHCIState *xhci, XHCIEvent *event);
+@@ -617,7 +621,7 @@ static void xhci_irq_update(XHCIState *xhci)
+         level = 1;
+     }
+ 
+-    if (xhci->msi && msi_enabled(&xhci->pci_dev)) {
++    if (msi_enabled(&xhci->pci_dev)) {
+         if (level) {
+             trace_usb_xhci_irq_msi(0);
+             msi_notify(&xhci->pci_dev, 0);
+@@ -2859,32 +2863,20 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
+     ret = pcie_cap_init(&xhci->pci_dev, 0xa0, PCI_EXP_TYPE_ENDPOINT, 0);
+     assert(ret >= 0);
+ 
+-    if (xhci->msi) {
+-        ret = msi_init(&xhci->pci_dev, 0x70, 1, true, false);
+-        assert(ret >= 0);
++    if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
++        msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false);
+     }
+ 
+     return 0;
+ }
+ 
+-static void xhci_write_config(PCIDevice *dev, uint32_t addr, uint32_t val,
+-                              int len)
+-{
+-    XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev);
+-
+-    pci_default_write_config(dev, addr, val, len);
+-    if (xhci->msi) {
+-        msi_write_config(dev, addr, val, len);
+-    }
+-}
+-
+ static const VMStateDescription vmstate_xhci = {
+     .name = "xhci",
+     .unmigratable = 1,
+ };
+ 
+ static Property xhci_properties[] = {
+-    DEFINE_PROP_UINT32("msi", XHCIState, msi, 0),
++    DEFINE_PROP_BIT("msi",    XHCIState, flags, XHCI_FLAG_USE_MSI, true),
+     DEFINE_PROP_UINT32("p2",  XHCIState, numports_2, 4),
+     DEFINE_PROP_UINT32("p3",  XHCIState, numports_3, 4),
+     DEFINE_PROP_END_OF_LIST(),
+@@ -2904,7 +2896,6 @@ static void xhci_class_init(ObjectClass *klass, void *data)
+     k->class_id     = PCI_CLASS_SERIAL_USB;
+     k->revision     = 0x03;
+     k->is_express   = 1;
+-    k->config_write = xhci_write_config;
+ }
+ 
+ static TypeInfo xhci_info = {
+-- 
+1.7.12
+
diff --git a/0346-xhci-rework-interrupt-handling.patch b/0346-xhci-rework-interrupt-handling.patch
new file mode 100644
index 0000000..b0ea1df
--- /dev/null
+++ b/0346-xhci-rework-interrupt-handling.patch
@@ -0,0 +1,117 @@
+From da881a3422ae525f1d0a24f61117a47473261037 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 30 Aug 2012 13:05:10 +0200
+Subject: [PATCH 346/366] xhci: rework interrupt handling
+
+Split xhci_irq_update into a function which handles intx updates
+(including lowering the irq line once the guests acks the interrupt)
+and one which is used for raising an irq only.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 47 +++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 33 insertions(+), 14 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index e1d5d2a..5eae32e 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -612,24 +612,43 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
+     return &xhci->ports[index];
+ }
+ 
+-static void xhci_irq_update(XHCIState *xhci)
++static void xhci_intx_update(XHCIState *xhci)
+ {
+     int level = 0;
+ 
+-    if (xhci->iman & IMAN_IP && xhci->iman & IMAN_IE &&
++    if (msi_enabled(&xhci->pci_dev)) {
++        return;
++    }
++
++    if (xhci->iman & IMAN_IP &&
++        xhci->iman & IMAN_IE &&
+         xhci->usbcmd & USBCMD_INTE) {
+         level = 1;
+     }
+ 
++    trace_usb_xhci_irq_intx(level);
++    qemu_set_irq(xhci->irq, level);
++}
++
++static void xhci_intr_raise(XHCIState *xhci)
++{
++    if (!(xhci->iman & IMAN_IP) ||
++        !(xhci->iman & IMAN_IE)) {
++        return;
++    }
++
++    if (!(xhci->usbcmd & USBCMD_INTE)) {
++        return;
++    }
++
+     if (msi_enabled(&xhci->pci_dev)) {
+-        if (level) {
+-            trace_usb_xhci_irq_msi(0);
+-            msi_notify(&xhci->pci_dev, 0);
+-        }
+-    } else {
+-        trace_usb_xhci_irq_intx(level);
+-        qemu_set_irq(xhci->irq, level);
++        trace_usb_xhci_irq_msi(0);
++        msi_notify(&xhci->pci_dev, 0);
++        return;
+     }
++
++    trace_usb_xhci_irq_intx(1);
++    qemu_set_irq(xhci->irq, 1);
+ }
+ 
+ static inline int xhci_running(XHCIState *xhci)
+@@ -732,7 +751,7 @@ static void xhci_events_update(XHCIState *xhci)
+         xhci->erdp_low |= ERDP_EHB;
+         xhci->iman |= IMAN_IP;
+         xhci->usbsts |= USBSTS_EINT;
+-        xhci_irq_update(xhci);
++        xhci_intr_raise(xhci);
+     }
+ 
+     if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) {
+@@ -796,7 +815,7 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event)
+     xhci->iman |= IMAN_IP;
+     xhci->usbsts |= USBSTS_EINT;
+ 
+-    xhci_irq_update(xhci);
++    xhci_intr_raise(xhci);
+ }
+ 
+ static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
+@@ -2481,13 +2500,13 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         if (val & USBCMD_HCRST) {
+             xhci_reset(&xhci->pci_dev.qdev);
+         }
+-        xhci_irq_update(xhci);
++        xhci_intx_update(xhci);
+         break;
+ 
+     case 0x04: /* USBSTS */
+         /* these bits are write-1-to-clear */
+         xhci->usbsts &= ~(val & (USBSTS_HSE|USBSTS_EINT|USBSTS_PCD|USBSTS_SRE));
+-        xhci_irq_update(xhci);
++        xhci_intx_update(xhci);
+         break;
+ 
+     case 0x14: /* DNCTRL */
+@@ -2572,7 +2591,7 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         }
+         xhci->iman &= ~IMAN_IE;
+         xhci->iman |= val & IMAN_IE;
+-        xhci_irq_update(xhci);
++        xhci_intx_update(xhci);
+         break;
+     case 0x24: /* IMOD */
+         xhci->imod = val;
+-- 
+1.7.12
+
diff --git a/0347-xhci-add-msix-support.patch b/0347-xhci-add-msix-support.patch
new file mode 100644
index 0000000..5110aaa
--- /dev/null
+++ b/0347-xhci-add-msix-support.patch
@@ -0,0 +1,156 @@
+From 03ab86ecf677d777864a3643ec8479037d3f41cd Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 30 Aug 2012 12:06:59 +0200
+Subject: [PATCH 347/366] xhci: add msix support
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++-
+ trace-events      |  3 +++
+ 2 files changed, 49 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 5eae32e..3bac99a 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -23,6 +23,7 @@
+ #include "hw/usb.h"
+ #include "hw/pci.h"
+ #include "hw/msi.h"
++#include "hw/msix.h"
+ #include "trace.h"
+ 
+ //#define DEBUG_XHCI
+@@ -59,6 +60,8 @@
+ #define OFF_OPER        LEN_CAP
+ #define OFF_RUNTIME     0x1000
+ #define OFF_DOORBELL    0x2000
++#define OFF_MSIX_TABLE  0x3000
++#define OFF_MSIX_PBA    0x3800
+ /* must be power of 2 */
+ #define LEN_REGS        0x4000
+ 
+@@ -411,6 +414,7 @@ struct XHCIState {
+     uint32_t erstba_high;
+     uint32_t erdp_low;
+     uint32_t erdp_high;
++    bool     msix_used;
+ 
+     int64_t mfindex_start;
+     QEMUTimer *mfwrap_timer;
+@@ -437,6 +441,7 @@ typedef struct XHCIEvRingSeg {
+ 
+ enum xhci_flags {
+     XHCI_FLAG_USE_MSI = 1,
++    XHCI_FLAG_USE_MSI_X,
+ };
+ 
+ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+@@ -616,7 +621,8 @@ static void xhci_intx_update(XHCIState *xhci)
+ {
+     int level = 0;
+ 
+-    if (msi_enabled(&xhci->pci_dev)) {
++    if (msix_enabled(&xhci->pci_dev) ||
++        msi_enabled(&xhci->pci_dev)) {
+         return;
+     }
+ 
+@@ -630,6 +636,30 @@ static void xhci_intx_update(XHCIState *xhci)
+     qemu_set_irq(xhci->irq, level);
+ }
+ 
++static void xhci_msix_update(XHCIState *xhci)
++{
++    bool enabled;
++
++    if (!msix_enabled(&xhci->pci_dev)) {
++        return;
++    }
++
++    enabled = xhci->iman & IMAN_IE;
++    if (enabled == xhci->msix_used) {
++        return;
++    }
++
++    if (enabled) {
++        trace_usb_xhci_irq_msix_use(0);
++        msix_vector_use(&xhci->pci_dev, 0);
++        xhci->msix_used = true;
++    } else {
++        trace_usb_xhci_irq_msix_unuse(0);
++        msix_vector_unuse(&xhci->pci_dev, 0);
++        xhci->msix_used = false;
++    }
++}
++
+ static void xhci_intr_raise(XHCIState *xhci)
+ {
+     if (!(xhci->iman & IMAN_IP) ||
+@@ -641,6 +671,12 @@ static void xhci_intr_raise(XHCIState *xhci)
+         return;
+     }
+ 
++    if (msix_enabled(&xhci->pci_dev)) {
++        trace_usb_xhci_irq_msix(0);
++        msix_notify(&xhci->pci_dev, 0);
++        return;
++    }
++
+     if (msi_enabled(&xhci->pci_dev)) {
+         trace_usb_xhci_irq_msi(0);
+         msi_notify(&xhci->pci_dev, 0);
+@@ -2284,6 +2320,7 @@ static void xhci_reset(DeviceState *dev)
+     xhci->erstba_high = 0;
+     xhci->erdp_low = 0;
+     xhci->erdp_high = 0;
++    xhci->msix_used = 0;
+ 
+     xhci->er_ep_idx = 0;
+     xhci->er_pcs = 1;
+@@ -2592,6 +2629,7 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         xhci->iman &= ~IMAN_IE;
+         xhci->iman |= val & IMAN_IE;
+         xhci_intx_update(xhci);
++        xhci_msix_update(xhci);
+         break;
+     case 0x24: /* IMOD */
+         xhci->imod = val;
+@@ -2885,6 +2923,12 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
+     if (xhci->flags & (1 << XHCI_FLAG_USE_MSI)) {
+         msi_init(&xhci->pci_dev, 0x70, MAXINTRS, true, false);
+     }
++    if (xhci->flags & (1 << XHCI_FLAG_USE_MSI_X)) {
++        msix_init(&xhci->pci_dev, MAXINTRS,
++                  &xhci->mem, 0, OFF_MSIX_TABLE,
++                  &xhci->mem, 0, OFF_MSIX_PBA,
++                  0x90);
++    }
+ 
+     return 0;
+ }
+@@ -2896,6 +2940,7 @@ static const VMStateDescription vmstate_xhci = {
+ 
+ static Property xhci_properties[] = {
+     DEFINE_PROP_BIT("msi",    XHCIState, flags, XHCI_FLAG_USE_MSI, true),
++    DEFINE_PROP_BIT("msix",   XHCIState, flags, XHCI_FLAG_USE_MSI_X, true),
+     DEFINE_PROP_UINT32("p2",  XHCIState, numports_2, 4),
+     DEFINE_PROP_UINT32("p3",  XHCIState, numports_3, 4),
+     DEFINE_PROP_END_OF_LIST(),
+diff --git a/trace-events b/trace-events
+index 5bc591a..8589ca4 100644
+--- a/trace-events
++++ b/trace-events
+@@ -313,6 +313,9 @@ usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
+ usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
+ usb_xhci_irq_intx(uint32_t level) "level %d"
+ usb_xhci_irq_msi(uint32_t nr) "nr %d"
++usb_xhci_irq_msix(uint32_t nr) "nr %d"
++usb_xhci_irq_msix_use(uint32_t nr) "nr %d"
++usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d"
+ usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+ usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+ usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
+-- 
+1.7.12
+
diff --git a/0348-xhci-move-register-update-into-xhci_intr_raise.patch b/0348-xhci-move-register-update-into-xhci_intr_raise.patch
new file mode 100644
index 0000000..60537da
--- /dev/null
+++ b/0348-xhci-move-register-update-into-xhci_intr_raise.patch
@@ -0,0 +1,55 @@
+From c7ca31b2f54b945ba4babf8ad329c938462c75f5 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 30 Aug 2012 14:04:04 +0200
+Subject: [PATCH 348/366] xhci: move register update into xhci_intr_raise
+
+Now that we have a separate function to raise an IRQ we can move
+some comon code into the function.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 14 +++++---------
+ 1 file changed, 5 insertions(+), 9 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 3bac99a..e39fe04 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -662,8 +662,11 @@ static void xhci_msix_update(XHCIState *xhci)
+ 
+ static void xhci_intr_raise(XHCIState *xhci)
+ {
+-    if (!(xhci->iman & IMAN_IP) ||
+-        !(xhci->iman & IMAN_IE)) {
++    xhci->erdp_low |= ERDP_EHB;
++    xhci->iman |= IMAN_IP;
++    xhci->usbsts |= USBSTS_EINT;
++
++    if (!(xhci->iman & IMAN_IE)) {
+         return;
+     }
+ 
+@@ -784,9 +787,6 @@ static void xhci_events_update(XHCIState *xhci)
+     }
+ 
+     if (do_irq) {
+-        xhci->erdp_low |= ERDP_EHB;
+-        xhci->iman |= IMAN_IP;
+-        xhci->usbsts |= USBSTS_EINT;
+         xhci_intr_raise(xhci);
+     }
+ 
+@@ -847,10 +847,6 @@ static void xhci_event(XHCIState *xhci, XHCIEvent *event)
+         xhci_write_event(xhci, event);
+     }
+ 
+-    xhci->erdp_low |= ERDP_EHB;
+-    xhci->iman |= IMAN_IP;
+-    xhci->usbsts |= USBSTS_EINT;
+-
+     xhci_intr_raise(xhci);
+ }
+ 
+-- 
+1.7.12
+
diff --git a/0349-xhci-add-XHCIInterrupter.patch b/0349-xhci-add-XHCIInterrupter.patch
new file mode 100644
index 0000000..3ad7de3
--- /dev/null
+++ b/0349-xhci-add-XHCIInterrupter.patch
@@ -0,0 +1,642 @@
+From 84ce3d5155c984ad131ba7153b9c3646c88b8636 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 30 Aug 2012 15:49:03 +0200
+Subject: [PATCH 349/366] xhci: add XHCIInterrupter
+
+Move all state belonging to the (single) interrupter into a separate
+struct.  First step in adding support for multiple interrupters.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 307 ++++++++++++++++++++++++++++--------------------------
+ trace-events      |   2 +-
+ 2 files changed, 161 insertions(+), 148 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index e39fe04..ddc3825 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -378,6 +378,27 @@ typedef struct XHCIEvent {
+     uint8_t epid;
+ } XHCIEvent;
+ 
++typedef struct XHCIInterrupter {
++    uint32_t iman;
++    uint32_t imod;
++    uint32_t erstsz;
++    uint32_t erstba_low;
++    uint32_t erstba_high;
++    uint32_t erdp_low;
++    uint32_t erdp_high;
++
++    bool msix_used, er_pcs, er_full;
++
++    dma_addr_t er_start;
++    uint32_t er_size;
++    unsigned int er_ep_idx;
++
++    XHCIEvent ev_buffer[EV_QUEUE];
++    unsigned int ev_buffer_put;
++    unsigned int ev_buffer_get;
++
++} XHCIInterrupter;
++
+ struct XHCIState {
+     PCIDevice pci_dev;
+     USBBus bus;
+@@ -407,27 +428,9 @@ struct XHCIState {
+     uint32_t numports;
+ 
+     /* Runtime Registers */
+-    uint32_t iman;
+-    uint32_t imod;
+-    uint32_t erstsz;
+-    uint32_t erstba_low;
+-    uint32_t erstba_high;
+-    uint32_t erdp_low;
+-    uint32_t erdp_high;
+-    bool     msix_used;
+-
+     int64_t mfindex_start;
+     QEMUTimer *mfwrap_timer;
+-
+-    dma_addr_t er_start;
+-    uint32_t er_size;
+-    bool er_pcs;
+-    unsigned int er_ep_idx;
+-    bool er_full;
+-
+-    XHCIEvent ev_buffer[EV_QUEUE];
+-    unsigned int ev_buffer_put;
+-    unsigned int ev_buffer_get;
++    XHCIInterrupter intr[MAXINTRS];
+ 
+     XHCIRing cmd_ring;
+ };
+@@ -446,8 +449,8 @@ enum xhci_flags {
+ 
+ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
+                          unsigned int epid);
+-static void xhci_event(XHCIState *xhci, XHCIEvent *event);
+-static void xhci_write_event(XHCIState *xhci, XHCIEvent *event);
++static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v);
++static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v);
+ 
+ static const char *TRBType_names[] = {
+     [TRB_RESERVED]                     = "TRB_RESERVED",
+@@ -573,7 +576,7 @@ static void xhci_mfwrap_timer(void *opaque)
+     XHCIState *xhci = opaque;
+     XHCIEvent wrap = { ER_MFINDEX_WRAP, CC_SUCCESS };
+ 
+-    xhci_event(xhci, &wrap);
++    xhci_event(xhci, &wrap, 0);
+     xhci_mfwrap_update(xhci);
+ }
+ 
+@@ -626,8 +629,8 @@ static void xhci_intx_update(XHCIState *xhci)
+         return;
+     }
+ 
+-    if (xhci->iman & IMAN_IP &&
+-        xhci->iman & IMAN_IE &&
++    if (xhci->intr[0].iman & IMAN_IP &&
++        xhci->intr[0].iman & IMAN_IE &&
+         xhci->usbcmd & USBCMD_INTE) {
+         level = 1;
+     }
+@@ -636,7 +639,7 @@ static void xhci_intx_update(XHCIState *xhci)
+     qemu_set_irq(xhci->irq, level);
+ }
+ 
+-static void xhci_msix_update(XHCIState *xhci)
++static void xhci_msix_update(XHCIState *xhci, int v)
+ {
+     bool enabled;
+ 
+@@ -644,29 +647,29 @@ static void xhci_msix_update(XHCIState *xhci)
+         return;
+     }
+ 
+-    enabled = xhci->iman & IMAN_IE;
+-    if (enabled == xhci->msix_used) {
++    enabled = xhci->intr[v].iman & IMAN_IE;
++    if (enabled == xhci->intr[v].msix_used) {
+         return;
+     }
+ 
+     if (enabled) {
+-        trace_usb_xhci_irq_msix_use(0);
+-        msix_vector_use(&xhci->pci_dev, 0);
+-        xhci->msix_used = true;
++        trace_usb_xhci_irq_msix_use(v);
++        msix_vector_use(&xhci->pci_dev, v);
++        xhci->intr[v].msix_used = true;
+     } else {
+-        trace_usb_xhci_irq_msix_unuse(0);
+-        msix_vector_unuse(&xhci->pci_dev, 0);
+-        xhci->msix_used = false;
++        trace_usb_xhci_irq_msix_unuse(v);
++        msix_vector_unuse(&xhci->pci_dev, v);
++        xhci->intr[v].msix_used = false;
+     }
+ }
+ 
+-static void xhci_intr_raise(XHCIState *xhci)
++static void xhci_intr_raise(XHCIState *xhci, int v)
+ {
+-    xhci->erdp_low |= ERDP_EHB;
+-    xhci->iman |= IMAN_IP;
++    xhci->intr[v].erdp_low |= ERDP_EHB;
++    xhci->intr[v].iman |= IMAN_IP;
+     xhci->usbsts |= USBSTS_EINT;
+ 
+-    if (!(xhci->iman & IMAN_IE)) {
++    if (!(xhci->intr[v].iman & IMAN_IE)) {
+         return;
+     }
+ 
+@@ -675,24 +678,26 @@ static void xhci_intr_raise(XHCIState *xhci)
+     }
+ 
+     if (msix_enabled(&xhci->pci_dev)) {
+-        trace_usb_xhci_irq_msix(0);
+-        msix_notify(&xhci->pci_dev, 0);
++        trace_usb_xhci_irq_msix(v);
++        msix_notify(&xhci->pci_dev, v);
+         return;
+     }
+ 
+     if (msi_enabled(&xhci->pci_dev)) {
+-        trace_usb_xhci_irq_msi(0);
+-        msi_notify(&xhci->pci_dev, 0);
++        trace_usb_xhci_irq_msi(v);
++        msi_notify(&xhci->pci_dev, v);
+         return;
+     }
+ 
+-    trace_usb_xhci_irq_intx(1);
+-    qemu_set_irq(xhci->irq, 1);
++    if (v == 0) {
++        trace_usb_xhci_irq_intx(1);
++        qemu_set_irq(xhci->irq, 1);
++    }
+ }
+ 
+ static inline int xhci_running(XHCIState *xhci)
+ {
+-    return !(xhci->usbsts & USBSTS_HCH) && !xhci->er_full;
++    return !(xhci->usbsts & USBSTS_HCH) && !xhci->intr[0].er_full;
+ }
+ 
+ static void xhci_die(XHCIState *xhci)
+@@ -701,8 +706,9 @@ static void xhci_die(XHCIState *xhci)
+     fprintf(stderr, "xhci: asserted controller error\n");
+ }
+ 
+-static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
++static void xhci_write_event(XHCIState *xhci, XHCIEvent *event, int v)
+ {
++    XHCIInterrupter *intr = &xhci->intr[v];
+     XHCITRB ev_trb;
+     dma_addr_t addr;
+ 
+@@ -710,27 +716,28 @@ static void xhci_write_event(XHCIState *xhci, XHCIEvent *event)
+     ev_trb.status = cpu_to_le32(event->length | (event->ccode << 24));
+     ev_trb.control = (event->slotid << 24) | (event->epid << 16) |
+                      event->flags | (event->type << TRB_TYPE_SHIFT);
+-    if (xhci->er_pcs) {
++    if (intr->er_pcs) {
+         ev_trb.control |= TRB_C;
+     }
+     ev_trb.control = cpu_to_le32(ev_trb.control);
+ 
+-    trace_usb_xhci_queue_event(xhci->er_ep_idx, trb_name(&ev_trb),
++    trace_usb_xhci_queue_event(v, intr->er_ep_idx, trb_name(&ev_trb),
+                                event_name(event), ev_trb.parameter,
+                                ev_trb.status, ev_trb.control);
+ 
+-    addr = xhci->er_start + TRB_SIZE*xhci->er_ep_idx;
++    addr = intr->er_start + TRB_SIZE*intr->er_ep_idx;
+     pci_dma_write(&xhci->pci_dev, addr, &ev_trb, TRB_SIZE);
+ 
+-    xhci->er_ep_idx++;
+-    if (xhci->er_ep_idx >= xhci->er_size) {
+-        xhci->er_ep_idx = 0;
+-        xhci->er_pcs = !xhci->er_pcs;
++    intr->er_ep_idx++;
++    if (intr->er_ep_idx >= intr->er_size) {
++        intr->er_ep_idx = 0;
++        intr->er_pcs = !intr->er_pcs;
+     }
+ }
+ 
+-static void xhci_events_update(XHCIState *xhci)
++static void xhci_events_update(XHCIState *xhci, int v)
+ {
++    XHCIInterrupter *intr = &xhci->intr[v];
+     dma_addr_t erdp;
+     unsigned int dp_idx;
+     bool do_irq = 0;
+@@ -739,115 +746,116 @@ static void xhci_events_update(XHCIState *xhci)
+         return;
+     }
+ 
+-    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
+-    if (erdp < xhci->er_start ||
+-        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
++    erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
++    if (erdp < intr->er_start ||
++        erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
+         fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp);
+-        fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n",
+-                xhci->er_start, xhci->er_size);
++        fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n",
++                v, intr->er_start, intr->er_size);
+         xhci_die(xhci);
+         return;
+     }
+-    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
+-    assert(dp_idx < xhci->er_size);
++    dp_idx = (erdp - intr->er_start) / TRB_SIZE;
++    assert(dp_idx < intr->er_size);
+ 
+     /* NEC didn't read section 4.9.4 of the spec (v1.0 p139 top Note) and thus
+      * deadlocks when the ER is full. Hack it by holding off events until
+      * the driver decides to free at least half of the ring */
+-    if (xhci->er_full) {
+-        int er_free = dp_idx - xhci->er_ep_idx;
++    if (intr->er_full) {
++        int er_free = dp_idx - intr->er_ep_idx;
+         if (er_free <= 0) {
+-            er_free += xhci->er_size;
++            er_free += intr->er_size;
+         }
+-        if (er_free < (xhci->er_size/2)) {
++        if (er_free < (intr->er_size/2)) {
+             DPRINTF("xhci_events_update(): event ring still "
+                     "more than half full (hack)\n");
+             return;
+         }
+     }
+ 
+-    while (xhci->ev_buffer_put != xhci->ev_buffer_get) {
+-        assert(xhci->er_full);
+-        if (((xhci->er_ep_idx+1) % xhci->er_size) == dp_idx) {
++    while (intr->ev_buffer_put != intr->ev_buffer_get) {
++        assert(intr->er_full);
++        if (((intr->er_ep_idx+1) % intr->er_size) == dp_idx) {
+             DPRINTF("xhci_events_update(): event ring full again\n");
+ #ifndef ER_FULL_HACK
+             XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
+-            xhci_write_event(xhci, &full);
++            xhci_write_event(xhci, &full, v);
+ #endif
+             do_irq = 1;
+             break;
+         }
+-        XHCIEvent *event = &xhci->ev_buffer[xhci->ev_buffer_get];
+-        xhci_write_event(xhci, event);
+-        xhci->ev_buffer_get++;
++        XHCIEvent *event = &intr->ev_buffer[intr->ev_buffer_get];
++        xhci_write_event(xhci, event, v);
++        intr->ev_buffer_get++;
+         do_irq = 1;
+-        if (xhci->ev_buffer_get == EV_QUEUE) {
+-            xhci->ev_buffer_get = 0;
++        if (intr->ev_buffer_get == EV_QUEUE) {
++            intr->ev_buffer_get = 0;
+         }
+     }
+ 
+     if (do_irq) {
+-        xhci_intr_raise(xhci);
++        xhci_intr_raise(xhci, v);
+     }
+ 
+-    if (xhci->er_full && xhci->ev_buffer_put == xhci->ev_buffer_get) {
++    if (intr->er_full && intr->ev_buffer_put == intr->ev_buffer_get) {
+         DPRINTF("xhci_events_update(): event ring no longer full\n");
+-        xhci->er_full = 0;
++        intr->er_full = 0;
+     }
+     return;
+ }
+ 
+-static void xhci_event(XHCIState *xhci, XHCIEvent *event)
++static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
+ {
++    XHCIInterrupter *intr = &xhci->intr[v];
+     dma_addr_t erdp;
+     unsigned int dp_idx;
+ 
+-    if (xhci->er_full) {
++    if (intr->er_full) {
+         DPRINTF("xhci_event(): ER full, queueing\n");
+-        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
++        if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
+             fprintf(stderr, "xhci: event queue full, dropping event!\n");
+             return;
+         }
+-        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
+-        if (xhci->ev_buffer_put == EV_QUEUE) {
+-            xhci->ev_buffer_put = 0;
++        intr->ev_buffer[intr->ev_buffer_put++] = *event;
++        if (intr->ev_buffer_put == EV_QUEUE) {
++            intr->ev_buffer_put = 0;
+         }
+         return;
+     }
+ 
+-    erdp = xhci_addr64(xhci->erdp_low, xhci->erdp_high);
+-    if (erdp < xhci->er_start ||
+-        erdp >= (xhci->er_start + TRB_SIZE*xhci->er_size)) {
++    erdp = xhci_addr64(intr->erdp_low, intr->erdp_high);
++    if (erdp < intr->er_start ||
++        erdp >= (intr->er_start + TRB_SIZE*intr->er_size)) {
+         fprintf(stderr, "xhci: ERDP out of bounds: "DMA_ADDR_FMT"\n", erdp);
+-        fprintf(stderr, "xhci: ER at "DMA_ADDR_FMT" len %d\n",
+-                xhci->er_start, xhci->er_size);
++        fprintf(stderr, "xhci: ER[%d] at "DMA_ADDR_FMT" len %d\n",
++                v, intr->er_start, intr->er_size);
+         xhci_die(xhci);
+         return;
+     }
+ 
+-    dp_idx = (erdp - xhci->er_start) / TRB_SIZE;
+-    assert(dp_idx < xhci->er_size);
++    dp_idx = (erdp - intr->er_start) / TRB_SIZE;
++    assert(dp_idx < intr->er_size);
+ 
+-    if ((xhci->er_ep_idx+1) % xhci->er_size == dp_idx) {
++    if ((intr->er_ep_idx+1) % intr->er_size == dp_idx) {
+         DPRINTF("xhci_event(): ER full, queueing\n");
+ #ifndef ER_FULL_HACK
+         XHCIEvent full = {ER_HOST_CONTROLLER, CC_EVENT_RING_FULL_ERROR};
+         xhci_write_event(xhci, &full);
+ #endif
+-        xhci->er_full = 1;
+-        if (((xhci->ev_buffer_put+1) % EV_QUEUE) == xhci->ev_buffer_get) {
++        intr->er_full = 1;
++        if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
+             fprintf(stderr, "xhci: event queue full, dropping event!\n");
+             return;
+         }
+-        xhci->ev_buffer[xhci->ev_buffer_put++] = *event;
+-        if (xhci->ev_buffer_put == EV_QUEUE) {
+-            xhci->ev_buffer_put = 0;
++        intr->ev_buffer[intr->ev_buffer_put++] = *event;
++        if (intr->ev_buffer_put == EV_QUEUE) {
++            intr->ev_buffer_put = 0;
+         }
+     } else {
+-        xhci_write_event(xhci, event);
++        xhci_write_event(xhci, event, v);
+     }
+ 
+-    xhci_intr_raise(xhci);
++    xhci_intr_raise(xhci, v);
+ }
+ 
+ static void xhci_ring_init(XHCIState *xhci, XHCIRing *ring,
+@@ -939,17 +947,18 @@ static int xhci_ring_chain_length(XHCIState *xhci, const XHCIRing *ring)
+     }
+ }
+ 
+-static void xhci_er_reset(XHCIState *xhci)
++static void xhci_er_reset(XHCIState *xhci, int v)
+ {
++    XHCIInterrupter *intr = &xhci->intr[v];
+     XHCIEvRingSeg seg;
+ 
+     /* cache the (sole) event ring segment location */
+-    if (xhci->erstsz != 1) {
+-        fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", xhci->erstsz);
++    if (intr->erstsz != 1) {
++        fprintf(stderr, "xhci: invalid value for ERSTSZ: %d\n", intr->erstsz);
+         xhci_die(xhci);
+         return;
+     }
+-    dma_addr_t erstba = xhci_addr64(xhci->erstba_low, xhci->erstba_high);
++    dma_addr_t erstba = xhci_addr64(intr->erstba_low, intr->erstba_high);
+     pci_dma_read(&xhci->pci_dev, erstba, &seg, sizeof(seg));
+     le32_to_cpus(&seg.addr_low);
+     le32_to_cpus(&seg.addr_high);
+@@ -959,15 +968,15 @@ static void xhci_er_reset(XHCIState *xhci)
+         xhci_die(xhci);
+         return;
+     }
+-    xhci->er_start = xhci_addr64(seg.addr_low, seg.addr_high);
+-    xhci->er_size = seg.size;
++    intr->er_start = xhci_addr64(seg.addr_low, seg.addr_high);
++    intr->er_size = seg.size;
+ 
+-    xhci->er_ep_idx = 0;
+-    xhci->er_pcs = 1;
+-    xhci->er_full = 0;
++    intr->er_ep_idx = 0;
++    intr->er_pcs = 1;
++    intr->er_full = 0;
+ 
+-    DPRINTF("xhci: event ring:" DMA_ADDR_FMT " [%d]\n",
+-            xhci->er_start, xhci->er_size);
++    DPRINTF("xhci: event ring[%d]:" DMA_ADDR_FMT " [%d]\n",
++            v, intr->er_start, intr->er_size);
+ }
+ 
+ static void xhci_run(XHCIState *xhci)
+@@ -1368,7 +1377,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
+                 DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length);
+                 edtla = 0;
+             }
+-            xhci_event(xhci, &event);
++            xhci_event(xhci, &event, 0 /* FIXME */);
+             reported = 1;
+             if (xfer->status != CC_SUCCESS) {
+                 return;
+@@ -2246,7 +2255,7 @@ static void xhci_process_commands(XHCIState *xhci)
+             break;
+         }
+         event.slotid = slotid;
+-        xhci_event(xhci, &event);
++        xhci_event(xhci, &event, 0 /* FIXME */);
+     }
+ }
+ 
+@@ -2276,7 +2285,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
+         port->portsc |= PORTSC_CSC;
+         XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
+                          port->portnr << 24};
+-        xhci_event(xhci, &ev);
++        xhci_event(xhci, &ev, 0 /* FIXME */);
+         DPRINTF("xhci: port change event for port %d\n", port->portnr);
+     }
+ }
+@@ -2309,20 +2318,22 @@ static void xhci_reset(DeviceState *dev)
+         xhci_update_port(xhci, xhci->ports + i, 0);
+     }
+ 
+-    xhci->iman = 0;
+-    xhci->imod = 0;
+-    xhci->erstsz = 0;
+-    xhci->erstba_low = 0;
+-    xhci->erstba_high = 0;
+-    xhci->erdp_low = 0;
+-    xhci->erdp_high = 0;
+-    xhci->msix_used = 0;
++    for (i = 0; i < MAXINTRS; i++) {
++        xhci->intr[i].iman = 0;
++        xhci->intr[i].imod = 0;
++        xhci->intr[i].erstsz = 0;
++        xhci->intr[i].erstba_low = 0;
++        xhci->intr[i].erstba_high = 0;
++        xhci->intr[i].erdp_low = 0;
++        xhci->intr[i].erdp_high = 0;
++        xhci->intr[i].msix_used = 0;
+ 
+-    xhci->er_ep_idx = 0;
+-    xhci->er_pcs = 1;
+-    xhci->er_full = 0;
+-    xhci->ev_buffer_put = 0;
+-    xhci->ev_buffer_get = 0;
++        xhci->intr[i].er_ep_idx = 0;
++        xhci->intr[i].er_pcs = 1;
++        xhci->intr[i].er_full = 0;
++        xhci->intr[i].ev_buffer_put = 0;
++        xhci->intr[i].ev_buffer_get = 0;
++    }
+ 
+     xhci->mfindex_start = qemu_get_clock_ns(vm_clock);
+     xhci_mfwrap_update(xhci);
+@@ -2553,7 +2564,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) {
+             XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED};
+             xhci->crcr_low &= ~CRCR_CRR;
+-            xhci_event(xhci, &event);
++            xhci_event(xhci, &event, 0 /* FIXME */);
+             DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
+         } else {
+             dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
+@@ -2577,6 +2588,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+ 
+ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+ {
++    XHCIInterrupter *intr = &xhci->intr[0];
+     uint32_t ret;
+ 
+     switch (reg) {
+@@ -2584,25 +2596,25 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+         ret = xhci_mfindex_get(xhci) & 0x3fff;
+         break;
+     case 0x20: /* IMAN */
+-        ret = xhci->iman;
++        ret = intr->iman;
+         break;
+     case 0x24: /* IMOD */
+-        ret = xhci->imod;
++        ret = intr->imod;
+         break;
+     case 0x28: /* ERSTSZ */
+-        ret = xhci->erstsz;
++        ret = intr->erstsz;
+         break;
+     case 0x30: /* ERSTBA low */
+-        ret = xhci->erstba_low;
++        ret = intr->erstba_low;
+         break;
+     case 0x34: /* ERSTBA high */
+-        ret = xhci->erstba_high;
++        ret = intr->erstba_high;
+         break;
+     case 0x38: /* ERDP low */
+-        ret = xhci->erdp_low;
++        ret = intr->erdp_low;
+         break;
+     case 0x3c: /* ERDP high */
+-        ret = xhci->erdp_high;
++        ret = intr->erdp_high;
+         break;
+     default:
+         fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
+@@ -2615,42 +2627,43 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+ 
+ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+ {
++    XHCIInterrupter *intr = &xhci->intr[0];
+     trace_usb_xhci_runtime_write(reg, val);
+ 
+     switch (reg) {
+     case 0x20: /* IMAN */
+         if (val & IMAN_IP) {
+-            xhci->iman &= ~IMAN_IP;
++            intr->iman &= ~IMAN_IP;
+         }
+-        xhci->iman &= ~IMAN_IE;
+-        xhci->iman |= val & IMAN_IE;
++        intr->iman &= ~IMAN_IE;
++        intr->iman |= val & IMAN_IE;
+         xhci_intx_update(xhci);
+-        xhci_msix_update(xhci);
++        xhci_msix_update(xhci, 0);
+         break;
+     case 0x24: /* IMOD */
+-        xhci->imod = val;
++        intr->imod = val;
+         break;
+     case 0x28: /* ERSTSZ */
+-        xhci->erstsz = val & 0xffff;
++        intr->erstsz = val & 0xffff;
+         break;
+     case 0x30: /* ERSTBA low */
+         /* XXX NEC driver bug: it doesn't align this to 64 bytes
+-        xhci->erstba_low = val & 0xffffffc0; */
+-        xhci->erstba_low = val & 0xfffffff0;
++        intr->erstba_low = val & 0xffffffc0; */
++        intr->erstba_low = val & 0xfffffff0;
+         break;
+     case 0x34: /* ERSTBA high */
+-        xhci->erstba_high = val;
+-        xhci_er_reset(xhci);
++        intr->erstba_high = val;
++        xhci_er_reset(xhci, 0);
+         break;
+     case 0x38: /* ERDP low */
+         if (val & ERDP_EHB) {
+-            xhci->erdp_low &= ~ERDP_EHB;
++            intr->erdp_low &= ~ERDP_EHB;
+         }
+-        xhci->erdp_low = (val & ~ERDP_EHB) | (xhci->erdp_low & ERDP_EHB);
++        intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB);
+         break;
+     case 0x3c: /* ERDP high */
+-        xhci->erdp_high = val;
+-        xhci_events_update(xhci);
++        intr->erdp_high = val;
++        xhci_events_update(xhci, 0);
+         break;
+     default:
+         fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+@@ -2780,7 +2793,7 @@ static void xhci_wakeup(USBPort *usbport)
+         return;
+     }
+     port->portsc |= PORTSC_PLC;
+-    xhci_event(xhci, &ev);
++    xhci_event(xhci, &ev, 0 /* FIXME */);
+ }
+ 
+ static void xhci_complete(USBPort *port, USBPacket *packet)
+diff --git a/trace-events b/trace-events
+index 8589ca4..b25ae1c 100644
+--- a/trace-events
++++ b/trace-events
+@@ -316,7 +316,7 @@ usb_xhci_irq_msi(uint32_t nr) "nr %d"
+ usb_xhci_irq_msix(uint32_t nr) "nr %d"
+ usb_xhci_irq_msix_use(uint32_t nr) "nr %d"
+ usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d"
+-usb_xhci_queue_event(uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
++usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+ usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+ usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
+ usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
+-- 
+1.7.12
+
diff --git a/0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch b/0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch
new file mode 100644
index 0000000..305b321
--- /dev/null
+++ b/0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch
@@ -0,0 +1,159 @@
+From 8bbd489bb885f5799ebbc108022eee3b2d5a1682 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 30 Aug 2012 17:15:12 +0200
+Subject: [PATCH 350/366] xhci: prepare xhci_runtime_{read,write} for multiple
+ interrupters
+
+Prepare xhci runtime register access function for multiple interrupters.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 100 +++++++++++++++++++++++++++++++-----------------------
+ 1 file changed, 57 insertions(+), 43 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index ddc3825..68a19ab 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -2588,37 +2588,43 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+ 
+ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+ {
+-    XHCIInterrupter *intr = &xhci->intr[0];
+-    uint32_t ret;
++    uint32_t ret = 0;
+ 
+-    switch (reg) {
+-    case 0x00: /* MFINDEX */
+-        ret = xhci_mfindex_get(xhci) & 0x3fff;
+-        break;
+-    case 0x20: /* IMAN */
+-        ret = intr->iman;
+-        break;
+-    case 0x24: /* IMOD */
+-        ret = intr->imod;
+-        break;
+-    case 0x28: /* ERSTSZ */
+-        ret = intr->erstsz;
+-        break;
+-    case 0x30: /* ERSTBA low */
+-        ret = intr->erstba_low;
+-        break;
+-    case 0x34: /* ERSTBA high */
+-        ret = intr->erstba_high;
+-        break;
+-    case 0x38: /* ERDP low */
+-        ret = intr->erdp_low;
+-        break;
+-    case 0x3c: /* ERDP high */
+-        ret = intr->erdp_high;
+-        break;
+-    default:
+-        fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
+-        ret = 0;
++    if (reg < 0x20) {
++        switch (reg) {
++        case 0x00: /* MFINDEX */
++            ret = xhci_mfindex_get(xhci) & 0x3fff;
++            break;
++        default:
++            fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
++            break;
++        }
++    } else {
++        int v = (reg - 0x20) / 0x20;
++        XHCIInterrupter *intr = &xhci->intr[v];
++        switch (reg & 0x1f) {
++        case 0x00: /* IMAN */
++            ret = intr->iman;
++            break;
++        case 0x04: /* IMOD */
++            ret = intr->imod;
++            break;
++        case 0x08: /* ERSTSZ */
++            ret = intr->erstsz;
++            break;
++        case 0x10: /* ERSTBA low */
++            ret = intr->erstba_low;
++            break;
++        case 0x14: /* ERSTBA high */
++            ret = intr->erstba_high;
++            break;
++        case 0x18: /* ERDP low */
++            ret = intr->erdp_low;
++            break;
++        case 0x1c: /* ERDP high */
++            ret = intr->erdp_high;
++            break;
++        }
+     }
+ 
+     trace_usb_xhci_runtime_read(reg, ret);
+@@ -2627,43 +2633,51 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+ 
+ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+ {
+-    XHCIInterrupter *intr = &xhci->intr[0];
++    int v = (reg - 0x20) / 0x20;
++    XHCIInterrupter *intr = &xhci->intr[v];
+     trace_usb_xhci_runtime_write(reg, val);
+ 
+-    switch (reg) {
+-    case 0x20: /* IMAN */
++    if (reg < 0x20) {
++        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
++        return;
++    }
++
++    switch (reg & 0x1f) {
++    case 0x00: /* IMAN */
+         if (val & IMAN_IP) {
+             intr->iman &= ~IMAN_IP;
+         }
+         intr->iman &= ~IMAN_IE;
+         intr->iman |= val & IMAN_IE;
+-        xhci_intx_update(xhci);
+-        xhci_msix_update(xhci, 0);
++        if (v == 0) {
++            xhci_intx_update(xhci);
++        }
++        xhci_msix_update(xhci, v);
+         break;
+-    case 0x24: /* IMOD */
++    case 0x04: /* IMOD */
+         intr->imod = val;
+         break;
+-    case 0x28: /* ERSTSZ */
++    case 0x08: /* ERSTSZ */
+         intr->erstsz = val & 0xffff;
+         break;
+-    case 0x30: /* ERSTBA low */
++    case 0x10: /* ERSTBA low */
+         /* XXX NEC driver bug: it doesn't align this to 64 bytes
+         intr->erstba_low = val & 0xffffffc0; */
+         intr->erstba_low = val & 0xfffffff0;
+         break;
+-    case 0x34: /* ERSTBA high */
++    case 0x14: /* ERSTBA high */
+         intr->erstba_high = val;
+-        xhci_er_reset(xhci, 0);
++        xhci_er_reset(xhci, v);
+         break;
+-    case 0x38: /* ERDP low */
++    case 0x18: /* ERDP low */
+         if (val & ERDP_EHB) {
+             intr->erdp_low &= ~ERDP_EHB;
+         }
+         intr->erdp_low = (val & ~ERDP_EHB) | (intr->erdp_low & ERDP_EHB);
+         break;
+-    case 0x3c: /* ERDP high */
++    case 0x1c: /* ERDP high */
+         intr->erdp_high = val;
+-        xhci_events_update(xhci, 0);
++        xhci_events_update(xhci, v);
+         break;
+     default:
+         fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
+-- 
+1.7.12
+
diff --git a/0351-xhci-pick-target-interrupter.patch b/0351-xhci-pick-target-interrupter.patch
new file mode 100644
index 0000000..bf95359
--- /dev/null
+++ b/0351-xhci-pick-target-interrupter.patch
@@ -0,0 +1,93 @@
+From 11ba203517cc28be513ac31c12762c4519e98ee5 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Fri, 31 Aug 2012 15:30:51 +0200
+Subject: [PATCH 351/366] xhci: pick target interrupter
+
+Pick the correct interrupter when queuing an event.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 22 ++++++++++++++++------
+ 1 file changed, 16 insertions(+), 6 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 68a19ab..d6ab0c6 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -264,6 +264,10 @@ typedef enum TRBCCode {
+ 
+ #define TRB_LK_TC           (1<<1)
+ 
++#define TRB_INTR_SHIFT          22
++#define TRB_INTR_MASK       0x3ff
++#define TRB_INTR(t)         (((t).status >> TRB_INTR_SHIFT) & TRB_INTR_MASK)
++
+ #define EP_TYPE_MASK        0x7
+ #define EP_TYPE_SHIFT           3
+ 
+@@ -806,10 +810,16 @@ static void xhci_events_update(XHCIState *xhci, int v)
+ 
+ static void xhci_event(XHCIState *xhci, XHCIEvent *event, int v)
+ {
+-    XHCIInterrupter *intr = &xhci->intr[v];
++    XHCIInterrupter *intr;
+     dma_addr_t erdp;
+     unsigned int dp_idx;
+ 
++    if (v >= MAXINTRS) {
++        DPRINTF("intr nr out of range (%d >= %d)\n", v, MAXINTRS);
++        return;
++    }
++    intr = &xhci->intr[v];
++
+     if (intr->er_full) {
+         DPRINTF("xhci_event(): ER full, queueing\n");
+         if (((intr->ev_buffer_put+1) % EV_QUEUE) == intr->ev_buffer_get) {
+@@ -1377,7 +1387,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
+                 DPRINTF("xhci_xfer_data: EDTLA=%d\n", event.length);
+                 edtla = 0;
+             }
+-            xhci_event(xhci, &event, 0 /* FIXME */);
++            xhci_event(xhci, &event, TRB_INTR(*trb));
+             reported = 1;
+             if (xfer->status != CC_SUCCESS) {
+                 return;
+@@ -2255,7 +2265,7 @@ static void xhci_process_commands(XHCIState *xhci)
+             break;
+         }
+         event.slotid = slotid;
+-        xhci_event(xhci, &event, 0 /* FIXME */);
++        xhci_event(xhci, &event, 0);
+     }
+ }
+ 
+@@ -2285,7 +2295,7 @@ static void xhci_update_port(XHCIState *xhci, XHCIPort *port, int is_detach)
+         port->portsc |= PORTSC_CSC;
+         XHCIEvent ev = { ER_PORT_STATUS_CHANGE, CC_SUCCESS,
+                          port->portnr << 24};
+-        xhci_event(xhci, &ev, 0 /* FIXME */);
++        xhci_event(xhci, &ev, 0);
+         DPRINTF("xhci: port change event for port %d\n", port->portnr);
+     }
+ }
+@@ -2564,7 +2574,7 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         if (xhci->crcr_low & (CRCR_CA|CRCR_CS) && (xhci->crcr_low & CRCR_CRR)) {
+             XHCIEvent event = {ER_COMMAND_COMPLETE, CC_COMMAND_RING_STOPPED};
+             xhci->crcr_low &= ~CRCR_CRR;
+-            xhci_event(xhci, &event, 0 /* FIXME */);
++            xhci_event(xhci, &event, 0);
+             DPRINTF("xhci: command ring stopped (CRCR=%08x)\n", xhci->crcr_low);
+         } else {
+             dma_addr_t base = xhci_addr64(xhci->crcr_low & ~0x3f, val);
+@@ -2807,7 +2817,7 @@ static void xhci_wakeup(USBPort *usbport)
+         return;
+     }
+     port->portsc |= PORTSC_PLC;
+-    xhci_event(xhci, &ev, 0 /* FIXME */);
++    xhci_event(xhci, &ev, 0);
+ }
+ 
+ static void xhci_complete(USBPort *port, USBPacket *packet)
+-- 
+1.7.12
+
diff --git a/0352-xhci-support-multiple-interrupters.patch b/0352-xhci-support-multiple-interrupters.patch
new file mode 100644
index 0000000..08b1d44
--- /dev/null
+++ b/0352-xhci-support-multiple-interrupters.patch
@@ -0,0 +1,40 @@
+From d6d045365af5cef5bc98ad48dcf4172cbe35c7c4 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 4 Sep 2012 12:56:55 +0200
+Subject: [PATCH 352/366] xhci: support multiple interrupters
+
+Everything is in place, flip the big switch now
+and enable support for multiple interrupters.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index d6ab0c6..55e31ec 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -42,7 +42,7 @@
+ 
+ #define MAXPORTS (MAXPORTS_2+MAXPORTS_3)
+ #define MAXSLOTS MAXPORTS
+-#define MAXINTRS 1 /* MAXPORTS */
++#define MAXINTRS MAXPORTS
+ 
+ #define TD_QUEUE 24
+ 
+@@ -75,10 +75,6 @@
+ # error Increase LEN_REGS
+ #endif
+ 
+-#if MAXINTRS > 1
+-# error TODO: only one interrupter supported
+-#endif
+-
+ /* bit definitions */
+ #define USBCMD_RS       (1<<0)
+ #define USBCMD_HCRST    (1<<1)
+-- 
+1.7.12
+
diff --git a/0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch b/0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch
new file mode 100644
index 0000000..04a2dbf
--- /dev/null
+++ b/0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch
@@ -0,0 +1,281 @@
+From e5295db184d2b78a6a779aac019acbf58ed3da5e Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 4 Sep 2012 14:42:20 +0200
+Subject: [PATCH 353/366] xhci: kill xhci_mem_{read,write} dispatcher
+ functions
+
+... and register subregions instead, so we offload the dispatching
+to the the memory subsystem which is designed to handle it.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 140 +++++++++++++++++++++++++++++-------------------------
+ 1 file changed, 75 insertions(+), 65 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 55e31ec..500892d 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -404,6 +404,10 @@ struct XHCIState {
+     USBBus bus;
+     qemu_irq irq;
+     MemoryRegion mem;
++    MemoryRegion mem_cap;
++    MemoryRegion mem_oper;
++    MemoryRegion mem_runtime;
++    MemoryRegion mem_doorbell;
+     const char *name;
+     unsigned int devaddr;
+ 
+@@ -2345,8 +2349,9 @@ static void xhci_reset(DeviceState *dev)
+     xhci_mfwrap_update(xhci);
+ }
+ 
+-static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
++static uint64_t xhci_cap_read(void *ptr, target_phys_addr_t reg, unsigned size)
+ {
++    XHCIState *xhci = ptr;
+     uint32_t ret;
+ 
+     switch (reg) {
+@@ -2403,7 +2408,7 @@ static uint32_t xhci_cap_read(XHCIState *xhci, uint32_t reg)
+         ret = 0x00000000; /* reserved */
+         break;
+     default:
+-        fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", reg);
++        fprintf(stderr, "xhci_cap_read: reg %d unimplemented\n", (int)reg);
+         ret = 0;
+     }
+ 
+@@ -2484,8 +2489,9 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+     }
+ }
+ 
+-static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
++static uint64_t xhci_oper_read(void *ptr, target_phys_addr_t reg, unsigned size)
+ {
++    XHCIState *xhci = ptr;
+     uint32_t ret;
+ 
+     if (reg >= 0x400) {
+@@ -2521,7 +2527,7 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
+         ret = xhci->config;
+         break;
+     default:
+-        fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", reg);
++        fprintf(stderr, "xhci_oper_read: reg 0x%x unimplemented\n", (int)reg);
+         ret = 0;
+     }
+ 
+@@ -2529,8 +2535,11 @@ static uint32_t xhci_oper_read(XHCIState *xhci, uint32_t reg)
+     return ret;
+ }
+ 
+-static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
++static void xhci_oper_write(void *ptr, target_phys_addr_t reg,
++                            uint64_t val, unsigned size)
+ {
++    XHCIState *xhci = ptr;
++
+     if (reg >= 0x400) {
+         xhci_port_write(xhci, reg - 0x400, val);
+         return;
+@@ -2588,12 +2597,14 @@ static void xhci_oper_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         xhci->config = val & 0xff;
+         break;
+     default:
+-        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
++        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg);
+     }
+ }
+ 
+-static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
++static uint64_t xhci_runtime_read(void *ptr, target_phys_addr_t reg,
++                                  unsigned size)
+ {
++    XHCIState *xhci = ptr;
+     uint32_t ret = 0;
+ 
+     if (reg < 0x20) {
+@@ -2602,7 +2613,8 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+             ret = xhci_mfindex_get(xhci) & 0x3fff;
+             break;
+         default:
+-            fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n", reg);
++            fprintf(stderr, "xhci_runtime_read: reg 0x%x unimplemented\n",
++                    (int)reg);
+             break;
+         }
+     } else {
+@@ -2637,14 +2649,16 @@ static uint32_t xhci_runtime_read(XHCIState *xhci, uint32_t reg)
+     return ret;
+ }
+ 
+-static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
++static void xhci_runtime_write(void *ptr, target_phys_addr_t reg,
++                               uint64_t val, unsigned size)
+ {
++    XHCIState *xhci = ptr;
+     int v = (reg - 0x20) / 0x20;
+     XHCIInterrupter *intr = &xhci->intr[v];
+     trace_usb_xhci_runtime_write(reg, val);
+ 
+     if (reg < 0x20) {
+-        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
++        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", (int)reg);
+         return;
+     }
+ 
+@@ -2686,19 +2700,24 @@ static void xhci_runtime_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         xhci_events_update(xhci, v);
+         break;
+     default:
+-        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n", reg);
++        fprintf(stderr, "xhci_oper_write: reg 0x%x unimplemented\n",
++                (int)reg);
+     }
+ }
+ 
+-static uint32_t xhci_doorbell_read(XHCIState *xhci, uint32_t reg)
++static uint64_t xhci_doorbell_read(void *ptr, target_phys_addr_t reg,
++                                   unsigned size)
+ {
+     /* doorbells always read as 0 */
+     trace_usb_xhci_doorbell_read(reg, 0);
+     return 0;
+ }
+ 
+-static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
++static void xhci_doorbell_write(void *ptr, target_phys_addr_t reg,
++                                uint64_t val, unsigned size)
+ {
++    XHCIState *xhci = ptr;
++
+     trace_usb_xhci_doorbell_write(reg, val);
+ 
+     if (!xhci_running(xhci)) {
+@@ -2712,69 +2731,47 @@ static void xhci_doorbell_write(XHCIState *xhci, uint32_t reg, uint32_t val)
+         if (val == 0) {
+             xhci_process_commands(xhci);
+         } else {
+-            fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n", val);
++            fprintf(stderr, "xhci: bad doorbell 0 write: 0x%x\n",
++                    (uint32_t)val);
+         }
+     } else {
+         if (reg > MAXSLOTS) {
+-            fprintf(stderr, "xhci: bad doorbell %d\n", reg);
++            fprintf(stderr, "xhci: bad doorbell %d\n", (int)reg);
+         } else if (val > 31) {
+-            fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n", reg, val);
++            fprintf(stderr, "xhci: bad doorbell %d write: 0x%x\n",
++                    (int)reg, (uint32_t)val);
+         } else {
+             xhci_kick_ep(xhci, reg, val);
+         }
+     }
+ }
+ 
+-static uint64_t xhci_mem_read(void *ptr, target_phys_addr_t addr,
+-                              unsigned size)
+-{
+-    XHCIState *xhci = ptr;
+-
+-    /* Only aligned reads are allowed on xHCI */
+-    if (addr & 3) {
+-        fprintf(stderr, "xhci_mem_read: Mis-aligned read\n");
+-        return 0;
+-    }
+-
+-    if (addr < LEN_CAP) {
+-        return xhci_cap_read(xhci, addr);
+-    } else if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
+-        return xhci_oper_read(xhci, addr - OFF_OPER);
+-    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
+-        return xhci_runtime_read(xhci, addr - OFF_RUNTIME);
+-    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
+-        return xhci_doorbell_read(xhci, addr - OFF_DOORBELL);
+-    } else {
+-        fprintf(stderr, "xhci_mem_read: Bad offset %x\n", (int)addr);
+-        return 0;
+-    }
+-}
+-
+-static void xhci_mem_write(void *ptr, target_phys_addr_t addr,
+-                           uint64_t val, unsigned size)
+-{
+-    XHCIState *xhci = ptr;
++static const MemoryRegionOps xhci_cap_ops = {
++    .read = xhci_cap_read,
++    .valid.min_access_size = 4,
++    .valid.max_access_size = 4,
++    .endianness = DEVICE_LITTLE_ENDIAN,
++};
+ 
+-    /* Only aligned writes are allowed on xHCI */
+-    if (addr & 3) {
+-        fprintf(stderr, "xhci_mem_write: Mis-aligned write\n");
+-        return;
+-    }
++static const MemoryRegionOps xhci_oper_ops = {
++    .read = xhci_oper_read,
++    .write = xhci_oper_write,
++    .valid.min_access_size = 4,
++    .valid.max_access_size = 4,
++    .endianness = DEVICE_LITTLE_ENDIAN,
++};
+ 
+-    if (addr >= OFF_OPER && addr < (OFF_OPER + LEN_OPER)) {
+-        xhci_oper_write(xhci, addr - OFF_OPER, val);
+-    } else if (addr >= OFF_RUNTIME && addr < (OFF_RUNTIME + LEN_RUNTIME)) {
+-        xhci_runtime_write(xhci, addr - OFF_RUNTIME, val);
+-    } else if (addr >= OFF_DOORBELL && addr < (OFF_DOORBELL + LEN_DOORBELL)) {
+-        xhci_doorbell_write(xhci, addr - OFF_DOORBELL, val);
+-    } else {
+-        fprintf(stderr, "xhci_mem_write: Bad offset %x\n", (int)addr);
+-    }
+-}
++static const MemoryRegionOps xhci_runtime_ops = {
++    .read = xhci_runtime_read,
++    .write = xhci_runtime_write,
++    .valid.min_access_size = 4,
++    .valid.max_access_size = 4,
++    .endianness = DEVICE_LITTLE_ENDIAN,
++};
+ 
+-static const MemoryRegionOps xhci_mem_ops = {
+-    .read = xhci_mem_read,
+-    .write = xhci_mem_write,
++static const MemoryRegionOps xhci_doorbell_ops = {
++    .read = xhci_doorbell_read,
++    .write = xhci_doorbell_write,
+     .valid.min_access_size = 4,
+     .valid.max_access_size = 4,
+     .endianness = DEVICE_LITTLE_ENDIAN,
+@@ -2940,8 +2937,21 @@ static int usb_xhci_initfn(struct PCIDevice *dev)
+ 
+     xhci->irq = xhci->pci_dev.irq[0];
+ 
+-    memory_region_init_io(&xhci->mem, &xhci_mem_ops, xhci,
+-                          "xhci", LEN_REGS);
++    memory_region_init(&xhci->mem, "xhci", LEN_REGS);
++    memory_region_init_io(&xhci->mem_cap, &xhci_cap_ops, xhci,
++                          "capabilities", LEN_CAP);
++    memory_region_init_io(&xhci->mem_oper, &xhci_oper_ops, xhci,
++                          "operational", 0x400 + 0x10 * xhci->numports);
++    memory_region_init_io(&xhci->mem_runtime, &xhci_runtime_ops, xhci,
++                          "runtime", LEN_RUNTIME);
++    memory_region_init_io(&xhci->mem_doorbell, &xhci_doorbell_ops, xhci,
++                          "doorbell", LEN_DOORBELL);
++
++    memory_region_add_subregion(&xhci->mem, 0,            &xhci->mem_cap);
++    memory_region_add_subregion(&xhci->mem, OFF_OPER,     &xhci->mem_oper);
++    memory_region_add_subregion(&xhci->mem, OFF_RUNTIME,  &xhci->mem_runtime);
++    memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell);
++
+     pci_register_bar(&xhci->pci_dev, 0,
+                      PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64,
+                      &xhci->mem);
+-- 
+1.7.12
+
diff --git a/0354-xhci-allow-bytewise-capability-register-reads.patch b/0354-xhci-allow-bytewise-capability-register-reads.patch
new file mode 100644
index 0000000..12b4338
--- /dev/null
+++ b/0354-xhci-allow-bytewise-capability-register-reads.patch
@@ -0,0 +1,32 @@
+From 25968a7c7947b7e215e351b159a5f4ebf4609aab Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Tue, 4 Sep 2012 14:48:03 +0200
+Subject: [PATCH 354/366] xhci: allow bytewise capability register reads
+
+Some guests need this according to
+Alejandro Martinez Ruiz <alex@securiforest.com>
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 500892d..2918e64 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -2748,8 +2748,10 @@ static void xhci_doorbell_write(void *ptr, target_phys_addr_t reg,
+ 
+ static const MemoryRegionOps xhci_cap_ops = {
+     .read = xhci_cap_read,
+-    .valid.min_access_size = 4,
++    .valid.min_access_size = 1,
+     .valid.max_access_size = 4,
++    .impl.min_access_size = 4,
++    .impl.max_access_size = 4,
+     .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+ 
+-- 
+1.7.12
+
diff --git a/0355-ehci-switch-to-new-style-memory-ops.patch b/0355-ehci-switch-to-new-style-memory-ops.patch
new file mode 100644
index 0000000..78e6b4f
--- /dev/null
+++ b/0355-ehci-switch-to-new-style-memory-ops.patch
@@ -0,0 +1,367 @@
+From 538ee859ae415782e5be3b4a07e7db655cf70aa2 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 6 Sep 2012 11:24:51 +0200
+Subject: [PATCH 355/366] ehci: switch to new-style memory ops
+
+Also register different memory regions for capabilities,
+operational registers and port status registers.  Create
+separate tracepoints for operational regs and port status
+regs.  Ditch a bunch of sanity checks because the memory
+core will do this for us now.
+
+Offloading the byte, word and dword access handling to the
+memory core also has the side effect of fixing ehci register
+access on bigendian hosts.
+
+Cc: David Gibson <david@gibson.dropbear.id.au>
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 173 ++++++++++++++++++++++++++----------------------------
+ trace-events      |   9 ++-
+ 2 files changed, 90 insertions(+), 92 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 2f3e9c0..f5ba8e1 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -389,6 +389,9 @@ struct EHCIState {
+     USBBus bus;
+     qemu_irq irq;
+     MemoryRegion mem;
++    MemoryRegion mem_caps;
++    MemoryRegion mem_opreg;
++    MemoryRegion mem_ports;
+     int companion_count;
+ 
+     /* properties */
+@@ -398,10 +401,10 @@ struct EHCIState {
+      *  EHCI spec version 1.0 Section 2.3
+      *  Host Controller Operational Registers
+      */
++    uint8_t caps[OPREGBASE];
+     union {
+-        uint8_t mmio[MMIO_SIZE];
++        uint32_t opreg[(PORTSC_BEGIN-OPREGBASE)/sizeof(uint32_t)];
+         struct {
+-            uint8_t cap[OPREGBASE];
+             uint32_t usbcmd;
+             uint32_t usbsts;
+             uint32_t usbintr;
+@@ -411,9 +414,9 @@ struct EHCIState {
+             uint32_t asynclistaddr;
+             uint32_t notused[9];
+             uint32_t configflag;
+-            uint32_t portsc[NB_PORTS];
+         };
+     };
++    uint32_t portsc[NB_PORTS];
+ 
+     /*
+      *  Internal states, shadow registers, etc
+@@ -471,22 +474,12 @@ static const char *ehci_state_names[] = {
+ };
+ 
+ static const char *ehci_mmio_names[] = {
+-    [CAPLENGTH]         = "CAPLENGTH",
+-    [HCIVERSION]        = "HCIVERSION",
+-    [HCSPARAMS]         = "HCSPARAMS",
+-    [HCCPARAMS]         = "HCCPARAMS",
+     [USBCMD]            = "USBCMD",
+     [USBSTS]            = "USBSTS",
+     [USBINTR]           = "USBINTR",
+     [FRINDEX]           = "FRINDEX",
+     [PERIODICLISTBASE]  = "P-LIST BASE",
+     [ASYNCLISTADDR]     = "A-LIST ADDR",
+-    [PORTSC_BEGIN]      = "PORTSC #0",
+-    [PORTSC_BEGIN + 4]  = "PORTSC #1",
+-    [PORTSC_BEGIN + 8]  = "PORTSC #2",
+-    [PORTSC_BEGIN + 12] = "PORTSC #3",
+-    [PORTSC_BEGIN + 16] = "PORTSC #4",
+-    [PORTSC_BEGIN + 20] = "PORTSC #5",
+     [CONFIGFLAG]        = "CONFIGFLAG",
+ };
+ 
+@@ -509,7 +502,8 @@ static const char *state2str(uint32_t state)
+ 
+ static const char *addr2str(target_phys_addr_t addr)
+ {
+-    return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
++    return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names),
++                  addr + OPREGBASE);
+ }
+ 
+ static void ehci_trace_usbsts(uint32_t mask, int state)
+@@ -1018,7 +1012,7 @@ static int ehci_register_companion(USBBus *bus, USBPort *ports[],
+     }
+ 
+     s->companion_count++;
+-    s->mmio[0x05] = (s->companion_count << 4) | portcount;
++    s->caps[0x05] = (s->companion_count << 4) | portcount;
+ 
+     return 0;
+ }
+@@ -1063,7 +1057,8 @@ static void ehci_reset(void *opaque)
+         }
+     }
+ 
+-    memset(&s->mmio[OPREGBASE], 0x00, MMIO_SIZE - OPREGBASE);
++    memset(&s->opreg, 0x00, sizeof(s->opreg));
++    memset(&s->portsc, 0x00, sizeof(s->portsc));
+ 
+     s->usbcmd = NB_MAXINTRATE << USBCMD_ITC_SH;
+     s->usbsts = USBSTS_HALT;
+@@ -1090,50 +1085,35 @@ static void ehci_reset(void *opaque)
+     qemu_bh_cancel(s->async_bh);
+ }
+ 
+-static uint32_t ehci_mem_readb(void *ptr, target_phys_addr_t addr)
++static uint64_t ehci_caps_read(void *ptr, target_phys_addr_t addr,
++                               unsigned size)
+ {
+     EHCIState *s = ptr;
+-    uint32_t val;
+-
+-    val = s->mmio[addr];
+-
+-    return val;
++    return s->caps[addr];
+ }
+ 
+-static uint32_t ehci_mem_readw(void *ptr, target_phys_addr_t addr)
++static uint64_t ehci_opreg_read(void *ptr, target_phys_addr_t addr,
++                                unsigned size)
+ {
+     EHCIState *s = ptr;
+     uint32_t val;
+ 
+-    val = s->mmio[addr] | (s->mmio[addr+1] << 8);
+-
++    val = s->opreg[addr >> 2];
++    trace_usb_ehci_opreg_read(addr + OPREGBASE, addr2str(addr), val);
+     return val;
+ }
+ 
+-static uint32_t ehci_mem_readl(void *ptr, target_phys_addr_t addr)
++static uint64_t ehci_port_read(void *ptr, target_phys_addr_t addr,
++                               unsigned size)
+ {
+     EHCIState *s = ptr;
+     uint32_t val;
+ 
+-    val = s->mmio[addr] | (s->mmio[addr+1] << 8) |
+-          (s->mmio[addr+2] << 16) | (s->mmio[addr+3] << 24);
+-
+-    trace_usb_ehci_mmio_readl(addr, addr2str(addr), val);
++    val = s->portsc[addr >> 2];
++    trace_usb_ehci_portsc_read(addr + PORTSC_BEGIN, addr >> 2, val);
+     return val;
+ }
+ 
+-static void ehci_mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t val)
+-{
+-    fprintf(stderr, "EHCI doesn't handle byte writes to MMIO\n");
+-    exit(1);
+-}
+-
+-static void ehci_mem_writew(void *ptr, target_phys_addr_t addr, uint32_t val)
+-{
+-    fprintf(stderr, "EHCI doesn't handle 16-bit writes to MMIO\n");
+-    exit(1);
+-}
+-
+ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
+ {
+     USBDevice *dev = s->ports[port].dev;
+@@ -1162,11 +1142,17 @@ static void handle_port_owner_write(EHCIState *s, int port, uint32_t owner)
+     }
+ }
+ 
+-static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
++static void ehci_port_write(void *ptr, target_phys_addr_t addr,
++                            uint64_t val, unsigned size)
+ {
++    EHCIState *s = ptr;
++    int port = addr >> 2;
+     uint32_t *portsc = &s->portsc[port];
++    uint32_t old = *portsc;
+     USBDevice *dev = s->ports[port].dev;
+ 
++    trace_usb_ehci_portsc_write(addr + PORTSC_BEGIN, addr >> 2, val);
++
+     /* Clear rwc bits */
+     *portsc &= ~(val & PORTSC_RWC_MASK);
+     /* The guest may clear, but not set the PED bit */
+@@ -1198,39 +1184,20 @@ static void handle_port_status_write(EHCIState *s, int port, uint32_t val)
+ 
+     *portsc &= ~PORTSC_RO_MASK;
+     *portsc |= val;
++    trace_usb_ehci_portsc_change(addr + PORTSC_BEGIN, addr >> 2, *portsc, old);
+ }
+ 
+-static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
++static void ehci_opreg_write(void *ptr, target_phys_addr_t addr,
++                             uint64_t val, unsigned size)
+ {
+     EHCIState *s = ptr;
+-    uint32_t *mmio = (uint32_t *)(&s->mmio[addr]);
++    uint32_t *mmio = s->opreg + (addr >> 2);
+     uint32_t old = *mmio;
+     int i;
+ 
+-    trace_usb_ehci_mmio_writel(addr, addr2str(addr), val);
+-
+-    /* Only aligned reads are allowed on OHCI */
+-    if (addr & 3) {
+-        fprintf(stderr, "usb-ehci: Mis-aligned write to addr 0x"
+-                TARGET_FMT_plx "\n", addr);
+-        return;
+-    }
+-
+-    if (addr >= PORTSC && addr < PORTSC + 4 * NB_PORTS) {
+-        handle_port_status_write(s, (addr-PORTSC)/4, val);
+-        trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
+-        return;
+-    }
+-
+-    if (addr < OPREGBASE) {
+-        fprintf(stderr, "usb-ehci: write attempt to read-only register"
+-                TARGET_FMT_plx "\n", addr);
+-        return;
+-    }
+-
++    trace_usb_ehci_opreg_write(addr + OPREGBASE, addr2str(addr), val);
+ 
+-    /* Do any register specific pre-write processing here.  */
+-    switch(addr) {
++    switch (addr + OPREGBASE) {
+     case USBCMD:
+         if (val & USBCMD_HCRESET) {
+             ehci_reset(s);
+@@ -1241,7 +1208,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+         /* not supporting dynamic frame list size at the moment */
+         if ((val & USBCMD_FLS) && !(s->usbcmd & USBCMD_FLS)) {
+             fprintf(stderr, "attempt to set frame list size -- value %d\n",
+-                    val & USBCMD_FLS);
++                    (int)val & USBCMD_FLS);
+             val &= ~USBCMD_FLS;
+         }
+ 
+@@ -1308,7 +1275,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
+     }
+ 
+     *mmio = val;
+-    trace_usb_ehci_mmio_change(addr, addr2str(addr), *mmio, old);
++    trace_usb_ehci_opreg_change(addr + OPREGBASE, addr2str(addr), *mmio, old);
+ }
+ 
+ 
+@@ -2520,11 +2487,28 @@ static void ehci_async_bh(void *opaque)
+     ehci_advance_async_state(ehci);
+ }
+ 
+-static const MemoryRegionOps ehci_mem_ops = {
+-    .old_mmio = {
+-        .read = { ehci_mem_readb, ehci_mem_readw, ehci_mem_readl },
+-        .write = { ehci_mem_writeb, ehci_mem_writew, ehci_mem_writel },
+-    },
++static const MemoryRegionOps ehci_mmio_caps_ops = {
++    .read = ehci_caps_read,
++    .valid.min_access_size = 1,
++    .valid.max_access_size = 4,
++    .impl.min_access_size = 1,
++    .impl.max_access_size = 1,
++    .endianness = DEVICE_LITTLE_ENDIAN,
++};
++
++static const MemoryRegionOps ehci_mmio_opreg_ops = {
++    .read = ehci_opreg_read,
++    .write = ehci_opreg_write,
++    .valid.min_access_size = 4,
++    .valid.max_access_size = 4,
++    .endianness = DEVICE_LITTLE_ENDIAN,
++};
++
++static const MemoryRegionOps ehci_mmio_port_ops = {
++    .read = ehci_port_read,
++    .write = ehci_port_write,
++    .valid.min_access_size = 4,
++    .valid.max_access_size = 4,
+     .endianness = DEVICE_LITTLE_ENDIAN,
+ };
+ 
+@@ -2681,19 +2665,19 @@ static int usb_ehci_initfn(PCIDevice *dev)
+     pci_conf[0x6e] = 0x00;
+     pci_conf[0x6f] = 0xc0;  // USBLEFCTLSTS
+ 
+-    // 2.2 host controller interface version
+-    s->mmio[0x00] = (uint8_t) OPREGBASE;
+-    s->mmio[0x01] = 0x00;
+-    s->mmio[0x02] = 0x00;
+-    s->mmio[0x03] = 0x01;        // HC version
+-    s->mmio[0x04] = NB_PORTS;    // Number of downstream ports
+-    s->mmio[0x05] = 0x00;        // No companion ports at present
+-    s->mmio[0x06] = 0x00;
+-    s->mmio[0x07] = 0x00;
+-    s->mmio[0x08] = 0x80;        // We can cache whole frame, not 64-bit capable
+-    s->mmio[0x09] = 0x68;        // EECP
+-    s->mmio[0x0a] = 0x00;
+-    s->mmio[0x0b] = 0x00;
++    /* 2.2 host controller interface version */
++    s->caps[0x00] = (uint8_t) OPREGBASE;
++    s->caps[0x01] = 0x00;
++    s->caps[0x02] = 0x00;
++    s->caps[0x03] = 0x01;        /* HC version */
++    s->caps[0x04] = NB_PORTS;    /* Number of downstream ports */
++    s->caps[0x05] = 0x00;        /* No companion ports at present */
++    s->caps[0x06] = 0x00;
++    s->caps[0x07] = 0x00;
++    s->caps[0x08] = 0x80;        /* We can cache whole frame, no 64-bit */
++    s->caps[0x09] = 0x68;        /* EECP */
++    s->caps[0x0a] = 0x00;
++    s->caps[0x0b] = 0x00;
+ 
+     s->irq = s->dev.irq[3];
+ 
+@@ -2712,7 +2696,18 @@ static int usb_ehci_initfn(PCIDevice *dev)
+ 
+     qemu_register_reset(ehci_reset, s);
+ 
+-    memory_region_init_io(&s->mem, &ehci_mem_ops, s, "ehci", MMIO_SIZE);
++    memory_region_init(&s->mem, "ehci", MMIO_SIZE);
++    memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s,
++                          "capabilities", OPREGBASE);
++    memory_region_init_io(&s->mem_opreg, &ehci_mmio_opreg_ops, s,
++                          "operational", PORTSC_BEGIN - OPREGBASE);
++    memory_region_init_io(&s->mem_ports, &ehci_mmio_port_ops, s,
++                          "ports", PORTSC_END - PORTSC_BEGIN);
++
++    memory_region_add_subregion(&s->mem, 0,            &s->mem_caps);
++    memory_region_add_subregion(&s->mem, OPREGBASE,    &s->mem_opreg);
++    memory_region_add_subregion(&s->mem, PORTSC_BEGIN, &s->mem_ports);
++
+     pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mem);
+ 
+     return 0;
+diff --git a/trace-events b/trace-events
+index b25ae1c..a58b0b7 100644
+--- a/trace-events
++++ b/trace-events
+@@ -243,9 +243,12 @@ usb_port_release(int bus, const char *port) "bus %d, port %s"
+ 
+ # hw/usb/hcd-ehci.c
+ usb_ehci_reset(void) "=== RESET ==="
+-usb_ehci_mmio_readl(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
+-usb_ehci_mmio_writel(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
+-usb_ehci_mmio_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
++usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
++usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
++usb_ehci_opreg_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
++usb_ehci_portsc_read(uint32_t addr, uint32_t port, uint32_t val) "rd mmio %04x [port %d] = %x"
++usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio %04x [port %d] = %x"
++usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio %04x [port %d] = %x (old: %x)"
+ usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
+ usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
+ usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
+-- 
+1.7.12
+
diff --git a/0356-xhci-drop-unused-wlength.patch b/0356-xhci-drop-unused-wlength.patch
new file mode 100644
index 0000000..7bc3008
--- /dev/null
+++ b/0356-xhci-drop-unused-wlength.patch
@@ -0,0 +1,33 @@
+From 0b32fd99ab948db3b4cac32562272095a2594f4c Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 6 Sep 2012 11:55:06 +0200
+Subject: [PATCH 356/366] xhci: drop unused wlength
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/hcd-xhci.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
+index 2918e64..e0ca690 100644
+--- a/hw/usb/hcd-xhci.c
++++ b/hw/usb/hcd-xhci.c
+@@ -1505,7 +1505,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+ {
+     XHCITRB *trb_setup, *trb_status;
+     uint8_t bmRequestType;
+-    uint16_t wLength;
+     int ret;
+ 
+     trb_setup = &xfer->trbs[0];
+@@ -1540,7 +1539,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
+     }
+ 
+     bmRequestType = trb_setup->parameter;
+-    wLength = trb_setup->parameter >> 48;
+ 
+     xfer->in_xfer = bmRequestType & USB_DIR_IN;
+     xfer->iso_xfer = false;
+-- 
+1.7.12
+
diff --git a/0357-usb-host-allow-emulated-non-async-control-requests-w.patch b/0357-usb-host-allow-emulated-non-async-control-requests-w.patch
new file mode 100644
index 0000000..752582f
--- /dev/null
+++ b/0357-usb-host-allow-emulated-non-async-control-requests-w.patch
@@ -0,0 +1,37 @@
+From 487e24442148aa659a53f69db394642a7d93c3c6 Mon Sep 17 00:00:00 2001
+From: Gerd Hoffmann <kraxel@redhat.com>
+Date: Thu, 6 Sep 2012 12:03:41 +0200
+Subject: [PATCH 357/366] usb-host: allow emulated (non-async) control
+ requests without USBPacket
+
+xhci needs this for USB_REQ_SET_ADDRESS due to the way
+usb addressing is handled by the xhci hardware.
+
+Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
+---
+ hw/usb/host-linux.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
+index 8df9207..44f1a64 100644
+--- a/hw/usb/host-linux.c
++++ b/hw/usb/host-linux.c
+@@ -1045,7 +1045,6 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
+ 
+     /* Note request is (bRequestType << 8) | bRequest */
+     trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
+-    assert(p->result == 0);
+ 
+     switch (request) {
+     case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+@@ -1074,6 +1073,7 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
+     }
+ 
+     /* The rest are asynchronous */
++    assert(p && p->result == 0);
+ 
+     if (length > sizeof(dev->data_buf)) {
+         fprintf(stderr, "husb: ctrl buffer too small (%d > %zu)\n",
+-- 
+1.7.12
+
diff --git a/0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch b/0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch
new file mode 100644
index 0000000..f40d17b
--- /dev/null
+++ b/0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch
@@ -0,0 +1,101 @@
+From 833eeda8129b2cf4955a34600b60c01e00652526 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 5 Sep 2012 12:13:41 +0200
+Subject: [PATCH 358/366] ehci: Don't set seen to 0 when removing unseen
+ queue-heads
+
+When removing unseen queue-heads from the async queue list, we should not
+set the seen flag to 0, as this may cause them to be removed by
+ehci_queues_rip_unused() during the next call to ehci_advance_async_state()
+if the timer is late or running at a low frequency.
+
+Note:
+1) This *may* have caused the instant unlink / relinks described in commit
+   9bc3a3a216e2689bfcdd36c3e079333bbdbf3ba0
+
+2) Rather then putting more if-s inside ehci_queues_rip_unused, this patch
+   instead introduces a new ehci_queues_rip_unseen function.
+
+3) This patch also makes it save to call ehci_queues_rip_unseen() multiple
+   times, which gets used in the folluw up patch titled:
+   "ehci: Walk async schedule before and after migration"
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 24 ++++++++++++++++++------
+ 1 file changed, 18 insertions(+), 6 deletions(-)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index f5ba8e1..6f48132 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -847,10 +847,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
+     return NULL;
+ }
+ 
+-static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
++static void ehci_queues_rip_unused(EHCIState *ehci, int async)
+ {
+     EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+-    const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL;
++    const char *warn = async ? "guest unlinked busy QH" : NULL;
+     uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
+     EHCIQueue *q, *tmp;
+ 
+@@ -860,13 +860,25 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
+             q->ts = ehci->last_run_ns;
+             continue;
+         }
+-        if (!flush && ehci->last_run_ns < q->ts + maxage) {
++        if (ehci->last_run_ns < q->ts + maxage) {
+             continue;
+         }
+         ehci_free_queue(q, warn);
+     }
+ }
+ 
++static void ehci_queues_rip_unseen(EHCIState *ehci, int async)
++{
++    EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
++    EHCIQueue *q, *tmp;
++
++    QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
++        if (!q->seen) {
++            ehci_free_queue(q, NULL);
++        }
++    }
++}
++
+ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
+ {
+     EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
+@@ -1699,7 +1711,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci,  int async)
+         ehci_set_usbsts(ehci, USBSTS_REC);
+     }
+ 
+-    ehci_queues_rip_unused(ehci, async, 0);
++    ehci_queues_rip_unused(ehci, async);
+ 
+     /*  Find the head of the list (4.9.1.1) */
+     for(i = 0; i < MAX_QH; i++) {
+@@ -2331,7 +2343,7 @@ static void ehci_advance_async_state(EHCIState *ehci)
+          */
+         if (ehci->usbcmd & USBCMD_IAAD) {
+             /* Remove all unseen qhs from the async qhs queue */
+-            ehci_queues_rip_unused(ehci, async, 1);
++            ehci_queues_rip_unseen(ehci, async);
+             trace_usb_ehci_doorbell_ack();
+             ehci->usbcmd &= ~USBCMD_IAAD;
+             ehci_raise_irq(ehci, USBSTS_IAA);
+@@ -2384,7 +2396,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
+         ehci_set_fetch_addr(ehci, async,entry);
+         ehci_set_state(ehci, async, EST_FETCHENTRY);
+         ehci_advance_state(ehci, async);
+-        ehci_queues_rip_unused(ehci, async, 0);
++        ehci_queues_rip_unused(ehci, async);
+         break;
+ 
+     default:
+-- 
+1.7.12
+
diff --git a/0359-ehci-Walk-async-schedule-before-and-after-migration.patch b/0359-ehci-Walk-async-schedule-before-and-after-migration.patch
new file mode 100644
index 0000000..3c2bf54
--- /dev/null
+++ b/0359-ehci-Walk-async-schedule-before-and-after-migration.patch
@@ -0,0 +1,66 @@
+From fec70ddafe1632f40608ef6917760a7f946f278a Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 5 Sep 2012 12:07:10 +0200
+Subject: [PATCH 359/366] ehci: Walk async schedule before and after migration
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 6f48132..30d2b56 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -34,6 +34,7 @@
+ #include "monitor.h"
+ #include "trace.h"
+ #include "dma.h"
++#include "sysemu.h"
+ 
+ #define EHCI_DEBUG   0
+ 
+@@ -2558,6 +2559,32 @@ static int usb_ehci_post_load(void *opaque, int version_id)
+     return 0;
+ }
+ 
++static void usb_ehci_vm_state_change(void *opaque, int running, RunState state)
++{
++    EHCIState *ehci = opaque;
++
++    /*
++     * We don't migrate the EHCIQueue-s, instead we rebuild them for the
++     * schedule in guest memory. We must do the rebuilt ASAP, so that
++     * USB-devices which have async handled packages have a packet in the
++     * ep queue to match the completion with.
++     */
++    if (state == RUN_STATE_RUNNING) {
++        ehci_advance_async_state(ehci);
++    }
++
++    /*
++     * The schedule rebuilt from guest memory could cause the migration dest
++     * to miss a QH unlink, and fail to cancel packets, since the unlinked QH
++     * will never have existed on the destination. Therefor we must flush the
++     * async schedule on savevm to catch any not yet noticed unlinks.
++     */
++    if (state == RUN_STATE_SAVE_VM) {
++        ehci_advance_async_state(ehci);
++        ehci_queues_rip_unseen(ehci, 1);
++    }
++}
++
+ static const VMStateDescription vmstate_ehci = {
+     .name        = "ehci",
+     .version_id  = 2,
+@@ -2707,6 +2734,7 @@ static int usb_ehci_initfn(PCIDevice *dev)
+     usb_packet_init(&s->ipacket);
+ 
+     qemu_register_reset(ehci_reset, s);
++    qemu_add_vm_change_state_handler(usb_ehci_vm_state_change, s);
+ 
+     memory_region_init(&s->mem, "ehci", MMIO_SIZE);
+     memory_region_init_io(&s->mem_caps, &ehci_mmio_caps_ops, s,
+-- 
+1.7.12
+
diff --git a/0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch b/0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch
new file mode 100644
index 0000000..fb0fe6b
--- /dev/null
+++ b/0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch
@@ -0,0 +1,49 @@
+From d2901e4798cb106d5b265aa4e3ae05c06bf2bd1c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 6 Sep 2012 17:10:48 +0200
+Subject: [PATCH 360/366] ehci: Don't process too much frames in 1 timer tick
+
+The Linux ehci isoc scheduling code fills the entire schedule ahead of
+time minus 80 frames. If we make a large jump in where we are in the
+schedule, ie 40 frames, then the scheduler all of a sudden will only have
+40 frames left to work in, causing it to fail packet submissions
+with error -27 (-EFBIG).
+
+Note at first I had MAX_FR_PER_TICK set to 8, which works well with newer
+Linux guest kernels, but not with older ones (such as the RHEL-6 kernel).
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/hcd-ehci.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
+index 30d2b56..5398544 100644
+--- a/hw/usb/hcd-ehci.c
++++ b/hw/usb/hcd-ehci.c
+@@ -140,6 +140,7 @@
+ #define NB_PORTS         6        // Number of downstream ports
+ #define BUFF_SIZE        5*4096   // Max bytes to transfer per transaction
+ #define MAX_QH           100      // Max allowable queue heads in a chain
++#define MAX_FR_PER_TICK  4        /* Max frames to process in one timer tick */
+ 
+ /*  Internal periodic / asynchronous schedule state machine states
+  */
+@@ -2460,6 +2461,14 @@ static void ehci_frame_timer(void *opaque)
+             DPRINTF("WARNING - EHCI skipped %d frames\n", skipped_frames);
+         }
+ 
++        /*
++         * Processing too much frames at once causes the Linux EHCI isoc
++         * scheduling code to fail packet re-submissions with -EFBIG.
++         */
++        if (frames > MAX_FR_PER_TICK) {
++            frames = MAX_FR_PER_TICK;
++        }
++
+         for (i = 0; i < frames; i++) {
+             ehci_update_frindex(ehci, 1);
+             ehci_advance_periodic_state(ehci);
+-- 
+1.7.12
+
diff --git a/0361-usb-Migrate-over-device-speed-and-speedmask.patch b/0361-usb-Migrate-over-device-speed-and-speedmask.patch
new file mode 100644
index 0000000..d775b9c
--- /dev/null
+++ b/0361-usb-Migrate-over-device-speed-and-speedmask.patch
@@ -0,0 +1,43 @@
+From 6ba840c192897029895930a504527d4350b88d26 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 6 Sep 2012 15:34:19 +0200
+Subject: [PATCH 361/366] usb: Migrate over device speed and speedmask
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb.h     | 4 ++--
+ hw/usb/bus.c | 2 ++
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/hw/usb.h b/hw/usb.h
+index 48c8926..918af99 100644
+--- a/hw/usb.h
++++ b/hw/usb.h
+@@ -204,9 +204,9 @@ struct USBDevice {
+     uint32_t flags;
+ 
+     /* Actual connected speed */
+-    int speed;
++    int32_t speed;
+     /* Supported speeds, not in info because it may be variable (hostdevs) */
+-    int speedmask;
++    int32_t speedmask;
+     uint8_t addr;
+     char product_desc[32];
+     int auto_attach;
+diff --git a/hw/usb/bus.c b/hw/usb/bus.c
+index b649360..223c1df 100644
+--- a/hw/usb/bus.c
++++ b/hw/usb/bus.c
+@@ -55,6 +55,8 @@ const VMStateDescription vmstate_usb_device = {
+     .minimum_version_id = 1,
+     .post_load = usb_device_post_load,
+     .fields = (VMStateField []) {
++        VMSTATE_INT32(speed, USBDevice),
++        VMSTATE_INT32(speedmask, USBDevice),
+         VMSTATE_UINT8(addr, USBDevice),
+         VMSTATE_INT32(state, USBDevice),
+         VMSTATE_INT32(remote_wakeup, USBDevice),
+-- 
+1.7.12
+
diff --git a/0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch b/0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch
new file mode 100644
index 0000000..ee0c0f9
--- /dev/null
+++ b/0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch
@@ -0,0 +1,184 @@
+From 4d7d1b57fa5d3a950818e5dee459aefa4dc6ae27 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 4 Sep 2012 14:18:34 +0200
+Subject: [PATCH 362/366] usb-redir: Change cancelled packet code into a
+ generic packet-id queue
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/redirect.c | 102 +++++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 71 insertions(+), 31 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 9cbcddb..08776d9 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -43,7 +43,6 @@
+ #define EP2I(ep_address) (((ep_address & 0x80) >> 3) | (ep_address & 0x0f))
+ #define I2EP(i) (((i & 0x10) << 3) | (i & 0x0f))
+ 
+-typedef struct Cancelled Cancelled;
+ typedef struct USBRedirDevice USBRedirDevice;
+ 
+ /* Struct to hold buffered packets (iso or int input packets) */
+@@ -69,6 +68,18 @@ struct endp_data {
+     int bufpq_target_size;
+ };
+ 
++struct PacketIdQueueEntry {
++    uint64_t id;
++    QTAILQ_ENTRY(PacketIdQueueEntry)next;
++};
++
++struct PacketIdQueue {
++    USBRedirDevice *dev;
++    const char *name;
++    QTAILQ_HEAD(, PacketIdQueueEntry) head;
++    int size;
++};
++
+ struct USBRedirDevice {
+     USBDevice dev;
+     /* Properties */
+@@ -86,7 +97,7 @@ struct USBRedirDevice {
+     int64_t next_attach_time;
+     struct usbredirparser *parser;
+     struct endp_data endpoint[MAX_ENDPOINTS];
+-    QTAILQ_HEAD(, Cancelled) cancelled;
++    struct PacketIdQueue cancelled;
+     /* Data for device filtering */
+     struct usb_redir_device_connect_header device_info;
+     struct usb_redir_interface_info_header interface_info;
+@@ -94,11 +105,6 @@ struct USBRedirDevice {
+     int filter_rules_count;
+ };
+ 
+-struct Cancelled {
+-    uint64_t id;
+-    QTAILQ_ENTRY(Cancelled)next;
+-};
+-
+ static void usbredir_hello(void *priv, struct usb_redir_hello_header *h);
+ static void usbredir_device_connect(void *priv,
+     struct usb_redir_device_connect_header *device_connect);
+@@ -249,37 +255,75 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
+  * Cancelled and buffered packets helpers
+  */
+ 
+-static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
++static void packet_id_queue_init(struct PacketIdQueue *q,
++    USBRedirDevice *dev, const char *name)
+ {
+-    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+-    Cancelled *c;
++    q->dev = dev;
++    q->name = name;
++    QTAILQ_INIT(&q->head);
++    q->size = 0;
++}
++
++static void packet_id_queue_add(struct PacketIdQueue *q, uint64_t id)
++{
++    USBRedirDevice *dev = q->dev;
++    struct PacketIdQueueEntry *e;
++
++    DPRINTF("adding packet id %"PRIu64" to %s queue\n", id, q->name);
++
++    e = g_malloc0(sizeof(struct PacketIdQueueEntry));
++    e->id = id;
++    QTAILQ_INSERT_TAIL(&q->head, e, next);
++    q->size++;
++}
++
++static int packet_id_queue_remove(struct PacketIdQueue *q, uint64_t id)
++{
++    USBRedirDevice *dev = q->dev;
++    struct PacketIdQueueEntry *e;
++
++    QTAILQ_FOREACH(e, &q->head, next) {
++        if (e->id == id) {
++            DPRINTF("removing packet id %"PRIu64" from %s queue\n",
++                    id, q->name);
++            QTAILQ_REMOVE(&q->head, e, next);
++            q->size--;
++            g_free(e);
++            return 1;
++        }
++    }
++    return 0;
++}
++
++static void packet_id_queue_empty(struct PacketIdQueue *q)
++{
++    USBRedirDevice *dev = q->dev;
++    struct PacketIdQueueEntry *e, *next_e;
+ 
+-    DPRINTF("cancel packet id %"PRIu64"\n", p->id);
++    DPRINTF("removing %d packet-ids from %s queue\n", q->size, q->name);
+ 
+-    c = g_malloc0(sizeof(Cancelled));
+-    c->id = p->id;
+-    QTAILQ_INSERT_TAIL(&dev->cancelled, c, next);
++    QTAILQ_FOREACH_SAFE(e, &q->head, next, next_e) {
++        QTAILQ_REMOVE(&q->head, e, next);
++        g_free(e);
++    }
++    q->size = 0;
++}
+ 
++static void usbredir_cancel_packet(USBDevice *udev, USBPacket *p)
++{
++    USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
++
++    packet_id_queue_add(&dev->cancelled, p->id);
+     usbredirparser_send_cancel_data_packet(dev->parser, p->id);
+     usbredirparser_do_write(dev->parser);
+ }
+ 
+ static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id)
+ {
+-    Cancelled *c;
+-
+     if (!dev->dev.attached) {
+         return 1; /* Treat everything as cancelled after a disconnect */
+     }
+-
+-    QTAILQ_FOREACH(c, &dev->cancelled, next) {
+-        if (c->id == id) {
+-            QTAILQ_REMOVE(&dev->cancelled, c, next);
+-            g_free(c);
+-            return 1;
+-        }
+-    }
+-    return 0;
++    return packet_id_queue_remove(&dev->cancelled, id);
+ }
+ 
+ static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
+@@ -942,7 +986,7 @@ static int usbredir_initfn(USBDevice *udev)
+     dev->chardev_close_bh = qemu_bh_new(usbredir_chardev_close_bh, dev);
+     dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+ 
+-    QTAILQ_INIT(&dev->cancelled);
++    packet_id_queue_init(&dev->cancelled, dev, "cancelled");
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         QTAILQ_INIT(&dev->endpoint[i].bufpq);
+     }
+@@ -960,13 +1004,9 @@ static int usbredir_initfn(USBDevice *udev)
+ 
+ static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
+ {
+-    Cancelled *c, *next_c;
+     int i;
+ 
+-    QTAILQ_FOREACH_SAFE(c, &dev->cancelled, next, next_c) {
+-        QTAILQ_REMOVE(&dev->cancelled, c, next);
+-        g_free(c);
+-    }
++    packet_id_queue_empty(&dev->cancelled);
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         usbredir_free_bufpq(dev, I2EP(i));
+     }
+-- 
+1.7.12
+
diff --git a/0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch b/0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch
new file mode 100644
index 0000000..9fea679
--- /dev/null
+++ b/0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch
@@ -0,0 +1,119 @@
+From 4e0fcd7ef28b0ee06fd6f9d736dbdccefcf0b2bf Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 4 Sep 2012 17:03:54 +0200
+Subject: [PATCH 363/366] usb-redir: Add an already_in_flight packet-id queue
+
+After a live migration, the usb-hcd will re-queue all packets by
+walking over the schedule in the guest memory again, but requests which
+were encountered on the migration source before will already be in flight,
+so these should *not* be re-send to the usbredir-host.
+
+This patch adds an already in flight packet ud queue, which will be filled by
+the source before migration and then moved over to the migration dest, any
+async handled packets are then checked against this queue to avoid sending
+the same packet to the usbredir-host twice.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat,com>
+---
+ hw/usb/redirect.c | 43 +++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 43 insertions(+)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 08776d9..1c8edd3 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -98,6 +98,7 @@ struct USBRedirDevice {
+     struct usbredirparser *parser;
+     struct endp_data endpoint[MAX_ENDPOINTS];
+     struct PacketIdQueue cancelled;
++    struct PacketIdQueue already_in_flight;
+     /* Data for device filtering */
+     struct usb_redir_device_connect_header device_info;
+     struct usb_redir_interface_info_header interface_info;
+@@ -326,6 +327,34 @@ static int usbredir_is_cancelled(USBRedirDevice *dev, uint64_t id)
+     return packet_id_queue_remove(&dev->cancelled, id);
+ }
+ 
++static void usbredir_fill_already_in_flight_from_ep(USBRedirDevice *dev,
++    struct USBEndpoint *ep)
++{
++    static USBPacket *p;
++
++    QTAILQ_FOREACH(p, &ep->queue, queue) {
++        packet_id_queue_add(&dev->already_in_flight, p->id);
++    }
++}
++
++static void usbredir_fill_already_in_flight(USBRedirDevice *dev)
++{
++    int ep;
++    struct USBDevice *udev = &dev->dev;
++
++    usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_ctl);
++
++    for (ep = 0; ep < USB_MAX_ENDPOINTS; ep++) {
++        usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_in[ep]);
++        usbredir_fill_already_in_flight_from_ep(dev, &udev->ep_out[ep]);
++    }
++}
++
++static int usbredir_already_in_flight(USBRedirDevice *dev, uint64_t id)
++{
++    return packet_id_queue_remove(&dev->already_in_flight, id);
++}
++
+ static USBPacket *usbredir_find_packet_by_id(USBRedirDevice *dev,
+     uint8_t ep, uint64_t id)
+ {
+@@ -541,6 +570,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+ 
+     DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, p->iov.size, p->id);
+ 
++    if (usbredir_already_in_flight(dev, p->id)) {
++        return USB_RET_ASYNC;
++    }
++
+     bulk_packet.endpoint  = ep;
+     bulk_packet.length    = p->iov.size;
+     bulk_packet.stream_id = 0;
+@@ -621,6 +654,10 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+         DPRINTF("interrupt-out ep %02X len %zd id %"PRIu64"\n", ep,
+                 p->iov.size, p->id);
+ 
++        if (usbredir_already_in_flight(dev, p->id)) {
++            return USB_RET_ASYNC;
++        }
++
+         interrupt_packet.endpoint  = ep;
+         interrupt_packet.length    = p->iov.size;
+ 
+@@ -763,6 +800,10 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+     struct usb_redir_control_packet_header control_packet;
+ 
++    if (usbredir_already_in_flight(dev, p->id)) {
++        return USB_RET_ASYNC;
++    }
++
+     /* Special cases for certain standard device requests */
+     switch (request) {
+     case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+@@ -987,6 +1028,7 @@ static int usbredir_initfn(USBDevice *udev)
+     dev->attach_timer = qemu_new_timer_ms(vm_clock, usbredir_do_attach, dev);
+ 
+     packet_id_queue_init(&dev->cancelled, dev, "cancelled");
++    packet_id_queue_init(&dev->already_in_flight, dev, "already-in-flight");
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         QTAILQ_INIT(&dev->endpoint[i].bufpq);
+     }
+@@ -1007,6 +1049,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev)
+     int i;
+ 
+     packet_id_queue_empty(&dev->cancelled);
++    packet_id_queue_empty(&dev->already_in_flight);
+     for (i = 0; i < MAX_ENDPOINTS; i++) {
+         usbredir_free_bufpq(dev, I2EP(i));
+     }
+-- 
+1.7.12
+
diff --git a/0364-usb-redir-Store-max_packet_size-in-endp_data.patch b/0364-usb-redir-Store-max_packet_size-in-endp_data.patch
new file mode 100644
index 0000000..6976b2c
--- /dev/null
+++ b/0364-usb-redir-Store-max_packet_size-in-endp_data.patch
@@ -0,0 +1,38 @@
+From 372d6c55bbe5fb092ff9e96c72ff53bfd10fdede Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Thu, 6 Sep 2012 20:52:36 +0200
+Subject: [PATCH 364/366] usb-redir: Store max_packet_size in endp_data
+
+So that we've a place to migrate it to / from to allow restoring it after
+migration.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/redirect.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 1c8edd3..d8568ae 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -57,6 +57,7 @@ struct endp_data {
+     uint8_t type;
+     uint8_t interval;
+     uint8_t interface; /* bInterfaceNumber this ep belongs to */
++    uint16_t max_packet_size; /* In bytes, not wMaxPacketSize format !! */
+     uint8_t iso_started;
+     uint8_t iso_error; /* For reporting iso errors to the HC */
+     uint8_t interrupt_started;
+@@ -1305,7 +1306,8 @@ static void usbredir_ep_info(void *priv,
+         usb_ep->ifnum = dev->endpoint[i].interface;
+         if (usbredirparser_peer_has_cap(dev->parser,
+                                      usb_redir_cap_ep_info_max_packet_size)) {
+-            usb_ep->max_packet_size = ep_info->max_packet_size[i];
++            dev->endpoint[i].max_packet_size =
++                usb_ep->max_packet_size = ep_info->max_packet_size[i];
+         }
+         if (ep_info->type[i] == usb_redir_type_bulk) {
+             usb_ep->pipeline = true;
+-- 
+1.7.12
+
diff --git a/0365-usb-redir-Add-support-for-migration.patch b/0365-usb-redir-Add-support-for-migration.patch
new file mode 100644
index 0000000..97b8ab2
--- /dev/null
+++ b/0365-usb-redir-Add-support-for-migration.patch
@@ -0,0 +1,411 @@
+From e6a683c844845c53bcf14f9fb3ea175331eaca0c Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 5 Sep 2012 09:21:44 +0200
+Subject: [PATCH 365/366] usb-redir: Add support for migration
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/redirect.c | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 328 insertions(+), 3 deletions(-)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index d8568ae..8dbb722 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -65,8 +65,8 @@ struct endp_data {
+     uint8_t bufpq_prefilled;
+     uint8_t bufpq_dropping_packets;
+     QTAILQ_HEAD(, buf_packet) bufpq;
+-    int bufpq_size;
+-    int bufpq_target_size;
++    int32_t bufpq_size;
++    int32_t bufpq_target_size;
+ };
+ 
+ struct PacketIdQueueEntry {
+@@ -241,6 +241,11 @@ static int usbredir_write(void *priv, uint8_t *data, int count)
+         return 0;
+     }
+ 
++    /* Don't send new data to the chardev until our state is fully synced */
++    if (!runstate_check(RUN_STATE_RUNNING)) {
++        return 0;
++    }
++
+     r = qemu_chr_fe_write(dev->cs, data, count);
+ 
+     if (r < 0) {
+@@ -868,6 +873,7 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
+ {
+     uint32_t caps[USB_REDIR_CAPS_SIZE] = { 0, };
+     char version[32];
++    int flags = 0;
+ 
+     /* Make sure any pending closes are handled (no-op if none pending) */
+     usbredir_chardev_close_bh(dev);
+@@ -903,7 +909,12 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
+     usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+-    usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE, 0);
++
++    if (runstate_check(RUN_STATE_INMIGRATE)) {
++        flags |= usbredirparser_fl_no_hello;
++    }
++    usbredirparser_init(dev->parser, version, caps, USB_REDIR_CAPS_SIZE,
++                        flags);
+     usbredirparser_do_write(dev->parser);
+ }
+ 
+@@ -949,6 +960,11 @@ static int usbredir_chardev_can_read(void *opaque)
+         return 0;
+     }
+ 
++    /* Don't read new data from the chardev until our state is fully synced */
++    if (!runstate_check(RUN_STATE_RUNNING)) {
++        return 0;
++    }
++
+     /* usbredir_parser_do_read will consume *all* data we give it */
+     return 1024 * 1024;
+ }
+@@ -1004,6 +1020,15 @@ static const QemuChrHandlers usbredir_chr_handlers = {
+  * init + destroy
+  */
+ 
++static void usbredir_vm_state_change(void *priv, int running, RunState state)
++{
++    USBRedirDevice *dev = priv;
++
++    if (state == RUN_STATE_RUNNING && dev->parser != NULL) {
++        usbredirparser_do_write(dev->parser); /* Flush any pending writes */
++    }
++}
++
+ static int usbredir_initfn(USBDevice *udev)
+ {
+     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
+@@ -1041,6 +1066,7 @@ static int usbredir_initfn(USBDevice *udev)
+     qemu_chr_fe_open(dev->cs);
+     qemu_chr_add_handlers(dev->cs, &usbredir_chr_handlers, dev);
+ 
++    qemu_add_vm_change_state_handler(usbredir_vm_state_change, dev);
+     add_boot_device_path(dev->bootindex, &udev->qdev, NULL);
+     return 0;
+ }
+@@ -1530,6 +1556,304 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
+     }
+ }
+ 
++/*
++ * Migration code
++ */
++
++static void usbredir_pre_save(void *priv)
++{
++    USBRedirDevice *dev = priv;
++
++    usbredir_fill_already_in_flight(dev);
++}
++
++static int usbredir_post_load(void *priv, int version_id)
++{
++    USBRedirDevice *dev = priv;
++    struct USBEndpoint *usb_ep;
++    int i;
++
++    for (i = 0; i < MAX_ENDPOINTS; i++) {
++        usb_ep = usb_ep_get(&dev->dev,
++                            (i & 0x10) ? USB_TOKEN_IN : USB_TOKEN_OUT,
++                            i & 0x0f);
++        usb_ep->type = dev->endpoint[i].type;
++        usb_ep->ifnum = dev->endpoint[i].interface;
++        usb_ep->max_packet_size = dev->endpoint[i].max_packet_size;
++        if (dev->endpoint[i].type == usb_redir_type_bulk) {
++            usb_ep->pipeline = true;
++        }
++    }
++    return 0;
++}
++
++/* For usbredirparser migration */
++static void usbredir_put_parser(QEMUFile *f, void *priv, size_t unused)
++{
++    USBRedirDevice *dev = priv;
++    uint8_t *data;
++    int len;
++
++    if (dev->parser == NULL) {
++        qemu_put_be32(f, 0);
++        return;
++    }
++
++    usbredirparser_serialize(dev->parser, &data, &len);
++    qemu_oom_check(data);
++
++    qemu_put_be32(f, len);
++    qemu_put_buffer(f, data, len);
++
++    free(data);
++}
++
++static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused)
++{
++    USBRedirDevice *dev = priv;
++    uint8_t *data;
++    int len, ret;
++
++    len = qemu_get_be32(f);
++    if (len == 0) {
++        return 0;
++    }
++
++    /*
++     * Our chardev should be open already at this point, otherwise
++     * the usbredir channel will be broken (ie spice without seamless)
++     */
++    if (dev->parser == NULL) {
++        ERROR("get_parser called with closed chardev, failing migration\n");
++        return -1;
++    }
++
++    data = g_malloc(len);
++    qemu_get_buffer(f, data, len);
++
++    ret = usbredirparser_unserialize(dev->parser, data, len);
++
++    g_free(data);
++
++    return ret;
++}
++
++static const VMStateInfo usbredir_parser_vmstate_info = {
++    .name = "usb-redir-parser",
++    .put  = usbredir_put_parser,
++    .get  = usbredir_get_parser,
++};
++
++
++/* For buffered packets (iso/irq) queue migration */
++static void usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused)
++{
++    struct endp_data *endp = priv;
++    struct buf_packet *bufp;
++    int remain = endp->bufpq_size;
++
++    qemu_put_be32(f, endp->bufpq_size);
++    QTAILQ_FOREACH(bufp, &endp->bufpq, next) {
++        qemu_put_be32(f, bufp->len);
++        qemu_put_be32(f, bufp->status);
++        qemu_put_buffer(f, bufp->data, bufp->len);
++        remain--;
++    }
++    assert(remain == 0);
++}
++
++static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused)
++{
++    struct endp_data *endp = priv;
++    struct buf_packet *bufp;
++    int i;
++
++    endp->bufpq_size = qemu_get_be32(f);
++    for (i = 0; i < endp->bufpq_size; i++) {
++        bufp = g_malloc(sizeof(struct buf_packet));
++        bufp->len = qemu_get_be32(f);
++        bufp->status = qemu_get_be32(f);
++        bufp->data = qemu_oom_check(malloc(bufp->len)); /* regular malloc! */
++        qemu_get_buffer(f, bufp->data, bufp->len);
++        QTAILQ_INSERT_TAIL(&endp->bufpq, bufp, next);
++    }
++    return 0;
++}
++
++static const VMStateInfo usbredir_ep_bufpq_vmstate_info = {
++    .name = "usb-redir-bufpq",
++    .put  = usbredir_put_bufpq,
++    .get  = usbredir_get_bufpq,
++};
++
++
++/* For endp_data migration */
++static const VMStateDescription usbredir_ep_vmstate = {
++    .name = "usb-redir-ep",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .fields = (VMStateField []) {
++        VMSTATE_UINT8(type, struct endp_data),
++        VMSTATE_UINT8(interval, struct endp_data),
++        VMSTATE_UINT8(interface, struct endp_data),
++        VMSTATE_UINT16(max_packet_size, struct endp_data),
++        VMSTATE_UINT8(iso_started, struct endp_data),
++        VMSTATE_UINT8(iso_error, struct endp_data),
++        VMSTATE_UINT8(interrupt_started, struct endp_data),
++        VMSTATE_UINT8(interrupt_error, struct endp_data),
++        VMSTATE_UINT8(bufpq_prefilled, struct endp_data),
++        VMSTATE_UINT8(bufpq_dropping_packets, struct endp_data),
++        {
++            .name         = "bufpq",
++            .version_id   = 0,
++            .field_exists = NULL,
++            .size         = 0,
++            .info         = &usbredir_ep_bufpq_vmstate_info,
++            .flags        = VMS_SINGLE,
++            .offset       = 0,
++        },
++        VMSTATE_INT32(bufpq_target_size, struct endp_data),
++        VMSTATE_END_OF_LIST()
++    }
++};
++
++
++/* For PacketIdQueue migration */
++static void usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused)
++{
++    struct PacketIdQueue *q = priv;
++    USBRedirDevice *dev = q->dev;
++    struct PacketIdQueueEntry *e;
++    int remain = q->size;
++
++    DPRINTF("put_packet_id_q %s size %d\n", q->name, q->size);
++    qemu_put_be32(f, q->size);
++    QTAILQ_FOREACH(e, &q->head, next) {
++        qemu_put_be64(f, e->id);
++        remain--;
++    }
++    assert(remain == 0);
++}
++
++static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused)
++{
++    struct PacketIdQueue *q = priv;
++    USBRedirDevice *dev = q->dev;
++    int i, size;
++    uint64_t id;
++
++    size = qemu_get_be32(f);
++    DPRINTF("get_packet_id_q %s size %d\n", q->name, size);
++    for (i = 0; i < size; i++) {
++        id = qemu_get_be64(f);
++        packet_id_queue_add(q, id);
++    }
++    assert(q->size == size);
++    return 0;
++}
++
++static const VMStateInfo usbredir_ep_packet_id_q_vmstate_info = {
++    .name = "usb-redir-packet-id-q",
++    .put  = usbredir_put_packet_id_q,
++    .get  = usbredir_get_packet_id_q,
++};
++
++static const VMStateDescription usbredir_ep_packet_id_queue_vmstate = {
++    .name = "usb-redir-packet-id-queue",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .fields = (VMStateField []) {
++        {
++            .name         = "queue",
++            .version_id   = 0,
++            .field_exists = NULL,
++            .size         = 0,
++            .info         = &usbredir_ep_packet_id_q_vmstate_info,
++            .flags        = VMS_SINGLE,
++            .offset       = 0,
++        },
++        VMSTATE_END_OF_LIST()
++    }
++};
++
++
++/* For usb_redir_device_connect_header migration */
++static const VMStateDescription usbredir_device_info_vmstate = {
++    .name = "usb-redir-device-info",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .fields = (VMStateField []) {
++        VMSTATE_UINT8(speed, struct usb_redir_device_connect_header),
++        VMSTATE_UINT8(device_class, struct usb_redir_device_connect_header),
++        VMSTATE_UINT8(device_subclass, struct usb_redir_device_connect_header),
++        VMSTATE_UINT8(device_protocol, struct usb_redir_device_connect_header),
++        VMSTATE_UINT16(vendor_id, struct usb_redir_device_connect_header),
++        VMSTATE_UINT16(product_id, struct usb_redir_device_connect_header),
++        VMSTATE_UINT16(device_version_bcd,
++                       struct usb_redir_device_connect_header),
++        VMSTATE_END_OF_LIST()
++    }
++};
++
++
++/* For usb_redir_interface_info_header migration */
++static const VMStateDescription usbredir_interface_info_vmstate = {
++    .name = "usb-redir-interface-info",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .fields = (VMStateField []) {
++        VMSTATE_UINT32(interface_count,
++                       struct usb_redir_interface_info_header),
++        VMSTATE_UINT8_ARRAY(interface,
++                            struct usb_redir_interface_info_header, 32),
++        VMSTATE_UINT8_ARRAY(interface_class,
++                            struct usb_redir_interface_info_header, 32),
++        VMSTATE_UINT8_ARRAY(interface_subclass,
++                            struct usb_redir_interface_info_header, 32),
++        VMSTATE_UINT8_ARRAY(interface_protocol,
++                            struct usb_redir_interface_info_header, 32),
++        VMSTATE_END_OF_LIST()
++    }
++};
++
++
++/* And finally the USBRedirDevice vmstate itself */
++static const VMStateDescription usbredir_vmstate = {
++    .name = "usb-redir",
++    .version_id = 1,
++    .minimum_version_id = 1,
++    .pre_save = usbredir_pre_save,
++    .post_load = usbredir_post_load,
++    .fields = (VMStateField []) {
++        VMSTATE_USB_DEVICE(dev, USBRedirDevice),
++        VMSTATE_TIMER(attach_timer, USBRedirDevice),
++        {
++            .name         = "parser",
++            .version_id   = 0,
++            .field_exists = NULL,
++            .size         = 0,
++            .info         = &usbredir_parser_vmstate_info,
++            .flags        = VMS_SINGLE,
++            .offset       = 0,
++        },
++        VMSTATE_STRUCT_ARRAY(endpoint, USBRedirDevice, MAX_ENDPOINTS, 1,
++                             usbredir_ep_vmstate, struct endp_data),
++        VMSTATE_STRUCT(cancelled, USBRedirDevice, 1,
++                       usbredir_ep_packet_id_queue_vmstate,
++                       struct PacketIdQueue),
++        VMSTATE_STRUCT(already_in_flight, USBRedirDevice, 1,
++                       usbredir_ep_packet_id_queue_vmstate,
++                       struct PacketIdQueue),
++        VMSTATE_STRUCT(device_info, USBRedirDevice, 1,
++                       usbredir_device_info_vmstate,
++                       struct usb_redir_device_connect_header),
++        VMSTATE_STRUCT(interface_info, USBRedirDevice, 1,
++                       usbredir_interface_info_vmstate,
++                       struct usb_redir_interface_info_header),
++        VMSTATE_END_OF_LIST()
++    }
++};
++
+ static Property usbredir_properties[] = {
+     DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
+     DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, 0),
+@@ -1550,6 +1874,7 @@ static void usbredir_class_initfn(ObjectClass *klass, void *data)
+     uc->handle_reset   = usbredir_handle_reset;
+     uc->handle_data    = usbredir_handle_data;
+     uc->handle_control = usbredir_handle_control;
++    dc->vmsd           = &usbredir_vmstate;
+     dc->props          = usbredir_properties;
+ }
+ 
+-- 
+1.7.12
+
diff --git a/0366-usb-redir-Add-chardev-open-close-debug-logging.patch b/0366-usb-redir-Add-chardev-open-close-debug-logging.patch
new file mode 100644
index 0000000..42719c9
--- /dev/null
+++ b/0366-usb-redir-Add-chardev-open-close-debug-logging.patch
@@ -0,0 +1,54 @@
+From 93691f50a5200651f22e698cc29be0b1a020f5c5 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 5 Sep 2012 15:56:57 +0200
+Subject: [PATCH 366/366] usb-redir: Add chardev open / close debug logging
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+ hw/usb/redirect.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
+index 8dbb722..95a2167 100644
+--- a/hw/usb/redirect.c
++++ b/hw/usb/redirect.c
+@@ -864,6 +864,7 @@ static void usbredir_chardev_close_bh(void *opaque)
+     usbredir_device_disconnect(dev);
+ 
+     if (dev->parser) {
++        DPRINTF("destroying usbredirparser\n");
+         usbredirparser_destroy(dev->parser);
+         dev->parser = NULL;
+     }
+@@ -879,6 +880,8 @@ static void usbredir_chardev_open(USBRedirDevice *dev)
+     usbredir_chardev_close_bh(dev);
+     qemu_bh_cancel(dev->chardev_close_bh);
+ 
++    DPRINTF("creating usbredirparser\n");
++
+     strcpy(version, "qemu usb-redir guest ");
+     pstrcat(version, sizeof(version), qemu_get_version());
+ 
+@@ -990,9 +993,11 @@ static void usbredir_chardev_event(void *opaque, int event)
+ 
+     switch (event) {
+     case CHR_EVENT_OPENED:
++        DPRINTF("chardev open\n");
+         usbredir_chardev_open(dev);
+         break;
+     case CHR_EVENT_CLOSED:
++        DPRINTF("chardev close\n");
+         qemu_bh_schedule(dev->chardev_close_bh);
+         break;
+     }
+@@ -1255,6 +1260,7 @@ static void usbredir_device_disconnect(void *priv)
+     qemu_del_timer(dev->attach_timer);
+ 
+     if (dev->dev.attached) {
++        DPRINTF("detaching device\n");
+         usb_device_detach(&dev->dev);
+         /*
+          * Delay next usb device attach to give the guest a chance to see
+-- 
+1.7.12
+
diff --git a/qemu.spec b/qemu.spec
index de64f2a..452f280 100644
--- a/qemu.spec
+++ b/qemu.spec
@@ -34,12 +34,10 @@
 %bcond_without fdt              # enabled
 %endif
 
-%global rcversion rc1
-
 Summary: QEMU is a FAST! processor emulator
 Name: qemu
 Version: 1.2.0
-Release: 0.5.%{rcversion}%{?dist}
+Release: 1%{?dist}
 # Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
 Epoch: 2
 License: GPLv2+ and LGPLv2+ and BSD
@@ -55,7 +53,10 @@ ExclusiveArch: x86_64
 %define _smp_mflags %{nil}
 %endif
 
-Source0: http://downloads.sourceforge.net/sourceforge/kvm/qemu-kvm-%{version}-%{rcversion}.tar.gz
+# This is generated from the git qemu-kvm-1.2.0 tag, replace with proper
+# upstream tarbal once available
+Source0: qemu-kvm-%{version}.tar.gz
+#Source0: http://downloads.sourceforge.net/sourceforge/kvm/qemu-kvm-%{version}.tar.gz
 
 Source1: qemu.binfmt
 
@@ -98,6 +99,96 @@ Patch111: 0111-usb-redir-Add-flow-control-support.patch
 Patch112: 0112-virtio-serial-bus-replay-guest_open-on-migration.patch
 Patch113: 0113-char-Disable-write-callback-if-throttled-chardev-is-.patch
 
+# Spice features from upstream master: seamless migration & dynamic monitors
+Patch201: 0201-spice-abort-on-invalid-streaming-cmdline-params.patch
+Patch202: 0202-spice-notify-spice-server-on-vm-start-stop.patch
+Patch203: 0203-spice-notify-on-vm-state-change-only-via-spice_serve.patch
+Patch204: 0204-spice-migration-add-QEVENT_SPICE_MIGRATE_COMPLETED.patch
+Patch205: 0205-spice-add-migrated-flag-to-spice-info.patch
+Patch206: 0206-spice-adding-seamless-migration-option-to-the-comman.patch
+Patch207: 0207-spice-increase-the-verbosity-of-spice-section-in-qem.patch
+Patch208: 0208-qxl-update_area_io-guest_bug-on-invalid-parameters.patch
+Patch209: 0209-qxl-disallow-unknown-revisions.patch
+Patch210: 0210-qxl-add-QXL_IO_MONITORS_CONFIG_ASYNC.patch
+Patch211: 0211-configure-print-spice-protocol-and-spice-server-vers.patch
+Patch212: 0212-spice-make-number-of-surfaces-runtime-configurable.patch
+Patch213: 0213-qxl-Add-set_client_capabilities-interface-to-QXLInte.patch
+Patch214: 0214-Remove-ifdef-QXL_COMMAND_FLAG_COMPAT_16BPP.patch
+Patch215: 0215-qxl-dont-update-invalid-area.patch
+
+# Ugh, ton of USB bugfixes / preparation patches for usb-redir
+# live-migration which did not make 1.2.0 :|
+# All are in upstream master so can be dropped next qemu release
+Patch0301: 0301-usb-controllers-do-not-need-to-check-for-babble-them.patch
+Patch0302: 0302-usb-core-Don-t-set-packet-state-to-complete-on-a-nak.patch
+Patch0303: 0303-usb-core-Add-a-usb_ep_find_packet_by_id-helper-funct.patch
+Patch0304: 0304-usb-core-Allow-the-first-packet-of-a-pipelined-ep-to.patch
+Patch0305: 0305-Revert-ehci-don-t-flush-cache-on-doorbell-rings.patch
+Patch0306: 0306-ehci-Validate-qh-is-not-changed-unexpectedly-by-the-.patch
+Patch0307: 0307-ehci-Update-copyright-headers-to-reflect-recent-work.patch
+Patch0308: 0308-ehci-Properly-cleanup-packets-on-cancel.patch
+Patch0309: 0309-ehci-Properly-report-completed-but-not-yet-processed.patch
+Patch0310: 0310-ehci-check-for-EHCI_ASYNC_FINISHED-first-in-ehci_fre.patch
+Patch0311: 0311-ehci-trace-guest-bugs.patch
+Patch0312: 0312-ehci-add-doorbell-trace-events.patch
+Patch0313: 0313-ehci-Add-some-additional-ehci_trace_guest_bug-calls.patch
+Patch0314: 0314-ehci-Fix-memory-leak-in-handling-of-NAK-ed-packets.patch
+Patch0315: 0315-ehci-Handle-USB_RET_PROCERR-in-ehci_fill_queue.patch
+Patch0316: 0316-ehci-Correct-a-comment-in-fetchqtd-packet-processing.patch
+Patch0317: 0317-usb-redir-Never-return-USB_RET_NAK-for-async-handled.patch
+Patch0318: 0318-usb-redir-Don-t-delay-handling-of-open-events-to-a-b.patch
+Patch0319: 0319-usb-redir-Get-rid-of-async-struct-get-member.patch
+Patch0320: 0320-usb-redir-Get-rid-of-local-shadow-copy-of-packet-hea.patch
+Patch0321: 0321-usb-redir-Get-rid-of-unused-async-struct-dev-member.patch
+Patch0322: 0322-usb-redir-Move-to-core-packet-id-and-queue-handling.patch
+Patch0323: 0323-usb-redir-Return-babble-when-getting-more-bulk-data-.patch
+Patch0324: 0324-usb-redir-Convert-to-new-libusbredirparser-0.5-API.patch
+Patch0325: 0325-usb-redir-Set-ep-max_packet_size-if-available.patch
+Patch0326: 0326-usb-redir-Add-a-usbredir_reject_device-helper-functi.patch
+Patch0327: 0327-usb-redir-Ensure-our-peer-has-the-necessary-caps-whe.patch
+Patch0328: 0328-usb-redir-Enable-pipelining-for-bulk-endpoints.patch
+Patch0329: 0329-Better-name-usb-braille-device.patch
+Patch0330: 0330-usb-audio-fix-usb-version.patch
+Patch0331: 0331-xhci-rip-out-background-transfer-code.patch
+Patch0332: 0332-xhci-drop-buffering.patch
+Patch0333: 0333-xhci-move-device-lookup-into-xhci_setup_packet.patch
+Patch0334: 0334-xhci-implement-mfindex.patch
+Patch0335: 0335-xhci-iso-xfer-support.patch
+Patch0336: 0336-xhci-trace-cc-codes-in-cleartext.patch
+Patch0337: 0337-xhci-add-trace_usb_xhci_ep_set_dequeue.patch
+Patch0338: 0338-xhci-fix-runtime-write-tracepoint.patch
+Patch0339: 0339-xhci-update-register-layout.patch
+Patch0340: 0340-xhci-update-port-handling.patch
+Patch0341: 0341-usb3-superspeed-descriptors.patch
+Patch0342: 0342-usb3-superspeed-endpoint-companion.patch
+Patch0343: 0343-usb3-bos-decriptor.patch
+Patch0344: 0344-usb-storage-usb3-support.patch
+Patch0345: 0345-xhci-fix-cleanup-msi.patch
+Patch0346: 0346-xhci-rework-interrupt-handling.patch
+Patch0347: 0347-xhci-add-msix-support.patch
+Patch0348: 0348-xhci-move-register-update-into-xhci_intr_raise.patch
+Patch0349: 0349-xhci-add-XHCIInterrupter.patch
+Patch0350: 0350-xhci-prepare-xhci_runtime_-read-write-for-multiple-i.patch
+Patch0351: 0351-xhci-pick-target-interrupter.patch
+Patch0352: 0352-xhci-support-multiple-interrupters.patch
+Patch0353: 0353-xhci-kill-xhci_mem_-read-write-dispatcher-functions.patch
+Patch0354: 0354-xhci-allow-bytewise-capability-register-reads.patch
+Patch0355: 0355-ehci-switch-to-new-style-memory-ops.patch
+Patch0356: 0356-xhci-drop-unused-wlength.patch
+Patch0357: 0357-usb-host-allow-emulated-non-async-control-requests-w.patch
+
+# And the last few ehci fixes + the actual usb-redir live migration code
+# Not yet upstream but should get there real soon
+Patch0358: 0358-ehci-Don-t-set-seen-to-0-when-removing-unseen-queue-.patch
+Patch0359: 0359-ehci-Walk-async-schedule-before-and-after-migration.patch
+Patch0360: 0360-ehci-Don-t-process-too-much-frames-in-1-timer-tick.patch
+Patch0361: 0361-usb-Migrate-over-device-speed-and-speedmask.patch
+Patch0362: 0362-usb-redir-Change-cancelled-packet-code-into-a-generi.patch
+Patch0363: 0363-usb-redir-Add-an-already_in_flight-packet-id-queue.patch
+Patch0364: 0364-usb-redir-Store-max_packet_size-in-endp_data.patch
+Patch0365: 0365-usb-redir-Add-support-for-migration.patch
+Patch0366: 0366-usb-redir-Add-chardev-open-close-debug-logging.patch
+
 BuildRequires: SDL-devel
 BuildRequires: zlib-devel
 BuildRequires: which
@@ -111,11 +202,11 @@ BuildRequires: pciutils-devel
 BuildRequires: pulseaudio-libs-devel
 BuildRequires: ncurses-devel
 BuildRequires: libattr-devel
-BuildRequires: usbredir-devel >= 0.4.1
+BuildRequires: usbredir-devel >= 0.5
 BuildRequires: texinfo
 %ifarch %{ix86} x86_64
-BuildRequires: spice-protocol >= 0.8.1
-BuildRequires: spice-server-devel >= 0.9.0
+BuildRequires: spice-protocol >= 0.12.1
+BuildRequires: spice-server-devel >= 0.11.3
 BuildRequires: libseccomp-devel >= 1.0.0
 %endif
 # For network block driver
@@ -375,7 +466,7 @@ such as kvm_stat.
 %endif
 
 %prep
-%setup -q -n qemu-kvm-%{version}-%{rcversion}
+%setup -q -n qemu-kvm-%{version}
 
 %patch1 -p1
 %patch2 -p1
@@ -394,6 +485,90 @@ such as kvm_stat.
 %patch112 -p1
 %patch113 -p1
 
+%patch201 -p1
+%patch202 -p1
+%patch203 -p1
+%patch204 -p1
+%patch205 -p1
+%patch206 -p1
+%patch207 -p1
+%patch208 -p1
+%patch209 -p1
+%patch210 -p1
+%patch211 -p1
+%patch212 -p1
+%patch213 -p1
+%patch214 -p1
+%patch215 -p1
+
+%patch301 -p1
+%patch302 -p1
+%patch303 -p1
+%patch304 -p1
+%patch305 -p1
+%patch306 -p1
+%patch307 -p1
+%patch308 -p1
+%patch309 -p1
+%patch310 -p1
+%patch311 -p1
+%patch312 -p1
+%patch313 -p1
+%patch314 -p1
+%patch315 -p1
+%patch316 -p1
+%patch317 -p1
+%patch318 -p1
+%patch319 -p1
+%patch320 -p1
+%patch321 -p1
+%patch322 -p1
+%patch323 -p1
+%patch324 -p1
+%patch325 -p1
+%patch326 -p1
+%patch327 -p1
+%patch328 -p1
+%patch329 -p1
+%patch330 -p1
+%patch331 -p1
+%patch332 -p1
+%patch333 -p1
+%patch334 -p1
+%patch335 -p1
+%patch336 -p1
+%patch337 -p1
+%patch338 -p1
+%patch339 -p1
+%patch340 -p1
+%patch341 -p1
+%patch342 -p1
+%patch343 -p1
+%patch344 -p1
+%patch345 -p1
+%patch346 -p1
+%patch347 -p1
+%patch348 -p1
+%patch349 -p1
+%patch350 -p1
+%patch351 -p1
+%patch352 -p1
+%patch353 -p1
+%patch354 -p1
+%patch355 -p1
+%patch356 -p1
+%patch357 -p1
+%patch358 -p1
+%patch359 -p1
+%patch360 -p1
+%patch361 -p1
+%patch362 -p1
+%patch363 -p1
+%patch364 -p1
+%patch365 -p1
+%patch366 -p1
+
+
 %build
 buildarch="i386-softmmu x86_64-softmmu arm-softmmu cris-softmmu \
     m68k-softmmu mips-softmmu mipsel-softmmu mips64-softmmu \
@@ -871,6 +1046,12 @@ fi
 %{_mandir}/man1/qemu-img.1*
 
 %changelog
+* Fri Sep  9 2012 Hans de Goede <hdegoede@redhat.com> - 2:1.2.0-1
+- New upstream release 1.2.0 final
+- Add support for Spice seamless migration
+- Add support for Spice dynamic monitors
+- Add support for usb-redir live migration
+
 * Tue Sep 04 2012 Adam Jackson <ajax@redhat.com> 1.2.0-0.5.rc1
 - Flip Requires: ceph >= foo to Conflicts: ceph < foo, so we pull in only the
   libraries which we need and not the rest of ceph which we don't.
diff --git a/sources b/sources
index 00beae2..a841146 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-1ec2342c322102b4028b4ca3fbdde737  qemu-kvm-1.2.0-rc1.tar.gz
+2d8f71a31d73d669e9744b65b248a497  qemu-kvm-1.2.0.tar.gz