[Spice-commits] 47 commits - Makefile Makefile.objs block/nbd.c blockdev-nbd.c configure console.h dma.h exec.c hmp-commands.hx hmp.c hmp.h hw/megasas.c hw/mfi.h hw/qxl.c hw/scsi-disk.c hw/usb hw/usb.h hw/vfio_pci.c hw/virtio-scsi.c linux-headers/asm-powerpc linux-headers/asm-s390 linux-headers/asm-x86 linux-headers/linux nbd.c pflib.c pflib.h qemu-doc.texi qemu-nbd.c qemu-pixman.c qemu-pixman.h trace-events ui/spice-display.c ui/spice-display.h

Gerd Hoffmann kraxel at kemper.freedesktop.org
Thu Nov 15 00:20:58 PST 2012


 Makefile                             |    3 
 Makefile.objs                        |    1 
 block/nbd.c                          |  115 ++++++++++++++++--
 blockdev-nbd.c                       |   20 ++-
 configure                            |   24 ++-
 console.h                            |    5 
 dma.h                                |    5 
 exec.c                               |    5 
 hmp-commands.hx                      |   45 +++++++
 hmp.c                                |   76 ++++++++++++
 hmp.h                                |    3 
 hw/megasas.c                         |    2 
 hw/mfi.h                             |    2 
 hw/qxl.c                             |   36 +++++
 hw/scsi-disk.c                       |   44 ++++---
 hw/usb.h                             |   24 ++-
 hw/usb/bus.c                         |   13 --
 hw/usb/combined-packet.c             |   58 +++++----
 hw/usb/core.c                        |  209 +++++++++++++++++----------------
 hw/usb/desc.c                        |   16 +-
 hw/usb/desc.h                        |    3 
 hw/usb/dev-audio.c                   |   49 ++-----
 hw/usb/dev-bluetooth.c               |   33 ++---
 hw/usb/dev-hid.c                     |   42 +++---
 hw/usb/dev-hub.c                     |   34 ++---
 hw/usb/dev-network.c                 |  101 +++++++---------
 hw/usb/dev-serial.c                  |   29 +---
 hw/usb/dev-smartcard-reader.c        |   69 ++++-------
 hw/usb/dev-storage.c                 |   51 +++-----
 hw/usb/dev-uas.c                     |   36 ++---
 hw/usb/dev-wacom.c                   |   38 ++----
 hw/usb/hcd-ehci-pci.c                |    3 
 hw/usb/hcd-ehci.c                    |  216 ++++++++++++++++-------------------
 hw/usb/hcd-ehci.h                    |    1 
 hw/usb/hcd-musb.c                    |   16 +-
 hw/usb/hcd-ohci.c                    |   28 ++--
 hw/usb/hcd-uhci.c                    |   34 ++---
 hw/usb/hcd-xhci.c                    |  129 ++++++++++++--------
 hw/usb/host-bsd.c                    |   27 +---
 hw/usb/host-linux.c                  |  128 +++++++++++---------
 hw/usb/redirect.c                    |  187 +++++++++++++++---------------
 hw/vfio_pci.c                        |  210 ++++++++++++++++++++++++++++++----
 hw/virtio-scsi.c                     |   10 -
 linux-headers/asm-powerpc/kvm_para.h |    6 
 linux-headers/asm-s390/kvm_para.h    |    8 -
 linux-headers/asm-x86/kvm.h          |   17 ++
 linux-headers/linux/kvm.h            |   25 +++-
 linux-headers/linux/kvm_para.h       |    6 
 linux-headers/linux/vfio.h           |    6 
 linux-headers/linux/virtio_config.h  |    6 
 linux-headers/linux/virtio_ring.h    |    6 
 nbd.c                                |   25 +---
 pflib.c                              |  215 ----------------------------------
 pflib.h                              |   20 ---
 qemu-doc.texi                        |   25 ++--
 qemu-nbd.c                           |    2 
 qemu-pixman.c                        |   18 ++
 qemu-pixman.h                        |    7 +
 trace-events                         |    1 
 ui/spice-display.c                   |   53 +++-----
 ui/spice-display.h                   |    7 -
 61 files changed, 1444 insertions(+), 1189 deletions(-)

New commits:
commit ce34cf72fe508b27a78f83c184142e8d1e6a048a
Merge: 662d263... a771c51...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Wed Nov 14 08:53:40 2012 -0600

    Merge remote-tracking branch 'awilliam/tags/vfio-pci-for-qemu-1.3.0-rc0' into staging
    
    vfio-pci: KVM INTx accel & common msi_get_message
    
    * awilliam/tags/vfio-pci-for-qemu-1.3.0-rc0:
      vfio-pci: Use common msi_get_message
      vfio-pci: Add KVM INTx acceleration
      linux-headers: Update to 3.7-rc5
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit 662d263f280f6cfc82263a687b8322b69d2ed7db
Merge: de148eb... feb33ea...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Wed Nov 14 08:53:03 2012 -0600

    Merge remote-tracking branch 'kraxel/pixman.v5' into staging
    
    * kraxel/pixman.v5:
      pixman: cleanup properly on make distclean
      pixman: add licensing info
      pixman: build internal version early
      pixman: pass cflags, add -fPIC
      pixman: disable gtk
      pixman: set --host for cross builds
      pixman: add output dir to include path
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit de148eb79c7894178a1c2da6f2320b9ce6b0daee
Merge: 5f580e9... c8969ed...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Wed Nov 14 08:51:06 2012 -0600

    Merge remote-tracking branch 'bonzini/nbd-next' into staging
    
    * bonzini/nbd-next:
      nbd: fixes to read-only handling
      hmp: add NBD server commands
      nbd: disallow nbd-server-add before nbd-server-start
      nbd: force read-only export for read-only devices
      nbd: fix nbd_server_stop crash when no server was running
      nbd: accept URIs
      nbd: accept relative path to Unix socket
      qemu-nbd: initialize main loop before block layer
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit 5f580e9411a24b7decc07013edeb8abf8d5ae25c
Merge: ce5e5b5... dd72fdd...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Wed Nov 14 08:50:45 2012 -0600

    Merge remote-tracking branch 'bonzini/scsi-next' into staging
    
    * bonzini/scsi-next:
      virtio-scsi: use dma_context_memory
      dma: Define dma_context_memory and use in sysbus-ohci
      megasas: Correct target/lun mapping
      scsi-disk: flush cache after disabling it
      megasas: do not include block_int.h
      scsi: remove superfluous call to scsi_device_set_ua
      virtio-scsi: factor checks for VIRTIO_SCSI_S_DRIVER_OK when reporting events
      scsi: do not return short responses for emulated commands
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit ce5e5b522e8d3b848b6445c34226b69c77c46403
Merge: bf0dfb6... 9d15304...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Wed Nov 14 08:50:18 2012 -0600

    Merge remote-tracking branch 'kraxel/usb.70' into staging
    
    * kraxel/usb.70:
      ehci: fix migration
      xhci: Fix some DMA host endian bugs
      usb/combined-packet: Move freeing of combined to usb_combined_packet_remove()
      xhci: Add support for packets with both data and an error status
      ehci: Add support for packets with both data and an error status
      ehci: Get rid of the magical PROC_ERR status
      usb-redir: Allow packets to have both data and an error-status
      usb: split packet result into actual_length + status
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit bf0dfb69f860a7894958068fba45f5268dddd6be
Merge: f5022a1... bdd4df3...
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Wed Nov 14 08:49:54 2012 -0600

    Merge remote-tracking branch 'spice/spice.v63' into staging
    
    * spice/spice.v63:
      spice: fix initialization order
      pflib: unused, remove it.
      spice: switch to pixman
      qxl: call dpy_gfx_resize when entering vga mode
      qxl: fix cursor reset
      hw/qxl: qxl_send_events: nop if stopped
      hw/qxl: guest bug on primary create with stride %4 != 0
    
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit feb33ea728a72730389bc5d4bf52d33f09c7868b
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Nov 14 13:45:55 2012 +0100

    pixman: cleanup properly on make distclean
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/Makefile b/Makefile
index 4538b87..81c660f 100644
--- a/Makefile
+++ b/Makefile
@@ -278,6 +278,7 @@ distclean: clean
 	for d in $(TARGET_DIRS) $(QEMULIBS); do \
 	rm -rf $$d || exit 1 ; \
         done
+	test -f pixman/config.log && make -C pixman distclean
 
 KEYMAPS=da     en-gb  et  fr     fr-ch  is  lt  modifiers  no  pt-br  sv \
 ar      de     en-us  fi  fr-be  hr     it  lv  nl         pl  ru     th \
commit daa8e5a077e9d8ca2615f5590fa98b3fcc4c9f47
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Nov 13 09:38:06 2012 +0100

    pixman: add licensing info
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/qemu-pixman.c b/qemu-pixman.c
index 7547ed7..063b52d 100644
--- a/qemu-pixman.c
+++ b/qemu-pixman.c
@@ -1,3 +1,8 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
 #include "qemu-pixman.h"
 
 int qemu_pixman_get_type(int rshift, int gshift, int bshift)
diff --git a/qemu-pixman.h b/qemu-pixman.h
index 7652c41..783a392 100644
--- a/qemu-pixman.h
+++ b/qemu-pixman.h
@@ -1,3 +1,8 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
 #ifndef QEMU_PIXMAN_H
 #define QEMU_PIXMAN_H
 
commit b776eca138b641a0234a957a9ff5c2ca4c6772d5
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Mon Nov 12 12:18:38 2012 +0100

    pixman: build internal version early
    
    Signed-off-by: Eric Johnson <ericj at mips.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/configure b/configure
index 0a241eb..f847ee2 100755
--- a/configure
+++ b/configure
@@ -3956,9 +3956,6 @@ if test "$target_softmmu" = "yes" ; then
   if test "$smartcard_nss" = "yes" ; then
     echo "subdir-$target: subdir-libcacard" >> $config_host_mak
   fi
-  if test "$pixman" = "internal" ; then
-    echo "subdir-$target: subdir-pixman" >> $config_host_mak
-  fi
   case "$target_arch2" in
     i386|x86_64)
       echo "CONFIG_HAVE_CORE_DUMP=y" >> $config_target_mak
@@ -4156,6 +4153,10 @@ echo "QEMU_INCLUDES+=$includes" >> $config_target_mak
 
 done # for target in $targets
 
+if [ "$pixman" = "internal" ]; then
+  echo "config-host.h: subdir-pixman" >> $config_host_mak
+fi
+
 # build tree in object directory in case the source is not in the current directory
 DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32"
 DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
commit c28fa5a0b3566b133b5e3336d04f1333970ac179
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Nov 14 13:26:54 2012 +0100

    pixman: pass cflags, add -fPIC
    
    Pass on CFLAGS to the pixman configure script.
    Add -fPIC to the cflags, needed to make the final link succeed.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/Makefile b/Makefile
index e68bb8f..4538b87 100644
--- a/Makefile
+++ b/Makefile
@@ -122,7 +122,7 @@ subdir-pixman: pixman/Makefile
 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,)
 
 pixman/Makefile: $(SRC_PATH)/pixman/configure
-	(cd pixman; $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static)
+	(cd pixman; CFLAGS="$(CFLAGS) -fPIC" $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static)
 
 $(SRC_PATH)/pixman/configure:
 	(cd $(SRC_PATH)/pixman; autoreconf -v --install)
commit 4d5bdd0b460ee2ff8989b0f83fa38a3af052b343
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Nov 7 11:41:01 2012 +0100

    pixman: disable gtk
    
    gtk is only needed to build test cases.
    Disable it to simplify the build.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/Makefile b/Makefile
index ff2c16d..e68bb8f 100644
--- a/Makefile
+++ b/Makefile
@@ -122,7 +122,7 @@ subdir-pixman: pixman/Makefile
 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,)
 
 pixman/Makefile: $(SRC_PATH)/pixman/configure
-	(cd pixman; $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-shared --enable-static)
+	(cd pixman; $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-gtk --disable-shared --enable-static)
 
 $(SRC_PATH)/pixman/configure:
 	(cd $(SRC_PATH)/pixman; autoreconf -v --install)
commit 42da6041d5e30fcf49ba639477a05e781594a7a5
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Nov 7 11:09:52 2012 +0100

    pixman: set --host for cross builds
    
    Set --host when calling pixman configure while doing cross builds so
    pixman's autoconf picks up the cross build tools correctly.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/Makefile b/Makefile
index ca14a21..ff2c16d 100644
--- a/Makefile
+++ b/Makefile
@@ -122,7 +122,7 @@ subdir-pixman: pixman/Makefile
 	$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C pixman V="$(V)" all,)
 
 pixman/Makefile: $(SRC_PATH)/pixman/configure
-	(cd pixman; $(SRC_PATH)/pixman/configure --disable-shared --enable-static)
+	(cd pixman; $(SRC_PATH)/pixman/configure $(AUTOCONF_HOST) --disable-shared --enable-static)
 
 $(SRC_PATH)/pixman/configure:
 	(cd $(SRC_PATH)/pixman; autoreconf -v --install)
diff --git a/configure b/configure
index e6fe4f8..0a241eb 100755
--- a/configure
+++ b/configure
@@ -3649,6 +3649,11 @@ if test "$sparse" = "yes" ; then
   echo "HOST_CC      := REAL_CC=\"\$(HOST_CC)\" cgcc"  >> $config_host_mak
   echo "QEMU_CFLAGS  += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak
 fi
+if test "$cross_prefix" != ""; then
+  echo "AUTOCONF_HOST := --host=${cross_prefix%-}"     >> $config_host_mak
+else
+  echo "AUTOCONF_HOST := "                             >> $config_host_mak
+fi
 echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
 echo "ARLIBS_BEGIN=$arlibs_begin" >> $config_host_mak
 echo "ARLIBS_END=$arlibs_end" >> $config_host_mak
commit 5ca9388a4d22a68439a04b0735c34bdcac553e48
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Nov 7 11:06:23 2012 +0100

    pixman: add output dir to include path
    
    Needed to make sure the (generated) pixman-version.h file is found.
    Based on a patch from Blue Swirl.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/configure b/configure
index 7290f50..e6fe4f8 100755
--- a/configure
+++ b/configure
@@ -2121,11 +2121,10 @@ else
     echo "      git submodule update --init pixman"
     exit 1
   fi
-  pixman_cflags="-I${source_path}/pixman/pixman"
-  pixman_libs="-Lpixman/pixman/.libs -lpixman-1"
+  mkdir -p pixman/pixman
+  pixman_cflags="-I\$(SRC_PATH)/pixman/pixman -I\$(BUILD_DIR)/pixman/pixman"
+  pixman_libs="-L\$(BUILD_DIR)/pixman/pixman/.libs -lpixman-1"
 fi
-QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags"
-libs_softmmu="$libs_softmmu $pixman_libs"
 
 ##########################################
 # libcap probe
@@ -3137,6 +3136,10 @@ if test "$cpu" = "ppc64" -a "$targetos" != "Darwin" ; then
   roms="$roms spapr-rtas"
 fi
 
+# add pixman flags after all config tests are done
+QEMU_CFLAGS="$QEMU_CFLAGS $pixman_cflags"
+libs_softmmu="$libs_softmmu $pixman_libs"
+
 echo "Install prefix    $prefix"
 echo "BIOS directory    `eval echo $qemu_datadir`"
 echo "binary directory  `eval echo $bindir`"
@@ -4154,7 +4157,6 @@ DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas"
 DIRS="$DIRS roms/seabios roms/vgabios"
 DIRS="$DIRS qapi-generated"
 DIRS="$DIRS libcacard libcacard/libcacard libcacard/trace"
-DIRS="$DIRS pixman"
 FILES="Makefile tests/tcg/Makefile qdict-test-data.txt"
 FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit"
 FILES="$FILES tests/tcg/lm32/Makefile libcacard/Makefile"
commit a771c51703cf9f91023c6570426258bdf5ec775b
Author: Alex Williamson <alex.williamson at redhat.com>
Date:   Tue Nov 13 12:27:40 2012 -0700

    vfio-pci: Use common msi_get_message
    
    We can get rid of our local version now that a helper exists.
    
    Signed-off-by: Alex Williamson <alex.williamson at redhat.com>

diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c
index 4e9c2dd..7c27834 100644
--- a/hw/vfio_pci.c
+++ b/hw/vfio_pci.c
@@ -688,28 +688,6 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr)
     vector->use = false;
 }
 
-/* TODO This should move to msi.c */
-static MSIMessage msi_get_msg(PCIDevice *pdev, unsigned int vector)
-{
-    uint16_t flags = pci_get_word(pdev->config + pdev->msi_cap + PCI_MSI_FLAGS);
-    bool msi64bit = flags & PCI_MSI_FLAGS_64BIT;
-    MSIMessage msg;
-
-    if (msi64bit) {
-        msg.address = pci_get_quad(pdev->config +
-                                   pdev->msi_cap + PCI_MSI_ADDRESS_LO);
-    } else {
-        msg.address = pci_get_long(pdev->config +
-                                   pdev->msi_cap + PCI_MSI_ADDRESS_LO);
-    }
-
-    msg.data = pci_get_word(pdev->config + pdev->msi_cap +
-                            (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32));
-    msg.data += vector;
-
-    return msg;
-}
-
 static void vfio_enable_msix(VFIODevice *vdev)
 {
     vfio_disable_interrupts(vdev);
@@ -748,7 +726,7 @@ retry:
             error_report("vfio: Error: event_notifier_init failed\n");
         }
 
-        msg = msi_get_msg(&vdev->pdev, i);
+        msg = msi_get_message(&vdev->pdev, i);
 
         /*
          * Attempt to enable route through KVM irqchip,
commit e1d1e5867d0bd54c4fc51ec2cddc701258314db0
Author: Alex Williamson <alex.williamson at redhat.com>
Date:   Tue Nov 13 12:27:40 2012 -0700

    vfio-pci: Add KVM INTx acceleration
    
    This makes use of the new level irqfd support enabling bypass of qemu
    userspace both on INTx injection and unmask.  This significantly
    boosts the performance of devices making use of legacy interrupts (ex.
    ~60% better netperf TCP_RR scores for an e1000e assigned to a Linux
    guest and booted with pci=nomsi).  This also avoids flipping mmaps on
    and off to simulate EOIs, so greatly improves performance of device
    access in addition to interrupt latency.
    
    Signed-off-by: Alex Williamson <alex.williamson at redhat.com>

diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c
index 0473ae8..4e9c2dd 100644
--- a/hw/vfio_pci.c
+++ b/hw/vfio_pci.c
@@ -185,6 +185,21 @@ static void vfio_unmask_intx(VFIODevice *vdev)
     ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
 }
 
+#ifdef CONFIG_KVM /* Unused outside of CONFIG_KVM code */
+static void vfio_mask_intx(VFIODevice *vdev)
+{
+    struct vfio_irq_set irq_set = {
+        .argsz = sizeof(irq_set),
+        .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK,
+        .index = VFIO_PCI_INTX_IRQ_INDEX,
+        .start = 0,
+        .count = 1,
+    };
+
+    ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
+}
+#endif
+
 /*
  * Disabling BAR mmaping can be slow, but toggling it around INTx can
  * also be a huge overhead.  We try to get the best of both worlds by
@@ -248,6 +263,161 @@ static void vfio_eoi(VFIODevice *vdev)
     vfio_unmask_intx(vdev);
 }
 
+static void vfio_enable_intx_kvm(VFIODevice *vdev)
+{
+#ifdef CONFIG_KVM
+    struct kvm_irqfd irqfd = {
+        .fd = event_notifier_get_fd(&vdev->intx.interrupt),
+        .gsi = vdev->intx.route.irq,
+        .flags = KVM_IRQFD_FLAG_RESAMPLE,
+    };
+    struct vfio_irq_set *irq_set;
+    int ret, argsz;
+    int32_t *pfd;
+
+    if (!kvm_irqchip_in_kernel() ||
+        vdev->intx.route.mode != PCI_INTX_ENABLED ||
+        !kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) {
+        return;
+    }
+
+    /* Get to a known interrupt state */
+    qemu_set_fd_handler(irqfd.fd, NULL, NULL, vdev);
+    vfio_mask_intx(vdev);
+    vdev->intx.pending = false;
+    qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
+
+    /* Get an eventfd for resample/unmask */
+    if (event_notifier_init(&vdev->intx.unmask, 0)) {
+        error_report("vfio: Error: event_notifier_init failed eoi\n");
+        goto fail;
+    }
+
+    /* KVM triggers it, VFIO listens for it */
+    irqfd.resamplefd = event_notifier_get_fd(&vdev->intx.unmask);
+
+    if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
+        error_report("vfio: Error: Failed to setup resample irqfd: %m\n");
+        goto fail_irqfd;
+    }
+
+    argsz = sizeof(*irq_set) + sizeof(*pfd);
+
+    irq_set = g_malloc0(argsz);
+    irq_set->argsz = argsz;
+    irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK;
+    irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
+    irq_set->start = 0;
+    irq_set->count = 1;
+    pfd = (int32_t *)&irq_set->data;
+
+    *pfd = irqfd.resamplefd;
+
+    ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set);
+    g_free(irq_set);
+    if (ret) {
+        error_report("vfio: Error: Failed to setup INTx unmask fd: %m\n");
+        goto fail_vfio;
+    }
+
+    /* Let'em rip */
+    vfio_unmask_intx(vdev);
+
+    vdev->intx.kvm_accel = true;
+
+    DPRINTF("%s(%04x:%02x:%02x.%x) KVM INTx accel enabled\n",
+            __func__, vdev->host.domain, vdev->host.bus,
+            vdev->host.slot, vdev->host.function);
+
+    return;
+
+fail_vfio:
+    irqfd.flags = KVM_IRQFD_FLAG_DEASSIGN;
+    kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd);
+fail_irqfd:
+    event_notifier_cleanup(&vdev->intx.unmask);
+fail:
+    qemu_set_fd_handler(irqfd.fd, vfio_intx_interrupt, NULL, vdev);
+    vfio_unmask_intx(vdev);
+#endif
+}
+
+static void vfio_disable_intx_kvm(VFIODevice *vdev)
+{
+#ifdef CONFIG_KVM
+    struct kvm_irqfd irqfd = {
+        .fd = event_notifier_get_fd(&vdev->intx.interrupt),
+        .gsi = vdev->intx.route.irq,
+        .flags = KVM_IRQFD_FLAG_DEASSIGN,
+    };
+
+    if (!vdev->intx.kvm_accel) {
+        return;
+    }
+
+    /*
+     * Get to a known state, hardware masked, QEMU ready to accept new
+     * interrupts, QEMU IRQ de-asserted.
+     */
+    vfio_mask_intx(vdev);
+    vdev->intx.pending = false;
+    qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
+
+    /* Tell KVM to stop listening for an INTx irqfd */
+    if (kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd)) {
+        error_report("vfio: Error: Failed to disable INTx irqfd: %m\n");
+    }
+
+    /* We only need to close the eventfd for VFIO to cleanup the kernel side */
+    event_notifier_cleanup(&vdev->intx.unmask);
+
+    /* QEMU starts listening for interrupt events. */
+    qemu_set_fd_handler(irqfd.fd, vfio_intx_interrupt, NULL, vdev);
+
+    vdev->intx.kvm_accel = false;
+
+    /* If we've missed an event, let it re-fire through QEMU */
+    vfio_unmask_intx(vdev);
+
+    DPRINTF("%s(%04x:%02x:%02x.%x) KVM INTx accel disabled\n",
+            __func__, vdev->host.domain, vdev->host.bus,
+            vdev->host.slot, vdev->host.function);
+#endif
+}
+
+static void vfio_update_irq(PCIDevice *pdev)
+{
+    VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
+    PCIINTxRoute route;
+
+    if (vdev->interrupt != VFIO_INT_INTx) {
+        return;
+    }
+
+    route = pci_device_route_intx_to_irq(&vdev->pdev, vdev->intx.pin);
+
+    if (!pci_intx_route_changed(&vdev->intx.route, &route)) {
+        return; /* Nothing changed */
+    }
+
+    DPRINTF("%s(%04x:%02x:%02x.%x) IRQ moved %d -> %d\n", __func__,
+            vdev->host.domain, vdev->host.bus, vdev->host.slot,
+            vdev->host.function, vdev->intx.route.irq, route.irq);
+
+    vfio_disable_intx_kvm(vdev);
+
+    vdev->intx.route = route;
+
+    if (route.mode != PCI_INTX_ENABLED) {
+        return;
+    }
+
+    vfio_enable_intx_kvm(vdev);
+
+    /* Re-enable the interrupt in cased we missed an EOI */
+    vfio_eoi(vdev);
+}
+
 static int vfio_enable_intx(VFIODevice *vdev)
 {
     uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1);
@@ -262,6 +432,18 @@ static int vfio_enable_intx(VFIODevice *vdev)
     vfio_disable_interrupts(vdev);
 
     vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */
+
+#ifdef CONFIG_KVM
+    /*
+     * Only conditional to avoid generating error messages on platforms
+     * where we won't actually use the result anyway.
+     */
+    if (kvm_check_extension(kvm_state, KVM_CAP_IRQFD_RESAMPLE)) {
+        vdev->intx.route = pci_device_route_intx_to_irq(&vdev->pdev,
+                                                        vdev->intx.pin);
+    }
+#endif
+
     ret = event_notifier_init(&vdev->intx.interrupt, 0);
     if (ret) {
         error_report("vfio: Error: event_notifier_init failed\n");
@@ -290,6 +472,8 @@ static int vfio_enable_intx(VFIODevice *vdev)
         return -errno;
     }
 
+    vfio_enable_intx_kvm(vdev);
+
     vdev->interrupt = VFIO_INT_INTx;
 
     DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain,
@@ -303,6 +487,7 @@ static void vfio_disable_intx(VFIODevice *vdev)
     int fd;
 
     qemu_del_timer(vdev->intx.mmap_timer);
+    vfio_disable_intx_kvm(vdev);
     vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX);
     vdev->intx.pending = false;
     qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0);
@@ -1839,6 +2024,7 @@ static int vfio_initfn(PCIDevice *pdev)
     if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
         vdev->intx.mmap_timer = qemu_new_timer_ms(vm_clock,
                                                   vfio_intx_mmap_enable, vdev);
+        pci_device_set_intx_routing_notifier(&vdev->pdev, vfio_update_irq);
         ret = vfio_enable_intx(vdev);
         if (ret) {
             goto out_teardown;
commit 716b8e4dd2f7a1674bf77d49fd50139cb577e70f
Author: Alex Williamson <alex.williamson at redhat.com>
Date:   Tue Nov 13 12:27:40 2012 -0700

    linux-headers: Update to 3.7-rc5
    
    update-linux-headers.sh script run against Linux tag v3.7-rc5
    
    Signed-off-by: Alex Williamson <alex.williamson at redhat.com>

diff --git a/linux-headers/asm-powerpc/kvm_para.h b/linux-headers/asm-powerpc/kvm_para.h
index c047a84..5e04383 100644
--- a/linux-headers/asm-powerpc/kvm_para.h
+++ b/linux-headers/asm-powerpc/kvm_para.h
@@ -17,8 +17,8 @@
  * Authors: Hollis Blanchard <hollisb at us.ibm.com>
  */
 
-#ifndef __POWERPC_KVM_PARA_H__
-#define __POWERPC_KVM_PARA_H__
+#ifndef _UAPI__POWERPC_KVM_PARA_H__
+#define _UAPI__POWERPC_KVM_PARA_H__
 
 #include <linux/types.h>
 
@@ -87,4 +87,4 @@ struct kvm_vcpu_arch_shared {
 #define KVM_MAGIC_FEAT_MAS0_TO_SPRG7	(1 << 1)
 
 
-#endif /* __POWERPC_KVM_PARA_H__ */
+#endif /* _UAPI__POWERPC_KVM_PARA_H__ */
diff --git a/linux-headers/asm-s390/kvm_para.h b/linux-headers/asm-s390/kvm_para.h
index 870051f..ff1f4e7 100644
--- a/linux-headers/asm-s390/kvm_para.h
+++ b/linux-headers/asm-s390/kvm_para.h
@@ -1,5 +1,5 @@
 /*
- * definition for paravirtual devices on s390
+ * User API definitions for paravirtual devices on s390
  *
  * Copyright IBM Corp. 2008
  *
@@ -9,9 +9,3 @@
  *
  *    Author(s): Christian Borntraeger <borntraeger at de.ibm.com>
  */
-
-#ifndef __S390_KVM_PARA_H
-#define __S390_KVM_PARA_H
-
-
-#endif /* __S390_KVM_PARA_H */
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index 246617e..a65ec29 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -9,6 +9,22 @@
 #include <linux/types.h>
 #include <linux/ioctl.h>
 
+#define DE_VECTOR 0
+#define DB_VECTOR 1
+#define BP_VECTOR 3
+#define OF_VECTOR 4
+#define BR_VECTOR 5
+#define UD_VECTOR 6
+#define NM_VECTOR 7
+#define DF_VECTOR 8
+#define TS_VECTOR 10
+#define NP_VECTOR 11
+#define SS_VECTOR 12
+#define GP_VECTOR 13
+#define PF_VECTOR 14
+#define MF_VECTOR 16
+#define MC_VECTOR 18
+
 /* Select x86 specific features in <linux/kvm.h> */
 #define __KVM_HAVE_PIT
 #define __KVM_HAVE_IOAPIC
@@ -25,6 +41,7 @@
 #define __KVM_HAVE_DEBUGREGS
 #define __KVM_HAVE_XSAVE
 #define __KVM_HAVE_XCRS
+#define __KVM_HAVE_READONLY_MEM
 
 /* Architectural interrupt line count. */
 #define KVM_NR_INTERRUPTS 256
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 4b9e575..81d2feb 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -101,9 +101,13 @@ struct kvm_userspace_memory_region {
 	__u64 userspace_addr; /* start of the userspace allocated memory */
 };
 
-/* for kvm_memory_region::flags */
-#define KVM_MEM_LOG_DIRTY_PAGES  1UL
-#define KVM_MEMSLOT_INVALID      (1UL << 1)
+/*
+ * The bit 0 ~ bit 15 of kvm_memory_region::flags are visible for userspace,
+ * other bits are reserved for kvm internal use which are defined in
+ * include/linux/kvm_host.h.
+ */
+#define KVM_MEM_LOG_DIRTY_PAGES	(1UL << 0)
+#define KVM_MEM_READONLY	(1UL << 1)
 
 /* for KVM_IRQ_LINE */
 struct kvm_irq_level {
@@ -618,6 +622,10 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_PPC_GET_SMMU_INFO 78
 #define KVM_CAP_S390_COW 79
 #define KVM_CAP_PPC_ALLOC_HTAB 80
+#ifdef __KVM_HAVE_READONLY_MEM
+#define KVM_CAP_READONLY_MEM 81
+#endif
+#define KVM_CAP_IRQFD_RESAMPLE 82
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
@@ -683,12 +691,21 @@ struct kvm_xen_hvm_config {
 #endif
 
 #define KVM_IRQFD_FLAG_DEASSIGN (1 << 0)
+/*
+ * Available with KVM_CAP_IRQFD_RESAMPLE
+ *
+ * KVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
+ * the irqfd to operate in resampling mode for level triggered interrupt
+ * emlation.  See Documentation/virtual/kvm/api.txt.
+ */
+#define KVM_IRQFD_FLAG_RESAMPLE (1 << 1)
 
 struct kvm_irqfd {
 	__u32 fd;
 	__u32 gsi;
 	__u32 flags;
-	__u8  pad[20];
+	__u32 resamplefd;
+	__u8  pad[16];
 };
 
 struct kvm_clock_data {
diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h
index 7bdcf93..cea2c5c 100644
--- a/linux-headers/linux/kvm_para.h
+++ b/linux-headers/linux/kvm_para.h
@@ -1,5 +1,5 @@
-#ifndef __LINUX_KVM_PARA_H
-#define __LINUX_KVM_PARA_H
+#ifndef _UAPI__LINUX_KVM_PARA_H
+#define _UAPI__LINUX_KVM_PARA_H
 
 /*
  * This header file provides a method for making a hypercall to the host
@@ -25,4 +25,4 @@
  */
 #include <asm/kvm_para.h>
 
-#endif /* __LINUX_KVM_PARA_H */
+#endif /* _UAPI__LINUX_KVM_PARA_H */
diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h
index f787b72..4758d1b 100644
--- a/linux-headers/linux/vfio.h
+++ b/linux-headers/linux/vfio.h
@@ -8,8 +8,8 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#ifndef VFIO_H
-#define VFIO_H
+#ifndef _UAPIVFIO_H
+#define _UAPIVFIO_H
 
 #include <linux/types.h>
 #include <linux/ioctl.h>
@@ -365,4 +365,4 @@ struct vfio_iommu_type1_dma_unmap {
 
 #define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14)
 
-#endif /* VFIO_H */
+#endif /* _UAPIVFIO_H */
diff --git a/linux-headers/linux/virtio_config.h b/linux-headers/linux/virtio_config.h
index 4f51d8f..b7cda39 100644
--- a/linux-headers/linux/virtio_config.h
+++ b/linux-headers/linux/virtio_config.h
@@ -1,5 +1,5 @@
-#ifndef _LINUX_VIRTIO_CONFIG_H
-#define _LINUX_VIRTIO_CONFIG_H
+#ifndef _UAPI_LINUX_VIRTIO_CONFIG_H
+#define _UAPI_LINUX_VIRTIO_CONFIG_H
 /* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so
  * anyone can use the definitions to implement compatible drivers/servers.
  *
@@ -51,4 +51,4 @@
  * suppressed them? */
 #define VIRTIO_F_NOTIFY_ON_EMPTY	24
 
-#endif /* _LINUX_VIRTIO_CONFIG_H */
+#endif /* _UAPI_LINUX_VIRTIO_CONFIG_H */
diff --git a/linux-headers/linux/virtio_ring.h b/linux-headers/linux/virtio_ring.h
index 1b333e2..921694a 100644
--- a/linux-headers/linux/virtio_ring.h
+++ b/linux-headers/linux/virtio_ring.h
@@ -1,5 +1,5 @@
-#ifndef _LINUX_VIRTIO_RING_H
-#define _LINUX_VIRTIO_RING_H
+#ifndef _UAPI_LINUX_VIRTIO_RING_H
+#define _UAPI_LINUX_VIRTIO_RING_H
 /* An interface for efficient virtio implementation, currently for use by KVM
  * and lguest, but hopefully others soon.  Do NOT change this since it will
  * break existing servers and clients.
@@ -160,4 +160,4 @@ static __inline__ int vring_need_event(__u16 event_idx, __u16 new_idx, __u16 old
 	return (__u16)(new_idx - event_idx - 1) < (__u16)(new_idx - old);
 }
 
-#endif /* _LINUX_VIRTIO_RING_H */
+#endif /* _UAPI_LINUX_VIRTIO_RING_H */
commit c8969eded252058e90e91f12f75f32aceae46ec9
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Tue Nov 13 10:34:17 2012 +0100

    nbd: fixes to read-only handling
    
    We do not need BLKROSET if the kernel supports setting flags.
    Also, always do BLKROSET even for a read-write export, otherwise
    the read-only state remains "sticky" after the invocation of
    "qemu-nbd -r".
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/nbd.c b/nbd.c
index cec5a94..97a5914 100644
--- a/nbd.c
+++ b/nbd.c
@@ -596,24 +596,23 @@ int nbd_init(int fd, int csock, uint32_t flags, off_t size, size_t blocksize)
         return -serrno;
     }
 
-    if (flags & NBD_FLAG_READ_ONLY) {
-        int read_only = 1;
-        TRACE("Setting readonly attribute");
-
-        if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) {
+    if (ioctl(fd, NBD_SET_FLAGS, flags) < 0) {
+        if (errno == ENOTTY) {
+            int read_only = (flags & NBD_FLAG_READ_ONLY) != 0;
+            TRACE("Setting readonly attribute");
+
+            if (ioctl(fd, BLKROSET, (unsigned long) &read_only) < 0) {
+                int serrno = errno;
+                LOG("Failed setting read-only attribute");
+                return -serrno;
+            }
+        } else {
             int serrno = errno;
-            LOG("Failed setting read-only attribute");
+            LOG("Failed setting flags");
             return -serrno;
         }
     }
 
-    if (ioctl(fd, NBD_SET_FLAGS, flags) < 0
-        && errno != ENOTTY) {
-        int serrno = errno;
-        LOG("Failed setting flags");
-        return -serrno;
-    }
-
     TRACE("Negotiation ended");
 
     return 0;
commit dd72fdd06268860a24f9f3828efade547ee2e2a9
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Tue Oct 30 17:31:43 2012 +0100

    virtio-scsi: use dma_context_memory
    
    Until address_space_rw was introduced, NULL was accepted as a
    placeholder for DMA with no IOMMU (to address_space_memory).
    
    This does not work anymore, and dma_context_memory needs to
    be specified explicitly.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
index 30d3f8a..7d546f6 100644
--- a/hw/virtio-scsi.c
+++ b/hw/virtio-scsi.c
@@ -204,7 +204,7 @@ static void virtio_scsi_bad_req(void)
 static void qemu_sgl_init_external(QEMUSGList *qsgl, struct iovec *sg,
                                    hwaddr *addr, int num)
 {
-    memset(qsgl, 0, sizeof(*qsgl));
+    qemu_sglist_init(qsgl, num, &dma_context_memory);
     while (num--) {
         qemu_sglist_add(qsgl, *(addr++), (sg++)->iov_len);
     }
commit 9e11908f12f92e31ea94dc2a4c962c836cba9f2a
Author: Peter Maydell <peter.maydell at linaro.org>
Date:   Mon Oct 29 11:34:32 2012 +1000

    dma: Define dma_context_memory and use in sysbus-ohci
    
    Define a new global dma_context_memory which is a DMAContext corresponding
    to the global address_space_memory AddressSpace. This can be used by
    sysbus peripherals like sysbus-ohci which need to do DMA.
    
    In particular, use it in the sysbus-ohci device, which fixes a
    segfault when attempting to use that device.
    
    Signed-off-by: Peter Maydell <peter.maydell at linaro.org>
    Reviewed-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>

diff --git a/dma.h b/dma.h
index 91ccdb5..eedf878 100644
--- a/dma.h
+++ b/dma.h
@@ -68,6 +68,11 @@ struct DMAContext {
     DMAUnmapFunc *unmap;
 };
 
+/* A global DMA context corresponding to the address_space_memory
+ * AddressSpace, for sysbus devices which do DMA.
+ */
+extern DMAContext dma_context_memory;
+
 static inline void dma_barrier(DMAContext *dma, DMADirection dir)
 {
     /*
diff --git a/exec.c b/exec.c
index af94f9c..8435de0 100644
--- a/exec.c
+++ b/exec.c
@@ -34,6 +34,7 @@
 #include "hw/xen.h"
 #include "qemu-timer.h"
 #include "memory.h"
+#include "dma.h"
 #include "exec-memory.h"
 #if defined(CONFIG_USER_ONLY)
 #include <qemu.h>
@@ -103,6 +104,7 @@ static MemoryRegion *system_io;
 
 AddressSpace address_space_io;
 AddressSpace address_space_memory;
+DMAContext dma_context_memory;
 
 MemoryRegion io_mem_ram, io_mem_rom, io_mem_unassigned, io_mem_notdirty;
 static MemoryRegion io_mem_subpage_ram;
@@ -3294,6 +3296,9 @@ static void memory_map_init(void)
     memory_listener_register(&core_memory_listener, &address_space_memory);
     memory_listener_register(&io_memory_listener, &address_space_io);
     memory_listener_register(&tcg_memory_listener, &address_space_memory);
+
+    dma_context_init(&dma_context_memory, &address_space_memory,
+                     NULL, NULL, NULL);
 }
 
 MemoryRegion *get_system_memory(void)
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 7571e9e..efae032 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -1851,7 +1851,7 @@ static int ohci_init_pxa(SysBusDevice *dev)
 
     /* Cannot fail as we pass NULL for masterbus */
     usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset, NULL, 0,
-                  NULL);
+                  &dma_context_memory);
     sysbus_init_irq(dev, &s->ohci.irq);
     sysbus_init_mmio(dev, &s->ohci.mem);
 
commit 4003e24fce1df84a2e8c992376ed2c294816c33c
Author: Hannes Reinecke <hare at suse.de>
Date:   Mon Nov 12 15:42:42 2012 +0100

    megasas: Correct target/lun mapping
    
    The structure to reference a logical drive has an unused field,
    which can be used to carry the lun ID. This enabled seabios to
    establish the proper target/LUN mapping.
    
    Cc: Paolo Bonzini <pbonzini at redhat.com>
    Cc: Gerd Hoffmann <kraxel at redhat.com>
    Signed-off-by: Hannes Reinecke <hare at suse.de>
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hw/megasas.c b/hw/megasas.c
index b845ea7..291ff40 100644
--- a/hw/megasas.c
+++ b/hw/megasas.c
@@ -1079,6 +1079,7 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
         /* Logical device size is in blocks */
         bdrv_get_geometry(conf->bs, &ld_size);
         info.ld_list[num_ld_disks].ld.v.target_id = sdev->id;
+        info.ld_list[num_ld_disks].ld.v.lun_id = sdev->lun;
         info.ld_list[num_ld_disks].state = MFI_LD_STATE_OPTIMAL;
         info.ld_list[num_ld_disks].size = cpu_to_le64(ld_size);
         num_ld_disks++;
diff --git a/hw/mfi.h b/hw/mfi.h
index 436b690..cd8355b 100644
--- a/hw/mfi.h
+++ b/hw/mfi.h
@@ -1085,7 +1085,7 @@ struct mfi_pd_list {
 union mfi_ld_ref {
     struct {
         uint8_t target_id;
-        uint8_t reserved;
+        uint8_t lun_id;
         uint16_t seq;
     } v;
     uint32_t ref;
commit accfeb2dd32ece73350b06cee1b2403f47e86fe3
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Wed Oct 31 17:14:41 2012 +0100

    scsi-disk: flush cache after disabling it
    
    SBC says that "if an application client changes the WCE bit from one to
    zero via a MODE SELECT command, then the device server shall write
    any data in volatile cache to non-volatile medium before completing
    the command".
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index d15f891..49b5686 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1387,6 +1387,7 @@ invalid_param_len:
 
 static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
 {
+    SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
     uint8_t *p = inbuf;
     int cmd = r->req.cmd.buf[0];
     int len = r->req.cmd.xfer;
@@ -1423,6 +1424,14 @@ static void scsi_disk_emulate_mode_select(SCSIDiskReq *r, uint8_t *inbuf)
             return;
         }
     }
+    if (!bdrv_enable_write_cache(s->qdev.conf.bs)) {
+        /* The request is used as the AIO opaque value, so add a ref.  */
+        scsi_req_ref(&r->req);
+        bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+        r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_aio_complete, r);
+        return;
+    }
+
     scsi_req_complete(&r->req, GOOD);
     return;
 
commit 346a3017ec8d5ac4a6961d823f1e576867dc35ef
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Fri Sep 28 16:43:41 2012 +0200

    megasas: do not include block_int.h
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hw/megasas.c b/hw/megasas.c
index 7a2036e..b845ea7 100644
--- a/hw/megasas.c
+++ b/hw/megasas.c
@@ -25,7 +25,6 @@
 #include "iov.h"
 #include "scsi.h"
 #include "scsi-defs.h"
-#include "block_int.h"
 #include "trace.h"
 
 #include "mfi.h"
commit b5232e904fadac8af239306719be4a554f9e9263
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Mon Oct 8 16:46:54 2012 +0200

    scsi: remove superfluous call to scsi_device_set_ua
    
    Suggested by Laszlo Ersek.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 098558d..d15f891 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -1964,7 +1964,6 @@ static void scsi_disk_resize_cb(void *opaque)
      * direct-access devices.
      */
     if (s->qdev.type == TYPE_DISK) {
-        scsi_device_set_ua(&s->qdev, SENSE_CODE(CAPACITY_CHANGED));
         scsi_device_report_change(&s->qdev, SENSE_CODE(CAPACITY_CHANGED));
     }
 }
commit cd41a671b370a3dd603963432d2b02f1e5990fb7
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Mon Oct 8 16:50:51 2012 +0200

    virtio-scsi: factor checks for VIRTIO_SCSI_S_DRIVER_OK when reporting events
    
    Suggested by Laszlo Ersek.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
index b54c789..30d3f8a 100644
--- a/hw/virtio-scsi.c
+++ b/hw/virtio-scsi.c
@@ -596,6 +596,10 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
     VirtIOSCSIEvent *evt;
     int in_size;
 
+    if (!(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+        return;
+    }
+
     if (!req) {
         s->events_dropped = true;
         return;
@@ -648,7 +652,6 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense)
     VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
 
     if (((s->vdev.guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) &&
-        (s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) &&
         dev->type != TYPE_ROM) {
         virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE,
                                sense.asc | (sense.ascq << 8));
@@ -659,8 +662,7 @@ static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev)
 {
     VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus);
 
-    if (((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) &&
-        (s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+    if ((s->vdev.guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) {
         virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET,
                                VIRTIO_SCSI_EVT_RESET_RESCAN);
     }
commit c8dcb531bcd37a4a81d2cc08a89fcd19c34348f9
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Wed Oct 10 12:18:03 2012 +0200

    scsi: do not return short responses for emulated commands
    
    The inquiry command, for the case of VPD=1, was returning short
    responses; the number of returned bytes was just the number of bytes
    in the request, without padding to the specified allocation length
    with zero bytes.  This is usually harmless, but it is a violation
    of the SCSI specification.
    
    To fix this, always pad with zero bytes to r->cmd.xfer in
    scsi_disk_emulate_command, and return at most r->buflen bytes
    (the size of the buffer for command data) rather than at most
    buflen bytes (the number of bytes that was filled in).
    
    Before this patch, "strace sg_inq -p0x83 /dev/sda" would report a
    non-zero resid value.  After this patch, it reports resid=0.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 1b0afa6..098558d 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -652,7 +652,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
     if (buflen > SCSI_MAX_INQUIRY_LEN) {
         buflen = SCSI_MAX_INQUIRY_LEN;
     }
-    memset(outbuf, 0, buflen);
 
     outbuf[0] = s->qdev.type & 0x1f;
     outbuf[1] = (s->features & (1 << SCSI_DISK_F_REMOVABLE)) ? 0x80 : 0;
@@ -1596,24 +1595,26 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
         break;
     }
 
+    /*
+     * FIXME: we shouldn't return anything bigger than 4k, but the code
+     * requires the buffer to be as big as req->cmd.xfer in several
+     * places.  So, do not allow CDBs with a very large ALLOCATION
+     * LENGTH.  The real fix would be to modify scsi_read_data and
+     * dma_buf_read, so that they return data beyond the buflen
+     * as all zeros.
+     */
+    if (req->cmd.xfer > 65536) {
+        goto illegal_request;
+    }
+    r->buflen = MAX(4096, req->cmd.xfer);
+
     if (!r->iov.iov_base) {
-        /*
-         * FIXME: we shouldn't return anything bigger than 4k, but the code
-         * requires the buffer to be as big as req->cmd.xfer in several
-         * places.  So, do not allow CDBs with a very large ALLOCATION
-         * LENGTH.  The real fix would be to modify scsi_read_data and
-         * dma_buf_read, so that they return data beyond the buflen
-         * as all zeros.
-         */
-        if (req->cmd.xfer > 65536) {
-            goto illegal_request;
-        }
-        r->buflen = MAX(4096, req->cmd.xfer);
         r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
     }
 
     buflen = req->cmd.xfer;
     outbuf = r->iov.iov_base;
+    memset(outbuf, 0, r->buflen);
     switch (req->cmd.buf[0]) {
     case TEST_UNIT_READY:
         assert(!s->tray_open && bdrv_is_inserted(s->qdev.conf.bs));
@@ -1694,12 +1695,14 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
         outbuf[5] = 0;
         outbuf[6] = s->qdev.blocksize >> 8;
         outbuf[7] = 0;
-        buflen = 8;
         break;
     case REQUEST_SENSE:
         /* Just return "NO SENSE".  */
         buflen = scsi_build_sense(NULL, 0, outbuf, r->buflen,
                                   (req->cmd.buf[1] & 1) == 0);
+        if (buflen < 0) {
+            goto illegal_request;
+        }
         break;
     case MECHANISM_STATUS:
         buflen = scsi_emulate_mechanism_status(s, outbuf);
@@ -1770,7 +1773,6 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
             }
 
             /* Protection, exponent and lowest lba field left blank. */
-            buflen = req->cmd.xfer;
             break;
         }
         DPRINTF("Unsupported Service Action In\n");
@@ -1827,7 +1829,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
         return 0;
     }
     assert(!r->req.aiocb);
-    r->iov.iov_len = MIN(buflen, req->cmd.xfer);
+    r->iov.iov_len = MIN(r->buflen, req->cmd.xfer);
     if (r->iov.iov_len == 0) {
         scsi_req_complete(&r->req, GOOD);
     }
commit 4057725f35abe00ea371f85c6e27dd25eafd9ddf
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Thu Aug 23 11:53:04 2012 +0200

    hmp: add NBD server commands
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/hmp-commands.hx b/hmp-commands.hx
index f916385..b74ef75 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1310,6 +1310,51 @@ Remove all matches from the access control list, and set the default
 policy back to @code{deny}.
 ETEXI
 
+    {
+        .name       = "nbd_server_start",
+        .args_type  = "all:-a,writable:-w,uri:s",
+        .params     = "nbd_server_start [-a] [-w] host:port",
+        .help       = "serve block devices on the given host and port",
+        .mhandler.cmd = hmp_nbd_server_start,
+    },
+STEXI
+ at item nbd_server_start @var{host}:@var{port}
+ at findex nbd_server_start
+Start an NBD server on the given host and/or port.  If the @option{-a}
+option is included, all of the virtual machine's block devices that
+have an inserted media on them are automatically exported; in this case,
+the @option{-w} option makes the devices writable too.
+ETEXI
+
+    {
+        .name       = "nbd_server_add",
+        .args_type  = "writable:-w,device:B",
+        .params     = "nbd_server_add [-w] device",
+        .help       = "export a block device via NBD",
+        .mhandler.cmd = hmp_nbd_server_add,
+    },
+STEXI
+ at item nbd_server_add @var{device}
+ at findex nbd_server_add
+Export a block device through QEMU's NBD server, which must be started
+beforehand with @command{nbd_server_start}.  The @option{-w} option makes the
+exported device writable too.
+ETEXI
+
+    {
+        .name       = "nbd_server_stop",
+        .args_type  = "",
+        .params     = "nbd_server_stop",
+        .help       = "stop serving block devices using the NBD protocol",
+        .mhandler.cmd = hmp_nbd_server_stop,
+    },
+STEXI
+ at item nbd_server_stop
+ at findex nbd_server_stop
+Stop the QEMU embedded NBD server.
+ETEXI
+
+
 #if defined(TARGET_I386)
 
     {
diff --git a/hmp.c b/hmp.c
index 895a343..180ba2b 100644
--- a/hmp.c
+++ b/hmp.c
@@ -18,6 +18,7 @@
 #include "qemu-option.h"
 #include "qemu-timer.h"
 #include "qmp-commands.h"
+#include "qemu_socket.h"
 #include "monitor.h"
 #include "console.h"
 
@@ -1259,3 +1260,78 @@ void hmp_screen_dump(Monitor *mon, const QDict *qdict)
     qmp_screendump(filename, &err);
     hmp_handle_error(mon, &err);
 }
+
+void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
+{
+    const char *uri = qdict_get_str(qdict, "uri");
+    int writable = qdict_get_try_bool(qdict, "writable", 0);
+    int all = qdict_get_try_bool(qdict, "all", 0);
+    Error *local_err = NULL;
+    BlockInfoList *block_list, *info;
+    SocketAddress *addr;
+
+    if (writable && !all) {
+        error_setg(&local_err, "-w only valid together with -a");
+        goto exit;
+    }
+
+    /* First check if the address is valid and start the server.  */
+    addr = socket_parse(uri, &local_err);
+    if (local_err != NULL) {
+        goto exit;
+    }
+
+    qmp_nbd_server_start(addr, &local_err);
+    qapi_free_SocketAddress(addr);
+    if (local_err != NULL) {
+        goto exit;
+    }
+
+    if (!all) {
+        return;
+    }
+
+    /* Then try adding all block devices.  If one fails, close all and
+     * exit.
+     */
+    block_list = qmp_query_block(NULL);
+
+    for (info = block_list; info; info = info->next) {
+        if (!info->value->has_inserted) {
+            continue;
+        }
+
+        qmp_nbd_server_add(info->value->device, true, writable, &local_err);
+
+        if (local_err != NULL) {
+            qmp_nbd_server_stop(NULL);
+            break;
+        }
+    }
+
+    qapi_free_BlockInfoList(block_list);
+
+exit:
+    hmp_handle_error(mon, &local_err);
+}
+
+void hmp_nbd_server_add(Monitor *mon, const QDict *qdict)
+{
+    const char *device = qdict_get_str(qdict, "device");
+    int writable = qdict_get_try_bool(qdict, "writable", 0);
+    Error *local_err = NULL;
+
+    qmp_nbd_server_add(device, true, writable, &local_err);
+
+    if (local_err != NULL) {
+        hmp_handle_error(mon, &local_err);
+    }
+}
+
+void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict)
+{
+    Error *errp = NULL;
+
+    qmp_nbd_server_stop(&errp);
+    hmp_handle_error(mon, &errp);
+}
diff --git a/hmp.h b/hmp.h
index 34eb2b3..0ab03be 100644
--- a/hmp.h
+++ b/hmp.h
@@ -77,5 +77,8 @@ void hmp_getfd(Monitor *mon, const QDict *qdict);
 void hmp_closefd(Monitor *mon, const QDict *qdict);
 void hmp_send_key(Monitor *mon, const QDict *qdict);
 void hmp_screen_dump(Monitor *mon, const QDict *qdict);
+void hmp_nbd_server_start(Monitor *mon, const QDict *qdict);
+void hmp_nbd_server_add(Monitor *mon, const QDict *qdict);
+void hmp_nbd_server_stop(Monitor *mon, const QDict *qdict);
 
 #endif
commit 17b6be4a7fc9db4f4c56908bab137d4c491471f1
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Mon Nov 12 14:25:17 2012 +0100

    nbd: disallow nbd-server-add before nbd-server-start
    
    It works nicely with the QMP commands, but it adds useless complication
    with HMP.  In particular, see the following:
    
        (qemu) nbd_server_add -w scsi0-hd0
        (qemu) nbd_server_start -a localhost:10809
        NBD server already exporting device scsi0-hd0
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index e362572..d1721a3 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -82,6 +82,11 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
     NBDExport *exp;
     NBDCloseNotifier *n;
 
+    if (server_fd == -1) {
+        error_setg(errp, "NBD server not running");
+        return;
+    }
+
     if (nbd_export_find(device)) {
         error_setg(errp, "NBD server already exporting device '%s'", device);
         return;
commit e644473445177671ec408dfdec705cf931657998
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Sun Nov 4 12:43:37 2012 +0100

    nbd: force read-only export for read-only devices
    
    This is the desired behavior for HMP, but it is a better choice for QMP as well.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 274fba6..e362572 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -93,6 +93,13 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
         return;
     }
 
+    if (!has_writable) {
+        writable = true;
+    }
+    if (bdrv_is_read_only(bs)) {
+        writable = false;
+    }
+
     exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
                          nbd_server_put_ref);
 
commit fc6467eaf22f974620b5dd046afca04e619f9be9
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Mon Nov 12 14:12:54 2012 +0100

    nbd: fix nbd_server_stop crash when no server was running
    
    This failed on the new assertion of qemu_set_fd_handler2:
    
    qemu-system-x86_64: /home/pbonzini/work/upstream/qemu/iohandler.c:60: qemu_set_fd_handler2: Assertion `fd >= 0' failed.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 8031813..274fba6 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -113,7 +113,9 @@ void qmp_nbd_server_stop(Error **errp)
         nbd_close_notifier(&cn->n, nbd_export_get_blockdev(cn->exp));
     }
 
-    qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
-    close(server_fd);
-    server_fd = -1;
+    if (server_fd != -1) {
+        qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL);
+        close(server_fd);
+        server_fd = -1;
+    }
 }
commit 1d7d2a9d2191c34bd1ad69b420db9b47faa3fb8c
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Sun Nov 4 13:04:24 2012 +0100

    nbd: accept URIs
    
    The URI syntax is consistent with the Gluster syntax.  Export names
    are specified in the path, preceded by one or more (otherwise unused)
    slashes.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/block/nbd.c b/block/nbd.c
index 48bbeca..e87c248 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -28,6 +28,7 @@
 
 #include "qemu-common.h"
 #include "nbd.h"
+#include "uri.h"
 #include "block_int.h"
 #include "module.h"
 #include "qemu_socket.h"
@@ -69,6 +70,69 @@ typedef struct BDRVNBDState {
     char *export_name; /* An NBD server may export several devices */
 } BDRVNBDState;
 
+static int nbd_parse_uri(BDRVNBDState *s, const char *filename)
+{
+    URI *uri;
+    const char *p;
+    QueryParams *qp = NULL;
+    int ret = 0;
+
+    uri = uri_parse(filename);
+    if (!uri) {
+        return -EINVAL;
+    }
+
+    /* transport */
+    if (!strcmp(uri->scheme, "nbd")) {
+        s->is_unix = false;
+    } else if (!strcmp(uri->scheme, "nbd+tcp")) {
+        s->is_unix = false;
+    } else if (!strcmp(uri->scheme, "nbd+unix")) {
+        s->is_unix = true;
+    } else {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    p = uri->path ? uri->path : "/";
+    p += strspn(p, "/");
+    if (p[0]) {
+        s->export_name = g_strdup(p);
+    }
+
+    qp = query_params_parse(uri->query);
+    if (qp->n > 1 || (s->is_unix && !qp->n) || (!s->is_unix && qp->n)) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    if (s->is_unix) {
+        /* nbd+unix:///export?socket=path */
+        if (uri->server || uri->port || strcmp(qp->p[0].name, "socket")) {
+            ret = -EINVAL;
+            goto out;
+        }
+        s->host_spec = g_strdup(qp->p[0].value);
+    } else {
+        /* nbd[+tcp]://host:port/export */
+        if (!uri->server) {
+            ret = -EINVAL;
+            goto out;
+        }
+        if (!uri->port) {
+            uri->port = NBD_DEFAULT_PORT;
+        }
+        s->host_spec = g_strdup_printf("%s:%d", uri->server, uri->port);
+    }
+
+out:
+    if (qp) {
+        query_params_free(qp);
+    }
+    uri_free(uri);
+    return ret;
+}
+
 static int nbd_config(BDRVNBDState *s, const char *filename)
 {
     char *file;
@@ -77,6 +141,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename)
     const char *unixpath;
     int err = -EINVAL;
 
+    if (strstr(filename, "://")) {
+        return nbd_parse_uri(s, filename);
+    }
+
     file = g_strdup(filename);
 
     export_name = strstr(file, EN_OPTSTR);
@@ -495,6 +563,33 @@ static int64_t nbd_getlength(BlockDriverState *bs)
 
 static BlockDriver bdrv_nbd = {
     .format_name         = "nbd",
+    .protocol_name       = "nbd",
+    .instance_size       = sizeof(BDRVNBDState),
+    .bdrv_file_open      = nbd_open,
+    .bdrv_co_readv       = nbd_co_readv,
+    .bdrv_co_writev      = nbd_co_writev,
+    .bdrv_close          = nbd_close,
+    .bdrv_co_flush_to_os = nbd_co_flush,
+    .bdrv_co_discard     = nbd_co_discard,
+    .bdrv_getlength      = nbd_getlength,
+};
+
+static BlockDriver bdrv_nbd_tcp = {
+    .format_name         = "nbd",
+    .protocol_name       = "nbd+tcp",
+    .instance_size       = sizeof(BDRVNBDState),
+    .bdrv_file_open      = nbd_open,
+    .bdrv_co_readv       = nbd_co_readv,
+    .bdrv_co_writev      = nbd_co_writev,
+    .bdrv_close          = nbd_close,
+    .bdrv_co_flush_to_os = nbd_co_flush,
+    .bdrv_co_discard     = nbd_co_discard,
+    .bdrv_getlength      = nbd_getlength,
+};
+
+static BlockDriver bdrv_nbd_unix = {
+    .format_name         = "nbd",
+    .protocol_name       = "nbd+unix",
     .instance_size       = sizeof(BDRVNBDState),
     .bdrv_file_open      = nbd_open,
     .bdrv_co_readv       = nbd_co_readv,
@@ -503,12 +598,13 @@ static BlockDriver bdrv_nbd = {
     .bdrv_co_flush_to_os = nbd_co_flush,
     .bdrv_co_discard     = nbd_co_discard,
     .bdrv_getlength      = nbd_getlength,
-    .protocol_name       = "nbd",
 };
 
 static void bdrv_nbd_init(void)
 {
     bdrv_register(&bdrv_nbd);
+    bdrv_register(&bdrv_nbd_tcp);
+    bdrv_register(&bdrv_nbd_unix);
 }
 
 block_init(bdrv_nbd_init);
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 35cabbc..d8fb2de 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -610,14 +610,14 @@ QEMU can access directly to block device exported using the Network Block Device
 protocol.
 
 @example
-qemu-system-i386 linux.img -hdb nbd:my_nbd_server.mydomain.org:1024
+qemu-system-i386 linux.img -hdb nbd://my_nbd_server.mydomain.org:1024/
 @end example
 
 If the NBD server is located on the same host, you can use an unix socket instead
 of an inet socket:
 
 @example
-qemu-system-i386 linux.img -hdb nbd:unix:/tmp/my_socket
+qemu-system-i386 linux.img -hdb nbd+unix://?socket=/tmp/my_socket
 @end example
 
 In this case, the block device must be exported using qemu-nbd:
@@ -631,17 +631,26 @@ The use of qemu-nbd allows to share a disk between several guests:
 qemu-nbd --socket=/tmp/my_socket --share=2 my_disk.qcow2
 @end example
 
+ at noindent
 and then you can use it with two guests:
 @example
-qemu-system-i386 linux1.img -hdb nbd:unix:/tmp/my_socket
-qemu-system-i386 linux2.img -hdb nbd:unix:/tmp/my_socket
+qemu-system-i386 linux1.img -hdb nbd+unix://?socket=/tmp/my_socket
+qemu-system-i386 linux2.img -hdb nbd+unix://?socket=/tmp/my_socket
 @end example
 
-If the nbd-server uses named exports (since NBD 2.9.18), you must use the
-"exportname" option:
+If the nbd-server uses named exports (supported since NBD 2.9.18, or with QEMU's
+own embedded NBD server), you must specify an export name in the URI:
 @example
-qemu-system-i386 -cdrom nbd:localhost:exportname=debian-500-ppc-netinst
-qemu-system-i386 -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst
+qemu-system-i386 -cdrom nbd://localhost/debian-500-ppc-netinst
+qemu-system-i386 -cdrom nbd://localhost/openSUSE-11.1-ppc-netinst
+ at end example
+
+The URI syntax for NBD is supported since QEMU 1.3.  An alternative syntax is
+also available.  Here are some example of the older syntax:
+ at example
+qemu-system-i386 linux.img -hdb nbd:my_nbd_server.mydomain.org:1024
+qemu-system-i386 linux2.img -hdb nbd:unix:/tmp/my_socket
+qemu-system-i386 -cdrom nbd:localhost:10809:exportname=debian-500-ppc-netinst
 @end example
 
 @node disk_images_sheepdog
commit d04b0bbbc9cf61446e5c2e159eef50891009fd9c
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Sun Nov 4 12:56:39 2012 +0100

    nbd: accept relative path to Unix socket
    
    Adding the "is_unix" member now will simplify the parsing of NBD URIs.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/block/nbd.c b/block/nbd.c
index 2bce47b..48bbeca 100644
--- a/block/nbd.c
+++ b/block/nbd.c
@@ -55,7 +55,6 @@ typedef struct BDRVNBDState {
     uint32_t nbdflags;
     off_t size;
     size_t blocksize;
-    char *export_name; /* An NBD server may export several devices */
 
     CoMutex send_mutex;
     CoMutex free_sema;
@@ -65,13 +64,12 @@ typedef struct BDRVNBDState {
     Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
     struct nbd_reply reply;
 
-    /* If it begins with  '/', this is a UNIX domain socket. Otherwise,
-     * it's a string of the form <hostname|ip4|\[ip6\]>:port
-     */
+    int is_unix;
     char *host_spec;
+    char *export_name; /* An NBD server may export several devices */
 } BDRVNBDState;
 
-static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
+static int nbd_config(BDRVNBDState *s, const char *filename)
 {
     char *file;
     char *export_name;
@@ -98,11 +96,10 @@ static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
 
     /* are we a UNIX or TCP socket? */
     if (strstart(host_spec, "unix:", &unixpath)) {
-        if (unixpath[0] != '/') { /* We demand  an absolute path*/
-            goto out;
-        }
+        s->is_unix = true;
         s->host_spec = g_strdup(unixpath);
     } else {
+        s->is_unix = false;
         s->host_spec = g_strdup(host_spec);
     }
 
@@ -262,7 +259,7 @@ static int nbd_establish_connection(BlockDriverState *bs)
     off_t size;
     size_t blocksize;
 
-    if (s->host_spec[0] == '/') {
+    if (s->is_unix) {
         sock = unix_socket_outgoing(s->host_spec);
     } else {
         sock = tcp_socket_outgoing_spec(s->host_spec);
@@ -320,7 +317,7 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
     qemu_co_mutex_init(&s->free_sema);
 
     /* Pop the config into our state object. Exit if invalid. */
-    result = nbd_config(s, filename, flags);
+    result = nbd_config(s, filename);
     if (result != 0) {
         return result;
     }
commit 7e7f4a0efc81258213dc34aeeaade36b0f59d076
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Sat Nov 3 18:06:26 2012 +0100

    qemu-nbd: initialize main loop before block layer
    
    qemu-nbd was broken because they initialized the block layer while
    qemu_aio_context was still NULL.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>

diff --git a/qemu-nbd.c b/qemu-nbd.c
index 15bcd08..80f08d8 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -539,6 +539,7 @@ int main(int argc, char **argv)
         snprintf(sockpath, 128, SOCKET_PATH, basename(device));
     }
 
+    qemu_init_main_loop();
     bdrv_init();
     atexit(bdrv_close_all);
 
@@ -584,7 +585,6 @@ int main(int argc, char **argv)
         memset(&client_thread, 0, sizeof(client_thread));
     }
 
-    qemu_init_main_loop();
     qemu_set_fd_handler2(fd, nbd_can_accept, nbd_accept, NULL,
                          (void *)(uintptr_t)fd);
 
commit 9d1530470bba5d8bec4796c0b43b874d5f9ef017
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Nov 8 10:14:46 2012 +0100

    ehci: fix migration
    
    Commit 5010d4dc618b6b8e7c21129c487c06f6493f71fc reorganized vmstate to
    split core + pci, but got two little details wrong.  Fix them.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c
index d5c7d46..fe45a1f 100644
--- a/hw/usb/hcd-ehci-pci.c
+++ b/hw/usb/hcd-ehci-pci.c
@@ -91,6 +91,7 @@ static const VMStateDescription vmstate_ehci_pci = {
     .fields      = (VMStateField[]) {
         VMSTATE_PCI_DEVICE(pcidev, EHCIPCIState),
         VMSTATE_STRUCT(ehci, EHCIPCIState, 2, vmstate_ehci, EHCIState),
+        VMSTATE_END_OF_LIST()
     }
 };
 
@@ -105,7 +106,7 @@ static void ehci_class_init(ObjectClass *klass, void *data)
     k->device_id = i->device_id;
     k->revision = i->revision;
     k->class_id = PCI_CLASS_SERIAL_USB;
-    dc->vmsd = &vmstate_ehci;
+    dc->vmsd = &vmstate_ehci_pci;
     dc->props = ehci_pci_properties;
 }
 
commit 616b5d53ae81a94a4aabe8a87a5d950e9d349bc5
Author: David Gibson <david at gibson.dropbear.id.au>
Date:   Mon Nov 5 14:29:01 2012 +1100

    xhci: Fix some DMA host endian bugs
    
    The xhci device does correct endian switches on the results of some DMAs
    but not all.  In particular, there are many DMAs of what are essentially
    arrays of 32-bit integers which never get byteswapped.  This causes them
    to be interpreted incorrectly on big-endian hosts, since (as per the xhci
    spec) these arrays are always little-endian in guest memory.
    
    This patch adds some helper functions to fix these bugs.  This may not be
    all the endian bugs in the xhci code, but it's certainly some of them and
    the Linux guest xhci driver certainly gets further with these fixes.
    
    Signed-off-by: David Gibson <david at gibson.dropbear.id.au>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index a181d45..8ef4b07 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -634,6 +634,34 @@ static inline dma_addr_t xhci_mask64(uint64_t addr)
     }
 }
 
+static inline void xhci_dma_read_u32s(XHCIState *xhci, dma_addr_t addr,
+                                      uint32_t *buf, size_t len)
+{
+    int i;
+
+    assert((len % sizeof(uint32_t)) == 0);
+
+    pci_dma_read(&xhci->pci_dev, addr, buf, len);
+
+    for (i = 0; i < (len / sizeof(uint32_t)); i++) {
+        buf[i] = le32_to_cpu(buf[i]);
+    }
+}
+
+static inline void xhci_dma_write_u32s(XHCIState *xhci, dma_addr_t addr,
+                                       uint32_t *buf, size_t len)
+{
+    int i;
+    uint32_t tmp[len / sizeof(uint32_t)];
+
+    assert((len % sizeof(uint32_t)) == 0);
+
+    for (i = 0; i < (len / sizeof(uint32_t)); i++) {
+        tmp[i] = cpu_to_le32(buf[i]);
+    }
+    pci_dma_write(&xhci->pci_dev, addr, tmp, len);
+}
+
 static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
 {
     int index;
@@ -1045,14 +1073,14 @@ static void xhci_set_ep_state(XHCIState *xhci, XHCIEPContext *epctx,
 {
     uint32_t ctx[5];
 
-    pci_dma_read(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx));
+    xhci_dma_read_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
     ctx[0] &= ~EP_STATE_MASK;
     ctx[0] |= state;
     ctx[2] = epctx->ring.dequeue | epctx->ring.ccs;
     ctx[3] = (epctx->ring.dequeue >> 16) >> 16;
     DPRINTF("xhci: set epctx: " DMA_ADDR_FMT " state=%d dequeue=%08x%08x\n",
             epctx->pctx, state, ctx[3], ctx[2]);
-    pci_dma_write(&xhci->pci_dev, epctx->pctx, ctx, sizeof(ctx));
+    xhci_dma_write_u32s(xhci, epctx->pctx, ctx, sizeof(ctx));
     epctx->state = state;
 }
 
@@ -1881,14 +1909,14 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
     assert(slotid >= 1 && slotid <= xhci->numslots);
 
     dcbaap = xhci_addr64(xhci->dcbaap_low, xhci->dcbaap_high);
-    pci_dma_read(&xhci->pci_dev, dcbaap + 8*slotid, &poctx, sizeof(poctx));
+    poctx = ldq_le_pci_dma(&xhci->pci_dev, dcbaap + 8*slotid);
     ictx = xhci_mask64(pictx);
-    octx = xhci_mask64(le64_to_cpu(poctx));
+    octx = xhci_mask64(poctx);
 
     DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx);
     DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx);
 
-    pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx));
+    xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
 
     if (ictl_ctx[0] != 0x0 || ictl_ctx[1] != 0x3) {
         fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@@ -1896,8 +1924,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
         return CC_TRB_ERROR;
     }
 
-    pci_dma_read(&xhci->pci_dev, ictx+32, slot_ctx, sizeof(slot_ctx));
-    pci_dma_read(&xhci->pci_dev, ictx+64, ep0_ctx, sizeof(ep0_ctx));
+    xhci_dma_read_u32s(xhci, ictx+32, slot_ctx, sizeof(slot_ctx));
+    xhci_dma_read_u32s(xhci, ictx+64, ep0_ctx, sizeof(ep0_ctx));
 
     DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
             slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
@@ -1951,8 +1979,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
     DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
             ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
 
-    pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
-    pci_dma_write(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx));
+    xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
+    xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
 
     return res;
 }
@@ -1985,17 +2013,17 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
             }
         }
 
-        pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+        xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
         slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
         slot_ctx[3] |= SLOT_ADDRESSED << SLOT_STATE_SHIFT;
         DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
                 slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-        pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+        xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
 
         return CC_SUCCESS;
     }
 
-    pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx));
+    xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
 
     if ((ictl_ctx[0] & 0x3) != 0x0 || (ictl_ctx[1] & 0x3) != 0x1) {
         fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@@ -2003,8 +2031,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
         return CC_TRB_ERROR;
     }
 
-    pci_dma_read(&xhci->pci_dev, ictx+32, islot_ctx, sizeof(islot_ctx));
-    pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+    xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx));
+    xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
 
     if (SLOT_STATE(slot_ctx[3]) < SLOT_ADDRESSED) {
         fprintf(stderr, "xhci: invalid slot state %08x\n", slot_ctx[3]);
@@ -2016,8 +2044,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
             xhci_disable_ep(xhci, slotid, i);
         }
         if (ictl_ctx[1] & (1<<i)) {
-            pci_dma_read(&xhci->pci_dev, ictx+32+(32*i), ep_ctx,
-                         sizeof(ep_ctx));
+            xhci_dma_read_u32s(xhci, ictx+32+(32*i), ep_ctx, sizeof(ep_ctx));
             DPRINTF("xhci: input ep%d.%d context: %08x %08x %08x %08x %08x\n",
                     i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
                     ep_ctx[3], ep_ctx[4]);
@@ -2029,7 +2056,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
             DPRINTF("xhci: output ep%d.%d context: %08x %08x %08x %08x %08x\n",
                     i/2, i%2, ep_ctx[0], ep_ctx[1], ep_ctx[2],
                     ep_ctx[3], ep_ctx[4]);
-            pci_dma_write(&xhci->pci_dev, octx+(32*i), ep_ctx, sizeof(ep_ctx));
+            xhci_dma_write_u32s(xhci, octx+(32*i), ep_ctx, sizeof(ep_ctx));
         }
     }
 
@@ -2041,7 +2068,7 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
     DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
             slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
 
-    pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+    xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
 
     return CC_SUCCESS;
 }
@@ -2066,7 +2093,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
     DPRINTF("xhci: input context at "DMA_ADDR_FMT"\n", ictx);
     DPRINTF("xhci: output context at "DMA_ADDR_FMT"\n", octx);
 
-    pci_dma_read(&xhci->pci_dev, ictx, ictl_ctx, sizeof(ictl_ctx));
+    xhci_dma_read_u32s(xhci, ictx, ictl_ctx, sizeof(ictl_ctx));
 
     if (ictl_ctx[0] != 0x0 || ictl_ctx[1] & ~0x3) {
         fprintf(stderr, "xhci: invalid input context control %08x %08x\n",
@@ -2075,12 +2102,12 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
     }
 
     if (ictl_ctx[1] & 0x1) {
-        pci_dma_read(&xhci->pci_dev, ictx+32, islot_ctx, sizeof(islot_ctx));
+        xhci_dma_read_u32s(xhci, ictx+32, islot_ctx, sizeof(islot_ctx));
 
         DPRINTF("xhci: input slot context: %08x %08x %08x %08x\n",
                 islot_ctx[0], islot_ctx[1], islot_ctx[2], islot_ctx[3]);
 
-        pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+        xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
 
         slot_ctx[1] &= ~0xFFFF; /* max exit latency */
         slot_ctx[1] |= islot_ctx[1] & 0xFFFF;
@@ -2090,17 +2117,17 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
         DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
                 slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
 
-        pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+        xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
     }
 
     if (ictl_ctx[1] & 0x2) {
-        pci_dma_read(&xhci->pci_dev, ictx+64, iep0_ctx, sizeof(iep0_ctx));
+        xhci_dma_read_u32s(xhci, ictx+64, iep0_ctx, sizeof(iep0_ctx));
 
         DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n",
                 iep0_ctx[0], iep0_ctx[1], iep0_ctx[2],
                 iep0_ctx[3], iep0_ctx[4]);
 
-        pci_dma_read(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx));
+        xhci_dma_read_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
 
         ep0_ctx[1] &= ~0xFFFF0000; /* max packet size*/
         ep0_ctx[1] |= iep0_ctx[1] & 0xFFFF0000;
@@ -2108,7 +2135,7 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid,
         DPRINTF("xhci: output ep0 context: %08x %08x %08x %08x %08x\n",
                 ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]);
 
-        pci_dma_write(&xhci->pci_dev, octx+32, ep0_ctx, sizeof(ep0_ctx));
+        xhci_dma_write_u32s(xhci, octx+32, ep0_ctx, sizeof(ep0_ctx));
     }
 
     return CC_SUCCESS;
@@ -2133,12 +2160,12 @@ static TRBCCode xhci_reset_slot(XHCIState *xhci, unsigned int slotid)
         }
     }
 
-    pci_dma_read(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+    xhci_dma_read_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
     slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
     slot_ctx[3] |= SLOT_DEFAULT << SLOT_STATE_SHIFT;
     DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n",
             slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]);
-    pci_dma_write(&xhci->pci_dev, octx, slot_ctx, sizeof(slot_ctx));
+    xhci_dma_write_u32s(xhci, octx, slot_ctx, sizeof(slot_ctx));
 
     return CC_SUCCESS;
 }
commit ffd8a97fb33d3b036d61c508bd73ee7fa4051c6b
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Nov 1 17:15:06 2012 +0100

    usb/combined-packet: Move freeing of combined to usb_combined_packet_remove()
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c
index e722198..4a0c299 100644
--- a/hw/usb/combined-packet.c
+++ b/hw/usb/combined-packet.c
@@ -31,12 +31,16 @@ static void usb_combined_packet_add(USBCombinedPacket *combined, USBPacket *p)
     p->combined = combined;
 }
 
+/* Note will free combined when the last packet gets removed */
 static void usb_combined_packet_remove(USBCombinedPacket *combined,
                                        USBPacket *p)
 {
     assert(p->combined == combined);
     p->combined = NULL;
     QTAILQ_REMOVE(&combined->packets, p, combined_entry);
+    if (QTAILQ_EMPTY(&combined->packets)) {
+        g_free(combined);
+    }
 }
 
 /* Also handles completion of non combined packets for pipelined input eps */
@@ -45,9 +49,8 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
     USBCombinedPacket *combined = p->combined;
     USBEndpoint *ep = p->ep;
     USBPacket *next;
-    enum { completing, complete, leftover };
-    int status, actual_length, state = completing;
-    bool short_not_ok;
+    int status, actual_length;
+    bool short_not_ok, done = false;
 
     if (combined == NULL) {
         usb_packet_complete_one(dev, p);
@@ -61,39 +64,34 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
     short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok;
 
     QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
-        if (state == completing) {
+        if (!done) {
             /* Distribute data over uncombined packets */
             if (actual_length >= p->iov.size) {
                 p->actual_length = p->iov.size;
             } else {
                 /* Send short or error packet to complete the transfer */
                 p->actual_length = actual_length;
-                state = complete;
+                done = true;
             }
             /* Report status on the last packet */
-            if (state == complete || next == NULL) {
+            if (done || next == NULL) {
                 p->status = status;
             } else {
                 p->status = USB_RET_SUCCESS;
             }
             p->short_not_ok = short_not_ok;
+            /* Note will free combined when the last packet gets removed! */
             usb_combined_packet_remove(combined, p);
             usb_packet_complete_one(dev, p);
             actual_length -= p->actual_length;
         } else {
             /* Remove any leftover packets from the queue */
-            state = leftover;
             p->status = USB_RET_REMOVE_FROM_QUEUE;
+            /* Note will free combined on the last packet! */
             dev->port->ops->complete(dev->port, p);
         }
     }
-    /*
-     * If we had leftover packets the hcd driver will have cancelled them
-     * and usb_combined_packet_cancel has already freed combined!
-     */
-    if (state != leftover) {
-        g_free(combined);
-    }
+    /* Do not use combined here, it has been freed! */
 leave:
     /* Check if there are packets in the queue waiting for our completion */
     usb_ep_combine_input_packets(ep);
@@ -104,14 +102,13 @@ void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p)
 {
     USBCombinedPacket *combined = p->combined;
     assert(combined != NULL);
+    USBPacket *first = p->combined->first;
 
+    /* Note will free combined on the last packet! */
     usb_combined_packet_remove(combined, p);
-    if (p == combined->first) {
+    if (p == first) {
         usb_device_cancel_packet(dev, p);
     }
-    if (QTAILQ_EMPTY(&combined->packets)) {
-        g_free(combined);
-    }
 }
 
 /*
commit 9b8251c5c44a70a63c642e4e345fa8c12ed022ed
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Nov 1 17:15:05 2012 +0100

    xhci: Add support for packets with both data and an error status
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index d4a2e0c..a181d45 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1388,7 +1388,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
     XHCIState *xhci = xfer->xhci;
     int i;
 
-    left = xfer->packet.status ? 0 : xfer->packet.actual_length;
+    left = xfer->packet.actual_length;
 
     for (i = 0; i < xfer->trb_count; i++) {
         XHCITRB *trb = &xfer->trbs[i];
@@ -1416,7 +1416,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
 
         if (!reported && ((trb->control & TRB_TR_IOC) ||
                           (shortpkt && (trb->control & TRB_TR_ISP)) ||
-                          (xfer->status != CC_SUCCESS))) {
+                          (xfer->status != CC_SUCCESS && left == 0))) {
             event.slotid = xfer->slotid;
             event.epid = xfer->epid;
             event.length = (trb->status & 0x1ffff) - chunk;
commit e696b1da42cd80787d45f6e9a6329d9ef3c8acb2
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Nov 1 17:15:04 2012 +0100

    ehci: Add support for packets with both data and an error status
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 73be575..ee6c9ae 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1126,16 +1126,16 @@ static int ehci_init_transfer(EHCIPacket *p)
     return 0;
 }
 
-static void ehci_finish_transfer(EHCIQueue *q, int status)
+static void ehci_finish_transfer(EHCIQueue *q, int len)
 {
     uint32_t cpage, offset;
 
-    if (status > 0) {
+    if (len > 0) {
         /* update cpage & offset */
         cpage  = get_field(q->qh.token, QTD_TOKEN_CPAGE);
         offset = q->qh.bufptr[0] & ~QTD_BUFPTR_MASK;
 
-        offset += status;
+        offset += len;
         cpage  += offset >> QTD_BUFPTR_SH;
         offset &= ~QTD_BUFPTR_MASK;
 
@@ -1168,7 +1168,6 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
 
     trace_usb_ehci_packet_action(p->queue, p, "wakeup");
     p->async = EHCI_ASYNC_FINISHED;
-    p->usb_status = packet->status ? packet->status : packet->actual_length;
 
     if (p->queue->async) {
         qemu_bh_schedule(p->queue->ehci->async_bh);
@@ -1178,58 +1177,60 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
 static void ehci_execute_complete(EHCIQueue *q)
 {
     EHCIPacket *p = QTAILQ_FIRST(&q->packets);
+    uint32_t tbytes;
 
     assert(p != NULL);
     assert(p->qtdaddr == q->qtdaddr);
     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);
+    DPRINTF("execute_complete: qhaddr 0x%x, next 0x%x, qtdaddr 0x%x, "
+            "status %d, actual_length %d\n",
+            q->qhaddr, q->qh.next, q->qtdaddr,
+            p->packet.status, p->packet.actual_length);
 
-    if (p->usb_status < 0) {
-        switch (p->usb_status) {
-        case USB_RET_IOERROR:
-        case USB_RET_NODEV:
-            q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
-            set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
-            ehci_raise_irq(q->ehci, USBSTS_ERRINT);
-            break;
-        case USB_RET_STALL:
-            q->qh.token |= QTD_TOKEN_HALT;
-            ehci_raise_irq(q->ehci, USBSTS_ERRINT);
-            break;
-        case USB_RET_NAK:
-            set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
-            return; /* We're not done yet with this transaction */
-        case USB_RET_BABBLE:
-            q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
-            ehci_raise_irq(q->ehci, USBSTS_ERRINT);
-            break;
-        default:
-            /* should not be triggerable */
-            fprintf(stderr, "USB invalid response %d\n", p->usb_status);
-            assert(0);
-            break;
+    switch (p->packet.status) {
+    case USB_RET_SUCCESS:
+        break;
+    case USB_RET_IOERROR:
+    case USB_RET_NODEV:
+        q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_XACTERR);
+        set_field(&q->qh.token, 0, QTD_TOKEN_CERR);
+        ehci_raise_irq(q->ehci, USBSTS_ERRINT);
+        break;
+    case USB_RET_STALL:
+        q->qh.token |= QTD_TOKEN_HALT;
+        ehci_raise_irq(q->ehci, USBSTS_ERRINT);
+        break;
+    case USB_RET_NAK:
+        set_field(&q->qh.altnext_qtd, 0, QH_ALTNEXT_NAKCNT);
+        return; /* We're not done yet with this transaction */
+    case USB_RET_BABBLE:
+        q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
+        ehci_raise_irq(q->ehci, USBSTS_ERRINT);
+        break;
+    default:
+        /* should not be triggerable */
+        fprintf(stderr, "USB invalid response %d\n", p->packet.status);
+        assert(0);
+        break;
+    }
+
+    /* TODO check 4.12 for splits */
+    tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
+    if (tbytes && p->pid == USB_TOKEN_IN) {
+        tbytes -= p->packet.actual_length;
+        if (tbytes) {
+            /* 4.15.1.2 must raise int on a short input packet */
+            ehci_raise_irq(q->ehci, USBSTS_INT);
         }
     } else {
-        // TODO check 4.12 for splits
-        uint32_t tbytes = get_field(q->qh.token, QTD_TOKEN_TBYTES);
-
-        if (tbytes && p->pid == USB_TOKEN_IN) {
-            tbytes -= p->usb_status;
-            if (tbytes) {
-                /* 4.15.1.2 must raise int on a short input packet */
-                ehci_raise_irq(q->ehci, USBSTS_INT);
-            }
-        } else {
-            tbytes = 0;
-        }
-
-        DPRINTF("updating tbytes to %d\n", tbytes);
-        set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
+        tbytes = 0;
     }
-    ehci_finish_transfer(q, p->usb_status);
+    DPRINTF("updating tbytes to %d\n", tbytes);
+    set_field(&q->qh.token, tbytes, QTD_TOKEN_TBYTES);
+
+    ehci_finish_transfer(q, p->packet.actual_length);
     usb_packet_unmap(&p->packet, &p->sgl);
     qemu_sglist_destroy(&p->sgl);
     p->async = EHCI_ASYNC_NONE;
@@ -1321,7 +1322,6 @@ static int ehci_process_itd(EHCIState *ehci,
 {
     USBDevice *dev;
     USBEndpoint *ep;
-    int ret;
     uint32_t i, len, pid, dir, devaddr, endp;
     uint32_t pg, off, ptr1, ptr2, max, mult;
 
@@ -1368,45 +1368,43 @@ static int ehci_process_itd(EHCIState *ehci,
                 usb_packet_map(&ehci->ipacket, &ehci->isgl);
                 usb_handle_packet(dev, &ehci->ipacket);
                 usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
-                ret = (ehci->ipacket.status == USB_RET_SUCCESS) ?
-                      ehci->ipacket.actual_length : ehci->ipacket.status;
             } else {
                 DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
-                ret = USB_RET_NAK;
+                ehci->ipacket.status = USB_RET_NAK;
+                ehci->ipacket.actual_length = 0;
             }
             qemu_sglist_destroy(&ehci->isgl);
 
-            if (ret < 0) {
-                switch (ret) {
-                default:
-                    fprintf(stderr, "Unexpected iso usb result: %d\n", ret);
-                    /* Fall through */
-                case USB_RET_IOERROR:
-                case USB_RET_NODEV:
-                    /* 3.3.2: XACTERR is only allowed on IN transactions */
-                    if (dir) {
-                        itd->transact[i] |= ITD_XACT_XACTERR;
-                        ehci_raise_irq(ehci, USBSTS_ERRINT);
-                    }
-                    break;
-                case USB_RET_BABBLE:
-                    itd->transact[i] |= ITD_XACT_BABBLE;
+            switch (ehci->ipacket.status) {
+            case USB_RET_SUCCESS:
+                break;
+            default:
+                fprintf(stderr, "Unexpected iso usb result: %d\n",
+                        ehci->ipacket.status);
+                /* Fall through */
+            case USB_RET_IOERROR:
+            case USB_RET_NODEV:
+                /* 3.3.2: XACTERR is only allowed on IN transactions */
+                if (dir) {
+                    itd->transact[i] |= ITD_XACT_XACTERR;
                     ehci_raise_irq(ehci, USBSTS_ERRINT);
-                    break;
-                case USB_RET_NAK:
-                    /* no data for us, so do a zero-length transfer */
-                    ret = 0;
-                    break;
                 }
+                break;
+            case USB_RET_BABBLE:
+                itd->transact[i] |= ITD_XACT_BABBLE;
+                ehci_raise_irq(ehci, USBSTS_ERRINT);
+                break;
+            case USB_RET_NAK:
+                /* no data for us, so do a zero-length transfer */
+                ehci->ipacket.actual_length = 0;
+                break;
             }
-            if (ret >= 0) {
-                if (!dir) {
-                    /* OUT */
-                    set_field(&itd->transact[i], len - ret, ITD_XACT_LENGTH);
-                } else {
-                    /* IN */
-                    set_field(&itd->transact[i], ret, ITD_XACT_LENGTH);
-                }
+            if (!dir) {
+                set_field(&itd->transact[i], len - ehci->ipacket.actual_length,
+                          ITD_XACT_LENGTH); /* OUT */
+            } else {
+                set_field(&itd->transact[i], ehci->ipacket.actual_length,
+                          ITD_XACT_LENGTH); /* IN */
             }
             if (itd->transact[i] & ITD_XACT_IOC) {
                 ehci_raise_irq(ehci, USBSTS_INT);
@@ -1862,11 +1860,6 @@ static int ehci_state_execute(EHCIQueue *q)
         }
         goto out;
     }
-    if (p->packet.status == USB_RET_SUCCESS) {
-        p->usb_status = p->packet.actual_length;
-    } else {
-        p->usb_status = p->packet.status;
-    }
 
     ehci_set_state(q->ehci, q->async, EST_EXECUTING);
     again = 1;
@@ -1890,7 +1883,7 @@ static int ehci_state_executing(EHCIQueue *q)
     }
 
     /* 4.10.5 */
-    if (p->usb_status == USB_RET_NAK) {
+    if (p->packet.status == USB_RET_NAK) {
         ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
     } else {
         ehci_set_state(q->ehci, q->async, EST_WRITEBACK);
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index 0ec675c..d8078f4 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -230,7 +230,6 @@ struct EHCIPacket {
     QEMUSGList sgl;
     int pid;
     enum async_state async;
-    int usb_status;
 };
 
 struct EHCIQueue {
commit 01e26b0ea3c289efc58d31e28408e42c3fcded22
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Nov 1 17:15:03 2012 +0100

    ehci: Get rid of the magical PROC_ERR status
    
    Instead make ehci_execute and ehci_fill_queue return the again value.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 3a1f513..73be575 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -29,9 +29,6 @@
 
 #include "hw/usb/hcd-ehci.h"
 
-/* internal processing - reset HC to try and recover */
-#define USB_RET_PROCERR   (-99)
-
 /* Capability Registers Base Address - section 2.2 */
 #define CAPLENGTH        0x0000  /* 1-byte, 0x0001 reserved */
 #define HCIVERSION       0x0002  /* 2-bytes, i/f version # */
@@ -1111,7 +1108,7 @@ static int ehci_init_transfer(EHCIPacket *p)
     while (bytes > 0) {
         if (cpage > 4) {
             fprintf(stderr, "cpage out of range (%d)\n", cpage);
-            return USB_RET_PROCERR;
+            return -1;
         }
 
         page  = p->qtd.bufptr[cpage] & QTD_BUFPTR_MASK;
@@ -1248,8 +1245,7 @@ static void ehci_execute_complete(EHCIQueue *q)
     }
 }
 
-// 4.10.3
-
+/* 4.10.3 returns "again" */
 static int ehci_execute(EHCIPacket *p, const char *action)
 {
     USBEndpoint *ep;
@@ -1261,13 +1257,13 @@ static int ehci_execute(EHCIPacket *p, const char *action)
 
     if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
         fprintf(stderr, "Attempting to execute inactive qtd\n");
-        return USB_RET_PROCERR;
+        return -1;
     }
 
     if (get_field(p->qtd.token, QTD_TOKEN_TBYTES) > BUFF_SIZE) {
         ehci_trace_guest_bug(p->queue->ehci,
                              "guest requested more bytes than allowed");
-        return USB_RET_PROCERR;
+        return -1;
     }
 
     p->pid = (p->qtd.token & QTD_TOKEN_PID_MASK) >> QTD_TOKEN_PID_SH;
@@ -1291,7 +1287,7 @@ static int ehci_execute(EHCIPacket *p, const char *action)
 
     if (p->async == EHCI_ASYNC_NONE) {
         if (ehci_init_transfer(p) != 0) {
-            return USB_RET_PROCERR;
+            return -1;
         }
 
         spd = (p->pid == USB_TOKEN_IN && NLPTR_TBIT(p->qtd.altnext) == 0);
@@ -1310,14 +1306,10 @@ static int ehci_execute(EHCIPacket *p, const char *action)
 
     if (p->packet.actual_length > BUFF_SIZE) {
         fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
-        return USB_RET_PROCERR;
+        return -1;
     }
 
-    if (p->packet.status == USB_RET_SUCCESS) {
-        return p->packet.actual_length;
-    } else {
-        return p->packet.status;
-    }
+    return 1;
 }
 
 /*  4.7.2
@@ -1352,7 +1344,7 @@ static int ehci_process_itd(EHCIState *ehci,
             }
 
             if (len > BUFF_SIZE) {
-                return USB_RET_PROCERR;
+                return -1;
             }
 
             qemu_sglist_init(&ehci->isgl, 2, ehci->dma);
@@ -1752,8 +1744,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
             break;
         case EHCI_ASYNC_INFLIGHT:
             /* Check if the guest has added new tds to the queue */
-            again = (ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)) ==
-                     USB_RET_PROCERR) ? -1 : 1;
+            again = ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head));
             /* Unfinished async handled packet, go horizontal */
             ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
             break;
@@ -1790,6 +1781,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
     return again;
 }
 
+/* Returns "again" */
 static int ehci_fill_queue(EHCIPacket *p)
 {
     USBEndpoint *ep = p->packet.ep;
@@ -1818,17 +1810,14 @@ static int ehci_fill_queue(EHCIPacket *p)
         p = ehci_alloc_packet(q);
         p->qtdaddr = qtdaddr;
         p->qtd = qtd;
-        p->usb_status = ehci_execute(p, "queue");
-        if (p->usb_status == USB_RET_PROCERR) {
-            break;
+        if (ehci_execute(p, "queue") == -1) {
+            return -1;
         }
-        assert(p->usb_status == USB_RET_ASYNC);
+        assert(p->packet.status == USB_RET_ASYNC);
         p->async = EHCI_ASYNC_INFLIGHT;
     }
-    if (p->usb_status != USB_RET_PROCERR) {
-        usb_device_flush_ep_queue(ep->dev, ep);
-    }
-    return p->usb_status;
+    usb_device_flush_ep_queue(ep->dev, ep);
+    return 1;
 }
 
 static int ehci_state_execute(EHCIQueue *q)
@@ -1857,23 +1846,27 @@ static int ehci_state_execute(EHCIQueue *q)
         ehci_set_usbsts(q->ehci, USBSTS_REC);
     }
 
-    p->usb_status = ehci_execute(p, "process");
-    if (p->usb_status == USB_RET_PROCERR) {
-        again = -1;
+    again = ehci_execute(p, "process");
+    if (again == -1) {
         goto out;
     }
-    if (p->usb_status == USB_RET_ASYNC) {
+    if (p->packet.status == USB_RET_ASYNC) {
         ehci_flush_qh(q);
         trace_usb_ehci_packet_action(p->queue, p, "async");
         p->async = EHCI_ASYNC_INFLIGHT;
         ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
         if (q->async) {
-            again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1;
+            again = ehci_fill_queue(p);
         } else {
             again = 1;
         }
         goto out;
     }
+    if (p->packet.status == USB_RET_SUCCESS) {
+        p->usb_status = p->packet.actual_length;
+    } else {
+        p->usb_status = p->packet.status;
+    }
 
     ehci_set_state(q->ehci, q->async, EST_EXECUTING);
     again = 1;
commit e94ca437ba92f6e0aeeb0dbda75e158479d413b7
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Nov 1 17:15:02 2012 +0100

    usb-redir: Allow packets to have both data and an error-status
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index cd4388e..be9a232 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -521,22 +521,16 @@ static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
                  isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
 
         status = isop->status;
-        if (status != usb_redir_success) {
-            bufp_free(dev, isop, ep);
-            p->status = USB_RET_IOERROR;
-            return;
-        }
-
         len = isop->len;
         if (len > p->iov.size) {
             ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
                   ep, len, (int)p->iov.size);
-            bufp_free(dev, isop, ep);
-            p->status = USB_RET_BABBLE;
-            return;
+            len = p->iov.size;
+            status = usb_redir_babble;
         }
         usb_packet_copy(p, isop->data, len);
         bufp_free(dev, isop, ep);
+        usbredir_handle_status(dev, p, status);
     } else {
         /* If the stream was not started because of a pending error don't
            send the packet to the usb-host */
@@ -656,21 +650,15 @@ static void usbredir_handle_interrupt_data(USBRedirDevice *dev,
                 intp->status, intp->len);
 
         status = intp->status;
-        if (status != usb_redir_success) {
-            bufp_free(dev, intp, ep);
-            usbredir_handle_status(dev, p, status);
-            return;
-        }
-
         len = intp->len;
         if (len > p->iov.size) {
             ERROR("received int data is larger then packet ep %02X\n", ep);
-            bufp_free(dev, intp, ep);
-            p->status = USB_RET_BABBLE;
-            return;
+            len = p->iov.size;
+            status = usb_redir_babble;
         }
         usb_packet_copy(p, intp->data, len);
         bufp_free(dev, intp, ep);
+        usbredir_handle_status(dev, p, status);
     } else {
         /* Output interrupt endpoint, normal async operation */
         struct usb_redir_interrupt_packet_header interrupt_packet;
@@ -1542,16 +1530,15 @@ static void usbredir_control_packet(void *priv, uint64_t id,
     p = usbredir_find_packet_by_id(dev, 0, id);
     if (p) {
         usbredir_handle_status(dev, p, control_packet->status);
-        if (p->status == USB_RET_SUCCESS) {
+        if (data_len > 0) {
             usbredir_log_data(dev, "ctrl data in:", data, data_len);
-            if (data_len <= sizeof(dev->dev.data_buf)) {
-                memcpy(dev->dev.data_buf, data, data_len);
-            } else {
+            if (data_len > sizeof(dev->dev.data_buf)) {
                 ERROR("ctrl buffer too small (%d > %zu)\n",
                       data_len, sizeof(dev->dev.data_buf));
                 p->status = USB_RET_STALL;
-                len = 0;
+                data_len = len = sizeof(dev->dev.data_buf);
             }
+            memcpy(dev->dev.data_buf, data, data_len);
         }
         p->actual_length = len;
         usb_generic_async_ctrl_complete(&dev->dev, p);
@@ -1575,20 +1562,19 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
     if (p) {
         size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
         usbredir_handle_status(dev, p, bulk_packet->status);
-        if (p->status == USB_RET_SUCCESS) {
+        if (data_len > 0) {
             usbredir_log_data(dev, "bulk data in:", data, data_len);
-            if (data_len <= size) {
-                if (p->combined) {
-                    iov_from_buf(p->combined->iov.iov, p->combined->iov.niov,
-                                 0, data, data_len);
-                } else {
-                    usb_packet_copy(p, data, data_len);
-                }
-            } else {
+            if (data_len > size) {
                 ERROR("bulk got more data then requested (%d > %zd)\n",
                       data_len, p->iov.size);
                 p->status = USB_RET_BABBLE;
-                len = 0;
+                data_len = len = size;
+            }
+            if (p->combined) {
+                iov_from_buf(p->combined->iov.iov, p->combined->iov.niov,
+                             0, data, data_len);
+            } else {
+                usb_packet_copy(p, data, data_len);
             }
         }
         p->actual_length = len;
@@ -1653,12 +1639,10 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
         /* bufp_alloc also adds the packet to the ep queue */
         bufp_alloc(dev, data, data_len, interrupt_packet->status, ep);
     } else {
-        int len = interrupt_packet->length;
-
         USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
         if (p) {
             usbredir_handle_status(dev, p, interrupt_packet->status);
-            p->actual_length = len;
+            p->actual_length = interrupt_packet->length;
             usb_packet_complete(&dev->dev, p);
         }
     }
commit 9a77a0f58923443913e1071ffb47b74c54566e70
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Nov 1 17:15:01 2012 +0100

    usb: split packet result into actual_length + status
    
    Since with the ehci and xhci controllers a single packet can be larger
    then maxpacketsize, it is possible for the result of a single packet
    to be both having transferred some data as well as the transfer to have
    an error.
    
    An example would be an input transfer from a bulk endpoint successfully
    receiving 1 or more maxpacketsize packets from the device, followed
    by a packet signalling halt.
    
    While already touching all the devices and controllers handle_packet /
    handle_data / handle_control code, also change the return type of
    these functions to void, solely storing the status in the packet. To
    make the code paths for regular versus async packet handling more
    uniform.
    
    This patch unfortunately is somewhat invasive, since makeing the qemu
    usb core deal with this requires changes everywhere. This patch only
    prepares the usb core for this, all the hcd / device changes are done
    in such a way that there are no functional changes.
    
    This patch has been tested with uhci and ehci hcds, together with usb-audio,
    usb-hid and usb-storage devices, as well as with usb-redir redirection
    with a wide variety of real devices.
    
    Note that there is usually no need to directly set packet->actual_length
    form devices handle_data callback, as that is done by usb_packet_copy()
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb.h b/hw/usb.h
index aef07f4..7d6de69 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -38,6 +38,7 @@
 #define USB_TOKEN_IN    0x69 /* device -> host */
 #define USB_TOKEN_OUT   0xe1 /* host -> device */
 
+#define USB_RET_SUCCESS           (0)
 #define USB_RET_NODEV             (-1)
 #define USB_RET_NAK               (-2)
 #define USB_RET_STALL             (-3)
@@ -280,18 +281,20 @@ typedef struct USBDeviceClass {
      * Process control request.
      * Called from handle_packet().
      *
-     * Returns length or one of the USB_RET_ codes.
+     * Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
+     * then the number of bytes transfered is stored in p->actual_length
      */
-    int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
-                          int index, int length, uint8_t *data);
+    void (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
+                           int index, int length, uint8_t *data);
 
     /*
      * Process data transfers (both BULK and ISOC).
      * Called from handle_packet().
      *
-     * Returns length or one of the USB_RET_ codes.
+     * Status gets stored in p->status, and if p->status == USB_RET_SUCCESS
+     * then the number of bytes transfered is stored in p->actual_length
      */
-    int (*handle_data)(USBDevice *dev, USBPacket *p);
+    void (*handle_data)(USBDevice *dev, USBPacket *p);
 
     void (*set_interface)(USBDevice *dev, int interface,
                           int alt_old, int alt_new);
@@ -354,7 +357,8 @@ struct USBPacket {
     uint64_t parameter; /* control transfers */
     bool short_not_ok;
     bool int_req;
-    int result; /* transfer length or USB_RET_* status code */
+    int status; /* USB_RET_* status code */
+    int actual_length; /* Number of bytes actually transfered */
     /* Internal use by the USB layer.  */
     USBPacketState state;
     USBCombinedPacket *combined;
@@ -388,7 +392,7 @@ static inline bool usb_packet_is_inflight(USBPacket *p)
 
 USBDevice *usb_find_device(USBPort *port, uint8_t addr);
 
-int usb_handle_packet(USBDevice *dev, USBPacket *p);
+void usb_handle_packet(USBDevice *dev, USBPacket *p);
 void usb_packet_complete(USBDevice *dev, USBPacket *p);
 void usb_packet_complete_one(USBDevice *dev, USBPacket *p);
 void usb_cancel_packet(USBPacket * p);
@@ -523,10 +527,10 @@ void usb_device_handle_attach(USBDevice *dev);
 
 void usb_device_handle_reset(USBDevice *dev);
 
-int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request, int value,
-                              int index, int length, uint8_t *data);
+void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
+                               int val, int index, int length, uint8_t *data);
 
-int usb_device_handle_data(USBDevice *dev, USBPacket *p);
+void usb_device_handle_data(USBDevice *dev, USBPacket *p);
 
 void usb_device_set_interface(USBDevice *dev, int interface,
                               int alt_old, int alt_new);
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 8066291..99aac7a 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -140,24 +140,21 @@ void usb_device_handle_reset(USBDevice *dev)
     }
 }
 
-int usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
-                              int value, int index, int length, uint8_t *data)
+void usb_device_handle_control(USBDevice *dev, USBPacket *p, int request,
+                               int value, int index, int length, uint8_t *data)
 {
     USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
     if (klass->handle_control) {
-        return klass->handle_control(dev, p, request, value, index, length,
-                                         data);
+        klass->handle_control(dev, p, request, value, index, length, data);
     }
-    return -ENOSYS;
 }
 
-int usb_device_handle_data(USBDevice *dev, USBPacket *p)
+void usb_device_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
     if (klass->handle_data) {
-        return klass->handle_data(dev, p);
+        klass->handle_data(dev, p);
     }
-    return -ENOSYS;
 }
 
 const char *usb_device_get_product_desc(USBDevice *dev)
diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c
index 3904e71..e722198 100644
--- a/hw/usb/combined-packet.c
+++ b/hw/usb/combined-packet.c
@@ -46,7 +46,7 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
     USBEndpoint *ep = p->ep;
     USBPacket *next;
     enum { completing, complete, leftover };
-    int result, state = completing;
+    int status, actual_length, state = completing;
     bool short_not_ok;
 
     if (combined == NULL) {
@@ -56,27 +56,34 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p)
 
     assert(combined->first == p && p == QTAILQ_FIRST(&combined->packets));
 
-    result = combined->first->result;
+    status = combined->first->status;
+    actual_length = combined->first->actual_length;
     short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok;
 
     QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) {
         if (state == completing) {
             /* Distribute data over uncombined packets */
-            if (result >= p->iov.size) {
-                p->result = p->iov.size;
+            if (actual_length >= p->iov.size) {
+                p->actual_length = p->iov.size;
             } else {
                 /* Send short or error packet to complete the transfer */
-                p->result = result;
+                p->actual_length = actual_length;
                 state = complete;
             }
+            /* Report status on the last packet */
+            if (state == complete || next == NULL) {
+                p->status = status;
+            } else {
+                p->status = USB_RET_SUCCESS;
+            }
             p->short_not_ok = short_not_ok;
             usb_combined_packet_remove(combined, p);
             usb_packet_complete_one(dev, p);
-            result -= p->result;
+            actual_length -= p->actual_length;
         } else {
             /* Remove any leftover packets from the queue */
             state = leftover;
-            p->result = USB_RET_REMOVE_FROM_QUEUE;
+            p->status = USB_RET_REMOVE_FROM_QUEUE;
             dev->port->ops->complete(dev->port, p);
         }
     }
@@ -117,7 +124,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
 {
     USBPacket *p, *u, *next, *prev = NULL, *first = NULL;
     USBPort *port = ep->dev->port;
-    int ret, totalsize;
+    int totalsize;
 
     assert(ep->pipeline);
     assert(ep->pid == USB_TOKEN_IN);
@@ -125,7 +132,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
     QTAILQ_FOREACH_SAFE(p, &ep->queue, queue, next) {
         /* Empty the queue on a halt */
         if (ep->halted) {
-            p->result = USB_RET_REMOVE_FROM_QUEUE;
+            p->status = USB_RET_REMOVE_FROM_QUEUE;
             port->ops->complete(port, p);
             continue;
         }
@@ -166,8 +173,8 @@ void usb_ep_combine_input_packets(USBEndpoint *ep)
                 next == NULL ||
                 /* Work around for Linux usbfs bulk splitting + migration */
                 (totalsize == 16348 && p->int_req)) {
-            ret = usb_device_handle_data(ep->dev, first);
-            assert(ret == USB_RET_ASYNC);
+            usb_device_handle_data(ep->dev, first);
+            assert(first->status == USB_RET_ASYNC);
             if (first->combined) {
                 QTAILQ_FOREACH(u, &first->combined->packets, combined_entry) {
                     usb_packet_set_state(u, USB_PACKET_ASYNC);
diff --git a/hw/usb/core.c b/hw/usb/core.c
index ab37f6f..52b5310 100644
--- a/hw/usb/core.c
+++ b/hw/usb/core.c
@@ -97,17 +97,17 @@ void usb_wakeup(USBEndpoint *ep)
 #define SETUP_STATE_ACK   3
 #define SETUP_STATE_PARAM 4
 
-static int do_token_setup(USBDevice *s, USBPacket *p)
+static void do_token_setup(USBDevice *s, USBPacket *p)
 {
     int request, value, index;
-    int ret = 0;
 
     if (p->iov.size != 8) {
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        return;
     }
 
     usb_packet_copy(p, s->setup_buf, p->iov.size);
-    p->result = 0;
+    p->actual_length = 0;
     s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
     s->setup_index = 0;
 
@@ -116,24 +116,26 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
     index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
 
     if (s->setup_buf[0] & USB_DIR_IN) {
-        ret = usb_device_handle_control(s, p, request, value, index,
-                                        s->setup_len, s->data_buf);
-        if (ret == USB_RET_ASYNC) {
-             s->setup_state = SETUP_STATE_SETUP;
-             return USB_RET_ASYNC;
+        usb_device_handle_control(s, p, request, value, index,
+                                  s->setup_len, s->data_buf);
+        if (p->status == USB_RET_ASYNC) {
+            s->setup_state = SETUP_STATE_SETUP;
+        }
+        if (p->status != USB_RET_SUCCESS) {
+            return;
         }
-        if (ret < 0)
-            return ret;
 
-        if (ret < s->setup_len)
-            s->setup_len = ret;
+        if (p->actual_length < s->setup_len) {
+            s->setup_len = p->actual_length;
+        }
         s->setup_state = SETUP_STATE_DATA;
     } else {
         if (s->setup_len > sizeof(s->data_buf)) {
             fprintf(stderr,
                 "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
                 s->setup_len, sizeof(s->data_buf));
-            return USB_RET_STALL;
+            p->status = USB_RET_STALL;
+            return;
         }
         if (s->setup_len == 0)
             s->setup_state = SETUP_STATE_ACK;
@@ -141,13 +143,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
             s->setup_state = SETUP_STATE_DATA;
     }
 
-    return ret;
+    p->actual_length = 8;
 }
 
-static int do_token_in(USBDevice *s, USBPacket *p)
+static void do_token_in(USBDevice *s, USBPacket *p)
 {
     int request, value, index;
-    int ret = 0;
 
     assert(p->ep->nr == 0);
 
@@ -158,19 +159,15 @@ static int do_token_in(USBDevice *s, USBPacket *p)
     switch(s->setup_state) {
     case SETUP_STATE_ACK:
         if (!(s->setup_buf[0] & USB_DIR_IN)) {
-            ret = usb_device_handle_control(s, p, request, value, index,
-                                            s->setup_len, s->data_buf);
-            if (ret == USB_RET_ASYNC) {
-                return USB_RET_ASYNC;
+            usb_device_handle_control(s, p, request, value, index,
+                                      s->setup_len, s->data_buf);
+            if (p->status == USB_RET_ASYNC) {
+                return;
             }
             s->setup_state = SETUP_STATE_IDLE;
-            if (ret > 0)
-                return 0;
-            return ret;
+            p->actual_length = 0;
         }
-
-        /* return 0 byte */
-        return 0;
+        break;
 
     case SETUP_STATE_DATA:
         if (s->setup_buf[0] & USB_DIR_IN) {
@@ -180,20 +177,21 @@ static int do_token_in(USBDevice *s, USBPacket *p)
             }
             usb_packet_copy(p, s->data_buf + s->setup_index, len);
             s->setup_index += len;
-            if (s->setup_index >= s->setup_len)
+            if (s->setup_index >= s->setup_len) {
                 s->setup_state = SETUP_STATE_ACK;
-            return len;
+            }
+            return;
         }
-
         s->setup_state = SETUP_STATE_IDLE;
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        break;
 
     default:
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
     }
 }
 
-static int do_token_out(USBDevice *s, USBPacket *p)
+static void do_token_out(USBDevice *s, USBPacket *p)
 {
     assert(p->ep->nr == 0);
 
@@ -205,7 +203,7 @@ static int do_token_out(USBDevice *s, USBPacket *p)
         } else {
             /* ignore additional output */
         }
-        return 0;
+        break;
 
     case SETUP_STATE_DATA:
         if (!(s->setup_buf[0] & USB_DIR_IN)) {
@@ -215,23 +213,23 @@ static int do_token_out(USBDevice *s, USBPacket *p)
             }
             usb_packet_copy(p, s->data_buf + s->setup_index, len);
             s->setup_index += len;
-            if (s->setup_index >= s->setup_len)
+            if (s->setup_index >= s->setup_len) {
                 s->setup_state = SETUP_STATE_ACK;
-            return len;
+            }
+            return;
         }
-
         s->setup_state = SETUP_STATE_IDLE;
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        break;
 
     default:
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
     }
 }
 
-static int do_parameter(USBDevice *s, USBPacket *p)
+static void do_parameter(USBDevice *s, USBPacket *p)
 {
-    int request, value, index;
-    int i, ret = 0;
+    int i, request, value, index;
 
     for (i = 0; i < 8; i++) {
         s->setup_buf[i] = p->parameter >> (i*8);
@@ -249,27 +247,27 @@ static int do_parameter(USBDevice *s, USBPacket *p)
         fprintf(stderr,
                 "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
                 s->setup_len, sizeof(s->data_buf));
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        return;
     }
 
     if (p->pid == USB_TOKEN_OUT) {
         usb_packet_copy(p, s->data_buf, s->setup_len);
     }
 
-    ret = usb_device_handle_control(s, p, request, value, index,
-                                    s->setup_len, s->data_buf);
-    if (ret < 0) {
-        return ret;
+    usb_device_handle_control(s, p, request, value, index,
+                              s->setup_len, s->data_buf);
+    if (p->status == USB_RET_ASYNC) {
+        return;
     }
 
-    if (ret < s->setup_len) {
-        s->setup_len = ret;
+    if (p->actual_length < s->setup_len) {
+        s->setup_len = p->actual_length;
     }
     if (p->pid == USB_TOKEN_IN) {
+        p->actual_length = 0;
         usb_packet_copy(p, s->data_buf, s->setup_len);
     }
-
-    return ret;
 }
 
 /* ctrl complete function for devices which use usb_generic_handle_packet and
@@ -278,30 +276,30 @@ static int do_parameter(USBDevice *s, USBPacket *p)
    usb_packet_complete to complete their async control packets. */
 void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
 {
-    if (p->result < 0) {
+    if (p->status < 0) {
         s->setup_state = SETUP_STATE_IDLE;
     }
 
     switch (s->setup_state) {
     case SETUP_STATE_SETUP:
-        if (p->result < s->setup_len) {
-            s->setup_len = p->result;
+        if (p->actual_length < s->setup_len) {
+            s->setup_len = p->actual_length;
         }
         s->setup_state = SETUP_STATE_DATA;
-        p->result = 8;
+        p->actual_length = 8;
         break;
 
     case SETUP_STATE_ACK:
         s->setup_state = SETUP_STATE_IDLE;
-        p->result = 0;
+        p->actual_length = 0;
         break;
 
     case SETUP_STATE_PARAM:
-        if (p->result < s->setup_len) {
-            s->setup_len = p->result;
+        if (p->actual_length < s->setup_len) {
+            s->setup_len = p->actual_length;
         }
         if (p->pid == USB_TOKEN_IN) {
-            p->result = 0;
+            p->actual_length = 0;
             usb_packet_copy(p, s->data_buf, s->setup_len);
         }
         break;
@@ -342,40 +340,57 @@ USBDevice *usb_find_device(USBPort *port, uint8_t addr)
     return usb_device_find_device(dev, addr);
 }
 
-static int usb_process_one(USBPacket *p)
+static void usb_process_one(USBPacket *p)
 {
     USBDevice *dev = p->ep->dev;
 
+    /*
+     * Handlers expect status to be initialized to USB_RET_SUCCESS, but it
+     * can be USB_RET_NAK here from a previous usb_process_one() call,
+     * or USB_RET_ASYNC from going through usb_queue_one().
+     */
+    p->status = USB_RET_SUCCESS;
+
     if (p->ep->nr == 0) {
         /* control pipe */
         if (p->parameter) {
-            return do_parameter(dev, p);
+            do_parameter(dev, p);
+            return;
         }
         switch (p->pid) {
         case USB_TOKEN_SETUP:
-            return do_token_setup(dev, p);
+            do_token_setup(dev, p);
+            break;
         case USB_TOKEN_IN:
-            return do_token_in(dev, p);
+            do_token_in(dev, p);
+            break;
         case USB_TOKEN_OUT:
-            return do_token_out(dev, p);
+            do_token_out(dev, p);
+            break;
         default:
-            return USB_RET_STALL;
+            p->status = USB_RET_STALL;
         }
     } else {
         /* data pipe */
-        return usb_device_handle_data(dev, p);
+        usb_device_handle_data(dev, p);
     }
 }
 
-/* Hand over a packet to a device for processing.  Return value
+static void usb_queue_one(USBPacket *p)
+{
+    usb_packet_set_state(p, USB_PACKET_QUEUED);
+    QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
+    p->status = USB_RET_ASYNC;
+}
+
+/* Hand over a packet to a device for processing.  p->status ==
    USB_RET_ASYNC indicates the processing isn't finished yet, the
    driver will call usb_packet_complete() when done processing it. */
-int usb_handle_packet(USBDevice *dev, USBPacket *p)
+void usb_handle_packet(USBDevice *dev, USBPacket *p)
 {
-    int ret;
-
     if (dev == NULL) {
-        return USB_RET_NODEV;
+        p->status = USB_RET_NODEV;
+        return;
     }
     assert(dev == p->ep->dev);
     assert(dev->state == USB_STATE_DEFAULT);
@@ -389,32 +404,26 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
     }
 
     if (QTAILQ_EMPTY(&p->ep->queue) || p->ep->pipeline) {
-        ret = usb_process_one(p);
-        if (ret == USB_RET_ASYNC) {
+        usb_process_one(p);
+        if (p->status == USB_RET_ASYNC) {
             assert(p->ep->type != USB_ENDPOINT_XFER_ISOC);
             usb_packet_set_state(p, USB_PACKET_ASYNC);
             QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
-        } else if (ret == USB_RET_ADD_TO_QUEUE) {
-            usb_packet_set_state(p, USB_PACKET_QUEUED);
-            QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
-            ret = USB_RET_ASYNC;
+        } else if (p->status == USB_RET_ADD_TO_QUEUE) {
+            usb_queue_one(p);
         } else {
             /*
              * When pipelining is enabled usb-devices must always return async,
              * otherwise packets can complete out of order!
              */
             assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue));
-            if (ret != USB_RET_NAK) {
-                p->result = ret;
+            if (p->status != USB_RET_NAK) {
                 usb_packet_set_state(p, USB_PACKET_COMPLETE);
             }
         }
     } else {
-        ret = USB_RET_ASYNC;
-        usb_packet_set_state(p, USB_PACKET_QUEUED);
-        QTAILQ_INSERT_TAIL(&p->ep->queue, p, queue);
+        usb_queue_one(p);
     }
-    return ret;
 }
 
 void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
@@ -422,9 +431,10 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
     USBEndpoint *ep = p->ep;
 
     assert(QTAILQ_FIRST(&ep->queue) == p);
-    assert(p->result != USB_RET_ASYNC && p->result != USB_RET_NAK);
+    assert(p->status != USB_RET_ASYNC && p->status != USB_RET_NAK);
 
-    if (p->result < 0 || (p->short_not_ok && (p->result < p->iov.size))) {
+    if (p->status != USB_RET_SUCCESS ||
+            (p->short_not_ok && (p->actual_length < p->iov.size))) {
         ep->halted = true;
     }
     usb_packet_set_state(p, USB_PACKET_COMPLETE);
@@ -438,7 +448,6 @@ void usb_packet_complete_one(USBDevice *dev, USBPacket *p)
 void usb_packet_complete(USBDevice *dev, USBPacket *p)
 {
     USBEndpoint *ep = p->ep;
-    int ret;
 
     usb_packet_check_state(p, USB_PACKET_ASYNC);
     usb_packet_complete_one(dev, p);
@@ -447,7 +456,7 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
         p = QTAILQ_FIRST(&ep->queue);
         if (ep->halted) {
             /* Empty the queue on a halt */
-            p->result = USB_RET_REMOVE_FROM_QUEUE;
+            p->status = USB_RET_REMOVE_FROM_QUEUE;
             dev->port->ops->complete(dev->port, p);
             continue;
         }
@@ -455,12 +464,11 @@ void usb_packet_complete(USBDevice *dev, USBPacket *p)
             break;
         }
         usb_packet_check_state(p, USB_PACKET_QUEUED);
-        ret = usb_process_one(p);
-        if (ret == USB_RET_ASYNC) {
+        usb_process_one(p);
+        if (p->status == USB_RET_ASYNC) {
             usb_packet_set_state(p, USB_PACKET_ASYNC);
             break;
         }
-        p->result = ret;
         usb_packet_complete_one(ep->dev, p);
     }
 }
@@ -541,7 +549,8 @@ void usb_packet_setup(USBPacket *p, int pid, USBEndpoint *ep, uint64_t id,
     p->id = id;
     p->pid = pid;
     p->ep = ep;
-    p->result = 0;
+    p->status = USB_RET_SUCCESS;
+    p->actual_length = 0;
     p->parameter = 0;
     p->short_not_ok = short_not_ok;
     p->int_req = int_req;
@@ -557,31 +566,31 @@ void usb_packet_addbuf(USBPacket *p, void *ptr, size_t len)
 
 void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
 {
-    assert(p->result >= 0);
-    assert(p->result + bytes <= p->iov.size);
+    assert(p->actual_length >= 0);
+    assert(p->actual_length + bytes <= p->iov.size);
     switch (p->pid) {
     case USB_TOKEN_SETUP:
     case USB_TOKEN_OUT:
-        iov_to_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes);
+        iov_to_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
         break;
     case USB_TOKEN_IN:
-        iov_from_buf(p->iov.iov, p->iov.niov, p->result, ptr, bytes);
+        iov_from_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
         break;
     default:
         fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
         abort();
     }
-    p->result += bytes;
+    p->actual_length += bytes;
 }
 
 void usb_packet_skip(USBPacket *p, size_t bytes)
 {
-    assert(p->result >= 0);
-    assert(p->result + bytes <= p->iov.size);
+    assert(p->actual_length >= 0);
+    assert(p->actual_length + bytes <= p->iov.size);
     if (p->pid == USB_TOKEN_IN) {
-        iov_memset(p->iov.iov, p->iov.niov, p->result, 0, bytes);
+        iov_memset(p->iov.iov, p->iov.niov, p->actual_length, 0, bytes);
     }
-    p->result += bytes;
+    p->actual_length += bytes;
 }
 
 void usb_packet_cleanup(USBPacket *p)
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index 1f12eae..b7c3233 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -626,7 +626,8 @@ int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
     return pos;
 }
 
-int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len)
+int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
+                            int value, uint8_t *dest, size_t len)
 {
     const USBDesc *desc = usb_device_get_usb_desc(dev);
     const USBDescDevice *other_dev;
@@ -696,6 +697,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
             ret = len;
         }
         memcpy(dest, buf, ret);
+        p->actual_length = ret;
+        ret = 0;
     }
     return ret;
 }
@@ -715,7 +718,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         break;
 
     case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
-        ret = usb_desc_get_descriptor(dev, value, data, length);
+        ret = usb_desc_get_descriptor(dev, p, value, data, length);
         break;
 
     case DeviceRequest | USB_REQ_GET_CONFIGURATION:
@@ -724,7 +727,8 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
          * the non zero value of bConfigurationValue.
          */
         data[0] = dev->config ? dev->config->bConfigurationValue : 0;
-        ret = 1;
+        p->actual_length = 1;
+        ret = 0;
         break;
     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
         ret = usb_desc_set_config(dev, value);
@@ -749,7 +753,8 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
             data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
         }
         data[1] = 0x00;
-        ret = 2;
+        p->actual_length = 2;
+        ret = 0;
         break;
     }
     case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -772,7 +777,8 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
             break;
         }
         data[0] = dev->altsetting[index];
-        ret = 1;
+        p->actual_length = 1;
+        ret = 0;
         break;
     case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
         ret = usb_desc_set_interface(dev, index, value);
diff --git a/hw/usb/desc.h b/hw/usb/desc.h
index 68bb570..ddd3e74 100644
--- a/hw/usb/desc.h
+++ b/hw/usb/desc.h
@@ -216,7 +216,8 @@ void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
 void usb_desc_create_serial(USBDevice *dev);
 const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
 int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
-int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
+int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
+        int value, uint8_t *dest, size_t len);
 int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
         int request, int value, int index, int length, uint8_t *data);
 
diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c
index 2594c78..b669601 100644
--- a/hw/usb/dev-audio.c
+++ b/hw/usb/dev-audio.c
@@ -503,7 +503,7 @@ static int usb_audio_set_control(USBAudioState *s, uint8_t attrib,
     return ret;
 }
 
-static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_audio_handle_control(USBDevice *dev, USBPacket *p,
                                     int request, int value, int index,
                                     int length, uint8_t *data)
 {
@@ -518,7 +518,7 @@ static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
     switch (request) {
@@ -534,6 +534,7 @@ static int usb_audio_handle_control(USBDevice *dev, USBPacket *p,
             }
             goto fail;
         }
+        p->actual_length = ret;
         break;
 
     case ClassInterfaceOutRequest | CR_SET_CUR:
@@ -557,10 +558,9 @@ fail:
                     "request 0x%04x value 0x%04x index 0x%04x length 0x%04x\n",
                     request, value, index, length);
         }
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
 static void usb_audio_set_interface(USBDevice *dev, int iface,
@@ -583,50 +583,35 @@ static void usb_audio_handle_reset(USBDevice *dev)
     usb_audio_set_output_altset(s, ALTSET_OFF);
 }
 
-static int usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
+static void usb_audio_handle_dataout(USBAudioState *s, USBPacket *p)
 {
-    int rc;
-
     if (s->out.altset == ALTSET_OFF) {
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        return;
     }
 
-    rc = streambuf_put(&s->out.buf, p);
-    if (rc < p->iov.size && s->debug > 1) {
+    streambuf_put(&s->out.buf, p);
+    if (p->actual_length < p->iov.size && s->debug > 1) {
         fprintf(stderr, "usb-audio: output overrun (%zd bytes)\n",
-                p->iov.size - rc);
+                p->iov.size - p->actual_length);
     }
-
-    return 0;
 }
 
-static int usb_audio_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_audio_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBAudioState *s = (USBAudioState *) dev;
-    int ret = 0;
 
-    switch (p->pid) {
-    case USB_TOKEN_OUT:
-        switch (p->ep->nr) {
-        case 1:
-            ret = usb_audio_handle_dataout(s, p);
-            break;
-        default:
-            goto fail;
-        }
-        break;
-
-    default:
-fail:
-        ret = USB_RET_STALL;
-        break;
+    if (p->pid == USB_TOKEN_OUT && p->ep->nr == 1) {
+        usb_audio_handle_dataout(s, p);
+        return;
     }
-    if (ret == USB_RET_STALL && s->debug) {
+
+    p->status = USB_RET_STALL;
+    if (s->debug) {
         fprintf(stderr, "usb-audio: failed data transaction: "
                         "pid 0x%x ep 0x%x len 0x%zx\n",
                         p->pid, p->ep->nr, p->iov.size);
     }
-    return ret;
 }
 
 static void usb_audio_handle_destroy(USBDevice *dev)
diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c
index 55bc191..bfb96bf 100644
--- a/hw/usb/dev-bluetooth.c
+++ b/hw/usb/dev-bluetooth.c
@@ -285,13 +285,15 @@ static void usb_bt_fifo_enqueue(struct usb_hci_in_fifo_s *fifo,
     fifo->fifo[off].len = len;
 }
 
-static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
+static inline void usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
                 USBPacket *p)
 {
     int len;
 
-    if (likely(!fifo->len))
-        return USB_RET_STALL;
+    if (likely(!fifo->len)) {
+        p->status = USB_RET_STALL;
+        return;
+    }
 
     len = MIN(p->iov.size, fifo->fifo[fifo->start].len);
     usb_packet_copy(p, fifo->fifo[fifo->start].data, len);
@@ -310,8 +312,6 @@ static inline int usb_bt_fifo_dequeue(struct usb_hci_in_fifo_s *fifo,
         fifo->dstart = 0;
         fifo->dsize = DFIFO_LEN_MASK + 1;
     }
-
-    return len;
 }
 
 static inline void usb_bt_fifo_out_enqueue(struct USBBtState *s,
@@ -363,7 +363,7 @@ static void usb_bt_handle_reset(USBDevice *dev)
     s->outsco.len = 0;
 }
 
-static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_bt_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     struct USBBtState *s = (struct USBBtState *) dev->opaque;
@@ -382,16 +382,15 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
             usb_bt_fifo_reset(&s->sco);
             break;
         }
-        return ret;
+        return;
     }
 
-    ret = 0;
     switch (request) {
     case InterfaceRequest | USB_REQ_GET_STATUS:
     case EndpointRequest | USB_REQ_GET_STATUS:
         data[0] = 0x00;
         data[1] = 0x00;
-        ret = 2;
+        p->actual_length = 2;
         break;
     case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -407,16 +406,14 @@ static int usb_bt_handle_control(USBDevice *dev, USBPacket *p,
         break;
     default:
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
-static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_bt_handle_data(USBDevice *dev, USBPacket *p)
 {
     struct USBBtState *s = (struct USBBtState *) dev->opaque;
-    int ret = 0;
 
     if (!s->config)
         goto fail;
@@ -425,15 +422,15 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
     case USB_TOKEN_IN:
         switch (p->ep->nr) {
         case USB_EVT_EP:
-            ret = usb_bt_fifo_dequeue(&s->evt, p);
+            usb_bt_fifo_dequeue(&s->evt, p);
             break;
 
         case USB_ACL_EP:
-            ret = usb_bt_fifo_dequeue(&s->acl, p);
+            usb_bt_fifo_dequeue(&s->acl, p);
             break;
 
         case USB_SCO_EP:
-            ret = usb_bt_fifo_dequeue(&s->sco, p);
+            usb_bt_fifo_dequeue(&s->sco, p);
             break;
 
         default:
@@ -460,11 +457,9 @@ static int usb_bt_handle_data(USBDevice *dev, USBPacket *p)
 
     default:
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-
-    return ret;
 }
 
 static void usb_bt_out_hci_packet_event(void *opaque,
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index b3dcd23..55266b1 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -371,7 +371,7 @@ static void usb_hid_handle_reset(USBDevice *dev)
     hid_reset(&us->hid);
 }
 
-static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_hid_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
@@ -380,10 +380,9 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
-    ret = 0;
     switch (request) {
         /* hid specific requests */
     case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
@@ -392,15 +391,15 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
             if (hs->kind == HID_MOUSE) {
 		memcpy(data, qemu_mouse_hid_report_descriptor,
 		       sizeof(qemu_mouse_hid_report_descriptor));
-		ret = sizeof(qemu_mouse_hid_report_descriptor);
+                p->actual_length = sizeof(qemu_mouse_hid_report_descriptor);
             } else if (hs->kind == HID_TABLET) {
                 memcpy(data, qemu_tablet_hid_report_descriptor,
 		       sizeof(qemu_tablet_hid_report_descriptor));
-		ret = sizeof(qemu_tablet_hid_report_descriptor);
+                p->actual_length = sizeof(qemu_tablet_hid_report_descriptor);
             } else if (hs->kind == HID_KEYBOARD) {
                 memcpy(data, qemu_keyboard_hid_report_descriptor,
                        sizeof(qemu_keyboard_hid_report_descriptor));
-                ret = sizeof(qemu_keyboard_hid_report_descriptor);
+                p->actual_length = sizeof(qemu_keyboard_hid_report_descriptor);
             }
             break;
         default:
@@ -409,14 +408,14 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
         break;
     case GET_REPORT:
         if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
-            ret = hid_pointer_poll(hs, data, length);
+            p->actual_length = hid_pointer_poll(hs, data, length);
         } else if (hs->kind == HID_KEYBOARD) {
-            ret = hid_keyboard_poll(hs, data, length);
+            p->actual_length = hid_keyboard_poll(hs, data, length);
         }
         break;
     case SET_REPORT:
         if (hs->kind == HID_KEYBOARD) {
-            ret = hid_keyboard_write(hs, data, length);
+            p->actual_length = hid_keyboard_write(hs, data, length);
         } else {
             goto fail;
         }
@@ -425,19 +424,18 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
         if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
             goto fail;
         }
-        ret = 1;
         data[0] = hs->protocol;
+        p->actual_length = 1;
         break;
     case SET_PROTOCOL:
         if (hs->kind != HID_KEYBOARD && hs->kind != HID_MOUSE) {
             goto fail;
         }
-        ret = 0;
         hs->protocol = value;
         break;
     case GET_IDLE:
-        ret = 1;
         data[0] = hs->idle;
+        p->actual_length = 1;
         break;
     case SET_IDLE:
         hs->idle = (uint8_t) (value >> 8);
@@ -445,22 +443,20 @@ static int usb_hid_handle_control(USBDevice *dev, USBPacket *p,
         if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
             hid_pointer_activate(hs);
         }
-        ret = 0;
         break;
     default:
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
-static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_hid_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBHIDState *us = DO_UPCAST(USBHIDState, dev, dev);
     HIDState *hs = &us->hid;
     uint8_t buf[p->iov.size];
-    int ret = 0;
+    int len = 0;
 
     switch (p->pid) {
     case USB_TOKEN_IN:
@@ -471,15 +467,16 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
             }
             if (!hid_has_events(hs) &&
                 (!hs->idle || hs->next_idle_clock - curtime > 0)) {
-                return USB_RET_NAK;
+                p->status = USB_RET_NAK;
+                return;
             }
             hid_set_next_idle(hs, curtime);
             if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
-                ret = hid_pointer_poll(hs, buf, p->iov.size);
+                len = hid_pointer_poll(hs, buf, p->iov.size);
             } else if (hs->kind == HID_KEYBOARD) {
-                ret = hid_keyboard_poll(hs, buf, p->iov.size);
+                len = hid_keyboard_poll(hs, buf, p->iov.size);
             }
-            usb_packet_copy(p, buf, ret);
+            usb_packet_copy(p, buf, len);
         } else {
             goto fail;
         }
@@ -487,10 +484,9 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
     case USB_TOKEN_OUT:
     default:
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
 static void usb_hid_handle_destroy(USBDevice *dev)
diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c
index 8fd30df..9ee60dd 100644
--- a/hw/usb/dev-hub.c
+++ b/hw/usb/dev-hub.c
@@ -288,7 +288,7 @@ static const char *feature_name(int feature)
     return name[feature] ?: "?";
 }
 
-static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_hub_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     USBHubState *s = (USBHubState *)dev;
@@ -298,7 +298,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
     switch(request) {
@@ -306,7 +306,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
         if (value == 0 && index != 0x81) { /* clear ep halt */
             goto fail;
         }
-        ret = 0;
         break;
         /* usb specific requests */
     case GetHubStatus:
@@ -314,7 +313,7 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
         data[1] = 0;
         data[2] = 0;
         data[3] = 0;
-        ret = 4;
+        p->actual_length = 4;
         break;
     case GetPortStatus:
         {
@@ -331,16 +330,14 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
             data[1] = port->wPortStatus >> 8;
             data[2] = port->wPortChange;
             data[3] = port->wPortChange >> 8;
-            ret = 4;
+            p->actual_length = 4;
         }
         break;
     case SetHubFeature:
     case ClearHubFeature:
-        if (value == 0 || value == 1) {
-        } else {
+        if (value != 0 && value != 1) {
             goto fail;
         }
-        ret = 0;
         break;
     case SetPortFeature:
         {
@@ -373,7 +370,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
             default:
                 goto fail;
             }
-            ret = 0;
         }
         break;
     case ClearPortFeature:
@@ -413,7 +409,6 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
             default:
                 goto fail;
             }
-            ret = 0;
         }
         break;
     case GetHubDescriptor:
@@ -437,22 +432,20 @@ static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
                 var_hub_size++;
             }
 
-            ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
-            data[0] = ret;
+            p->actual_length = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
+            data[0] = p->actual_length;
             break;
         }
     default:
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
-static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_hub_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBHubState *s = (USBHubState *)dev;
-    int ret;
 
     switch(p->pid) {
     case USB_TOKEN_IN:
@@ -465,7 +458,8 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
             if (p->iov.size == 1) { /* FreeBSD workaround */
                 n = 1;
             } else if (n > p->iov.size) {
-                return USB_RET_BABBLE;
+                p->status = USB_RET_BABBLE;
+                return;
             }
             status = 0;
             for(i = 0; i < NUM_PORTS; i++) {
@@ -478,9 +472,8 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
                     buf[i] = status >> (8 * i);
                 }
                 usb_packet_copy(p, buf, n);
-                ret = n;
             } else {
-                ret = USB_RET_NAK; /* usb11 11.13.1 */
+                p->status = USB_RET_NAK; /* usb11 11.13.1 */
             }
         } else {
             goto fail;
@@ -489,10 +482,9 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
     case USB_TOKEN_OUT:
     default:
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
 static void usb_hub_handle_destroy(USBDevice *dev)
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index e4a4359..14d9e5a 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -1048,7 +1048,7 @@ static void usb_net_handle_reset(USBDevice *dev)
 {
 }
 
-static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_net_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     USBNetState *s = (USBNetState *) dev;
@@ -1056,10 +1056,9 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
-    ret = 0;
     switch(request) {
     case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
         if (!is_rndis(s) || value || index != 0) {
@@ -1078,22 +1077,25 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
         }
 #endif
         ret = rndis_parse(s, data, length);
+        if (ret < 0) {
+            p->status = ret;
+        }
         break;
 
     case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
         if (!is_rndis(s) || value || index != 0) {
             goto fail;
         }
-        ret = rndis_get_response(s, data);
-        if (!ret) {
+        p->actual_length = rndis_get_response(s, data);
+        if (p->actual_length == 0) {
             data[0] = 0;
-            ret = 1;
+            p->actual_length = 1;
         }
 #ifdef TRAFFIC_DEBUG
         {
             unsigned int i;
             fprintf(stderr, "GET_ENCAPSULATED_RESPONSE:");
-            for (i = 0; i < ret; i++) {
+            for (i = 0; i < p->actual_length; i++) {
                 if (!(i & 15))
                     fprintf(stderr, "\n%04x:", i);
                 fprintf(stderr, " %02x", data[i]);
@@ -1108,72 +1110,67 @@ static int usb_net_handle_control(USBDevice *dev, USBPacket *p,
         fprintf(stderr, "usbnet: failed control transaction: "
                         "request 0x%x value 0x%x index 0x%x length 0x%x\n",
                         request, value, index, length);
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
-static int usb_net_handle_statusin(USBNetState *s, USBPacket *p)
+static void usb_net_handle_statusin(USBNetState *s, USBPacket *p)
 {
     le32 buf[2];
-    int ret = 8;
 
     if (p->iov.size < 8) {
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        return;
     }
 
     buf[0] = cpu_to_le32(1);
     buf[1] = cpu_to_le32(0);
     usb_packet_copy(p, buf, 8);
-    if (!s->rndis_resp.tqh_first)
-        ret = USB_RET_NAK;
+    if (!s->rndis_resp.tqh_first) {
+        p->status = USB_RET_NAK;
+    }
 
 #ifdef TRAFFIC_DEBUG
     fprintf(stderr, "usbnet: interrupt poll len %zu return %d",
-            p->iov.size, ret);
-    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
+            p->iov.size, p->status);
+    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->status);
 #endif
-
-    return ret;
 }
 
-static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
+static void usb_net_handle_datain(USBNetState *s, USBPacket *p)
 {
-    int ret = USB_RET_NAK;
+    int len;
 
     if (s->in_ptr > s->in_len) {
         usb_net_reset_in_buf(s);
-        ret = USB_RET_NAK;
-        return ret;
+        p->status = USB_RET_NAK;
+        return;
     }
     if (!s->in_len) {
-        ret = USB_RET_NAK;
-        return ret;
+        p->status = USB_RET_NAK;
+        return;
     }
-    ret = s->in_len - s->in_ptr;
-    if (ret > p->iov.size) {
-        ret = p->iov.size;
+    len = s->in_len - s->in_ptr;
+    if (len > p->iov.size) {
+        len = p->iov.size;
     }
-    usb_packet_copy(p, &s->in_buf[s->in_ptr], ret);
-    s->in_ptr += ret;
+    usb_packet_copy(p, &s->in_buf[s->in_ptr], len);
+    s->in_ptr += len;
     if (s->in_ptr >= s->in_len &&
-                    (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
+                    (is_rndis(s) || (s->in_len & (64 - 1)) || !len)) {
         /* no short packet necessary */
         usb_net_reset_in_buf(s);
     }
 
 #ifdef TRAFFIC_DEBUG
-    fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, ret);
-    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", ret);
+    fprintf(stderr, "usbnet: data in len %zu return %d", p->iov.size, len);
+    iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", len);
 #endif
-
-    return ret;
 }
 
-static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
+static void usb_net_handle_dataout(USBNetState *s, USBPacket *p)
 {
-    int ret = p->iov.size;
     int sz = sizeof(s->out_buf) - s->out_ptr;
     struct rndis_packet_msg_type *msg =
             (struct rndis_packet_msg_type *) s->out_buf;
@@ -1184,21 +1181,23 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
     iov_hexdump(p->iov.iov, p->iov.niov, stderr, "usbnet", p->iov.size);
 #endif
 
-    if (sz > ret)
-        sz = ret;
+    if (sz > p->iov.size) {
+        sz = p->iov.size;
+    }
     usb_packet_copy(p, &s->out_buf[s->out_ptr], sz);
     s->out_ptr += sz;
 
     if (!is_rndis(s)) {
-        if (ret < 64) {
+        if (p->iov.size < 64) {
             qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
             s->out_ptr = 0;
         }
-        return ret;
+        return;
     }
     len = le32_to_cpu(msg->MessageLength);
-    if (s->out_ptr < 8 || s->out_ptr < len)
-        return ret;
+    if (s->out_ptr < 8 || s->out_ptr < len) {
+        return;
+    }
     if (le32_to_cpu(msg->MessageType) == RNDIS_PACKET_MSG) {
         uint32_t offs = 8 + le32_to_cpu(msg->DataOffset);
         uint32_t size = le32_to_cpu(msg->DataLength);
@@ -1207,24 +1206,21 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
     }
     s->out_ptr -= len;
     memmove(s->out_buf, &s->out_buf[len], s->out_ptr);
-
-    return ret;
 }
 
-static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_net_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBNetState *s = (USBNetState *) dev;
-    int ret = 0;
 
     switch(p->pid) {
     case USB_TOKEN_IN:
         switch (p->ep->nr) {
         case 1:
-            ret = usb_net_handle_statusin(s, p);
+            usb_net_handle_statusin(s, p);
             break;
 
         case 2:
-            ret = usb_net_handle_datain(s, p);
+            usb_net_handle_datain(s, p);
             break;
 
         default:
@@ -1235,7 +1231,7 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
     case USB_TOKEN_OUT:
         switch (p->ep->nr) {
         case 2:
-            ret = usb_net_handle_dataout(s, p);
+            usb_net_handle_dataout(s, p);
             break;
 
         default:
@@ -1245,14 +1241,15 @@ static int usb_net_handle_data(USBDevice *dev, USBPacket *p)
 
     default:
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    if (ret == USB_RET_STALL)
+
+    if (p->status == USB_RET_STALL) {
         fprintf(stderr, "usbnet: failed data transaction: "
                         "pid 0x%x ep 0x%x len 0x%zx\n",
                         p->pid, p->ep->nr, p->iov.size);
-    return ret;
+    }
 }
 
 static ssize_t usbnet_receive(NetClientState *nc, const uint8_t *buf, size_t size)
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index a466f99..99b19df 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -219,7 +219,7 @@ static uint8_t usb_get_modem_lines(USBSerialState *s)
     return ret;
 }
 
-static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_serial_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     USBSerialState *s = (USBSerialState *)dev;
@@ -228,13 +228,11 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
     DPRINTF("got control %x, value %x\n",request, value);
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
-    ret = 0;
     switch (request) {
     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
-        ret = 0;
         break;
 
         /* Class specific requests.  */
@@ -323,7 +321,7 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
     case DeviceInVendor | FTDI_GET_MDM_ST:
         data[0] = usb_get_modem_lines(s) | 1;
         data[1] = 0;
-        ret = 2;
+        p->actual_length = 2;
         break;
     case DeviceOutVendor | FTDI_SET_EVENT_CHR:
         /* TODO: handle it */
@@ -338,25 +336,23 @@ static int usb_serial_handle_control(USBDevice *dev, USBPacket *p,
         break;
     case DeviceInVendor | FTDI_GET_LATENCY:
         data[0] = s->latency;
-        ret = 1;
+        p->actual_length = 1;
         break;
     default:
     fail:
         DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
-static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBSerialState *s = (USBSerialState *)dev;
-    int i, ret = 0;
     uint8_t devep = p->ep->nr;
     struct iovec *iov;
     uint8_t header[2];
-    int first_len, len;
+    int i, first_len, len;
 
     switch (p->pid) {
     case USB_TOKEN_OUT:
@@ -366,6 +362,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
             iov = p->iov.iov + i;
             qemu_chr_fe_write(s->cs, iov->iov_base, iov->iov_len);
         }
+        p->actual_length = p->iov.size;
         break;
 
     case USB_TOKEN_IN:
@@ -374,7 +371,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
         first_len = RECV_BUF - s->recv_ptr;
         len = p->iov.size;
         if (len <= 2) {
-            ret = USB_RET_NAK;
+            p->status = USB_RET_NAK;
             break;
         }
         header[0] = usb_get_modem_lines(s) | 1;
@@ -384,7 +381,6 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
             s->event_trigger &= ~FTDI_BI;
             header[1] = FTDI_BI;
             usb_packet_copy(p, header, 2);
-            ret = 2;
             break;
         } else {
             header[1] = 0;
@@ -393,7 +389,7 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
         if (len > s->recv_used)
             len = s->recv_used;
         if (!len) {
-            ret = USB_RET_NAK;
+            p->status = USB_RET_NAK;
             break;
         }
         if (first_len > len)
@@ -404,17 +400,14 @@ static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
             usb_packet_copy(p, s->recv_buf, len - first_len);
         s->recv_used -= len;
         s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
-        ret = len + 2;
         break;
 
     default:
         DPRINTF("Bad token\n");
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-
-    return ret;
 }
 
 static void usb_serial_handle_destroy(USBDevice *dev)
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 1ea0791..190fcd6 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -635,39 +635,38 @@ static void ccid_handle_reset(USBDevice *dev)
     ccid_reset(s);
 }
 
-static int ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
+static void ccid_handle_control(USBDevice *dev, USBPacket *p, int request,
                                int value, int index, int length, uint8_t *data)
 {
     USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
-    int ret = 0;
+    int ret;
 
     DPRINTF(s, 1, "got control %x, value %x\n", request, value);
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
     switch (request) {
         /* Class specific requests.  */
     case InterfaceOutClass | CCID_CONTROL_ABORT:
         DPRINTF(s, 1, "ccid_control abort UNIMPLEMENTED\n");
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
         DPRINTF(s, 1, "ccid_control get clock frequencies UNIMPLEMENTED\n");
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES:
         DPRINTF(s, 1, "ccid_control get data rates UNIMPLEMENTED\n");
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     default:
         DPRINTF(s, 1, "got unsupported/bogus control %x, value %x\n",
                 request, value);
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
 static bool ccid_card_inserted(USBCCIDState *s)
@@ -870,18 +869,13 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
     }
 }
 
-/*
- * Handle a single USB_TOKEN_OUT, return value returned to guest.
- * Return value:
- *  0             - all ok
- *  USB_RET_STALL - failed to handle packet
- */
-static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
+static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
 {
     CCID_Header *ccid_header;
 
     if (p->iov.size + s->bulk_out_pos > BULK_OUT_DATA_SIZE) {
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        return;
     }
     ccid_header = (CCID_Header *)s->bulk_out_data;
     usb_packet_copy(p, s->bulk_out_data + s->bulk_out_pos, p->iov.size);
@@ -890,7 +884,7 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
         DPRINTF(s, D_VERBOSE,
             "usb-ccid: bulk_in: expecting more packets (%zd/%d)\n",
             p->iov.size, ccid_header->dwLength);
-        return 0;
+        return;
     }
     if (s->bulk_out_pos < 10) {
         DPRINTF(s, 1,
@@ -949,60 +943,52 @@ static int ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
         }
     }
     s->bulk_out_pos = 0;
-    return 0;
 }
 
-static int ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
+static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
 {
-    int ret = 0;
+    int len = 0;
 
-    assert(p->iov.size > 0);
     ccid_bulk_in_get(s);
     if (s->current_bulk_in != NULL) {
-        ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
+        len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos,
                   p->iov.size);
         usb_packet_copy(p, s->current_bulk_in->data +
-                        s->current_bulk_in->pos, ret);
-        s->current_bulk_in->pos += ret;
+                        s->current_bulk_in->pos, len);
+        s->current_bulk_in->pos += len;
         if (s->current_bulk_in->pos == s->current_bulk_in->len) {
             ccid_bulk_in_release(s);
         }
     } else {
         /* return when device has no data - usb 2.0 spec Table 8-4 */
-        ret = USB_RET_NAK;
+        p->status = USB_RET_NAK;
     }
-    if (ret > 0) {
+    if (len) {
         DPRINTF(s, D_MORE_INFO,
                 "%s: %zd/%d req/act to guest (BULK_IN)\n",
-                __func__, p->iov.size, ret);
+                __func__, p->iov.size, len);
     }
-    if (ret != USB_RET_NAK && ret < p->iov.size) {
+    if (len < p->iov.size) {
         DPRINTF(s, 1,
                 "%s: returning short (EREMOTEIO) %d < %zd\n",
-                __func__, ret, p->iov.size);
+                __func__, len, p->iov.size);
     }
-    return ret;
 }
 
-static int ccid_handle_data(USBDevice *dev, USBPacket *p)
+static void ccid_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
-    int ret = 0;
     uint8_t buf[2];
 
     switch (p->pid) {
     case USB_TOKEN_OUT:
-        ret = ccid_handle_bulk_out(s, p);
+        ccid_handle_bulk_out(s, p);
         break;
 
     case USB_TOKEN_IN:
         switch (p->ep->nr) {
         case CCID_BULK_IN_EP:
-            if (!p->iov.size) {
-                ret = USB_RET_NAK;
-            } else {
-                ret = ccid_bulk_in_copy_to_guest(s, p);
-            }
+            ccid_bulk_in_copy_to_guest(s, p);
             break;
         case CCID_INT_IN_EP:
             if (s->notify_slot_change) {
@@ -1010,7 +996,6 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
                 buf[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
                 buf[1] = s->bmSlotICCState;
                 usb_packet_copy(p, buf, 2);
-                ret = 2;
                 s->notify_slot_change = false;
                 s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
                 DPRINTF(s, D_INFO,
@@ -1021,17 +1006,15 @@ static int ccid_handle_data(USBDevice *dev, USBPacket *p)
             break;
         default:
             DPRINTF(s, 1, "Bad endpoint\n");
-            ret = USB_RET_STALL;
+            p->status = USB_RET_STALL;
             break;
         }
         break;
     default:
         DPRINTF(s, 1, "Bad token\n");
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-
-    return ret;
 }
 
 static void ccid_handle_destroy(USBDevice *dev)
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index e732191..50af971 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -215,7 +215,7 @@ static const USBDesc desc = {
 static void usb_msd_copy_data(MSDState *s, USBPacket *p)
 {
     uint32_t len;
-    len = p->iov.size - p->result;
+    len = p->iov.size - p->actual_length;
     if (len > s->scsi_len)
         len = s->scsi_len;
     usb_packet_copy(p, scsi_req_get_buf(s->req) + s->scsi_off, len);
@@ -263,7 +263,8 @@ static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
     if (p) {
         usb_msd_copy_data(s, p);
         p = s->packet;
-        if (p && p->result == p->iov.size) {
+        if (p && p->actual_length == p->iov.size) {
+            p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
             usb_msd_packet_complete(s);
         }
     }
@@ -292,7 +293,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
             s->mode = USB_MSDM_CBW;
         } else {
             if (s->data_len) {
-                int len = (p->iov.size - p->result);
+                int len = (p->iov.size - p->actual_length);
                 usb_packet_skip(p, len);
                 s->data_len -= len;
             }
@@ -300,6 +301,7 @@ static void usb_msd_command_complete(SCSIRequest *req, uint32_t status, size_t r
                 s->mode = USB_MSDM_CSW;
             }
         }
+        p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
         usb_msd_packet_complete(s);
     } else if (s->data_len == 0) {
         s->mode = USB_MSDM_CSW;
@@ -330,14 +332,14 @@ static void usb_msd_handle_reset(USBDevice *dev)
     assert(s->req == NULL);
 
     if (s->packet) {
-        s->packet->result = USB_RET_STALL;
+        s->packet->status = USB_RET_STALL;
         usb_msd_packet_complete(s);
     }
 
     s->mode = USB_MSDM_CBW;
 }
 
-static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_msd_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     MSDState *s = (MSDState *)dev;
@@ -345,29 +347,25 @@ static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
-    ret = 0;
     switch (request) {
     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
-        ret = 0;
         break;
         /* Class specific requests.  */
     case ClassInterfaceOutRequest | MassStorageReset:
         /* Reset state ready for the next CBW.  */
         s->mode = USB_MSDM_CBW;
-        ret = 0;
         break;
     case ClassInterfaceRequest | GetMaxLun:
         data[0] = 0;
-        ret = 1;
+        p->actual_length = 1;
         break;
     default:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
 static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
@@ -382,11 +380,10 @@ static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
     }
 }
 
-static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
 {
     MSDState *s = (MSDState *)dev;
     uint32_t tag;
-    int ret = 0;
     struct usb_msd_cbw cbw;
     uint8_t devep = p->ep->nr;
 
@@ -433,7 +430,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             if (s->req && s->req->cmd.xfer != SCSI_XFER_NONE) {
                 scsi_req_continue(s->req);
             }
-            ret = p->result;
             break;
 
         case USB_MSDM_DATAOUT:
@@ -446,7 +442,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
                 usb_msd_copy_data(s, p);
             }
             if (le32_to_cpu(s->csw.residue)) {
-                int len = p->iov.size - p->result;
+                int len = p->iov.size - p->actual_length;
                 if (len) {
                     usb_packet_skip(p, len);
                     s->data_len -= len;
@@ -455,12 +451,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
                     }
                 }
             }
-            if (p->result < p->iov.size) {
+            if (p->actual_length < p->iov.size) {
                 DPRINTF("Deferring packet %p [wait data-out]\n", p);
                 s->packet = p;
-                ret = USB_RET_ASYNC;
-            } else {
-                ret = p->result;
+                p->status = USB_RET_ASYNC;
             }
             break;
 
@@ -481,7 +475,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
             }
             /* Waiting for SCSI write to complete.  */
             s->packet = p;
-            ret = USB_RET_ASYNC;
+            p->status = USB_RET_ASYNC;
             break;
 
         case USB_MSDM_CSW:
@@ -493,11 +487,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
                 /* still in flight */
                 DPRINTF("Deferring packet %p [wait status]\n", p);
                 s->packet = p;
-                ret = USB_RET_ASYNC;
+                p->status = USB_RET_ASYNC;
             } else {
                 usb_msd_send_status(s, p);
                 s->mode = USB_MSDM_CBW;
-                ret = 13;
             }
             break;
 
@@ -508,7 +501,7 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
                 usb_msd_copy_data(s, p);
             }
             if (le32_to_cpu(s->csw.residue)) {
-                int len = p->iov.size - p->result;
+                int len = p->iov.size - p->actual_length;
                 if (len) {
                     usb_packet_skip(p, len);
                     s->data_len -= len;
@@ -517,12 +510,10 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
                     }
                 }
             }
-            if (p->result < p->iov.size) {
+            if (p->actual_length < p->iov.size) {
                 DPRINTF("Deferring packet %p [wait data-in]\n", p);
                 s->packet = p;
-                ret = USB_RET_ASYNC;
-            } else {
-                ret = p->result;
+                p->status = USB_RET_ASYNC;
             }
             break;
 
@@ -535,11 +526,9 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
     default:
         DPRINTF("Bad token\n");
     fail:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-
-    return ret;
 }
 
 static void usb_msd_password_cb(void *opaque, int err)
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
index 4389380..a21b2ba 100644
--- a/hw/usb/dev-uas.c
+++ b/hw/usb/dev-uas.c
@@ -256,10 +256,10 @@ static void usb_uas_send_status_bh(void *opaque)
 
     uas->status = NULL;
     usb_packet_copy(p, &st->status, st->length);
-    p->result = st->length;
     QTAILQ_REMOVE(&uas->results, st, next);
     g_free(st);
 
+    p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
     usb_packet_complete(&uas->dev, p);
 }
 
@@ -349,6 +349,7 @@ static void usb_uas_complete_data_packet(UASRequest *req)
     p = req->data;
     req->data = NULL;
     req->data_async = false;
+    p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
     usb_packet_complete(&req->uas->dev, p);
 }
 
@@ -357,16 +358,16 @@ static void usb_uas_copy_data(UASRequest *req)
     uint32_t length;
 
     length = MIN(req->buf_size - req->buf_off,
-                 req->data->iov.size - req->data->result);
+                 req->data->iov.size - req->data->actual_length);
     trace_usb_uas_xfer_data(req->uas->dev.addr, req->tag, length,
-                            req->data->result, req->data->iov.size,
+                            req->data->actual_length, req->data->iov.size,
                             req->buf_off, req->buf_size);
     usb_packet_copy(req->data, scsi_req_get_buf(req->req) + req->buf_off,
                     length);
     req->buf_off += length;
     req->data_off += length;
 
-    if (req->data->result == req->data->iov.size) {
+    if (req->data->actual_length == req->data->iov.size) {
         usb_uas_complete_data_packet(req);
     }
     if (req->buf_size && req->buf_off == req->buf_size) {
@@ -504,17 +505,17 @@ static void usb_uas_handle_reset(USBDevice *dev)
     }
 }
 
-static int usb_uas_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_uas_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     int ret;
 
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
     fprintf(stderr, "%s: unhandled control request\n", __func__);
-    return USB_RET_STALL;
+    p->status = USB_RET_STALL;
 }
 
 static void usb_uas_cancel_io(USBDevice *dev, USBPacket *p)
@@ -641,13 +642,13 @@ incorrect_lun:
     usb_uas_queue_response(uas, tag, UAS_RC_INCORRECT_LUN, 0);
 }
 
-static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_uas_handle_data(USBDevice *dev, USBPacket *p)
 {
     UASDevice *uas = DO_UPCAST(UASDevice, dev, dev);
     uas_ui ui;
     UASStatus *st;
     UASRequest *req;
-    int length, ret = 0;
+    int length;
 
     switch (p->ep->nr) {
     case UAS_PIPE_ID_COMMAND:
@@ -656,16 +657,14 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
         switch (ui.hdr.id) {
         case UAS_UI_COMMAND:
             usb_uas_command(uas, &ui);
-            ret = length;
             break;
         case UAS_UI_TASK_MGMT:
             usb_uas_task(uas, &ui);
-            ret = length;
             break;
         default:
             fprintf(stderr, "%s: unknown command ui: id 0x%x\n",
                     __func__, ui.hdr.id);
-            ret = USB_RET_STALL;
+            p->status = USB_RET_STALL;
             break;
         }
         break;
@@ -674,11 +673,10 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
         if (st == NULL) {
             assert(uas->status == NULL);
             uas->status = p;
-            ret = USB_RET_ASYNC;
+            p->status = USB_RET_ASYNC;
             break;
         }
         usb_packet_copy(p, &st->status, st->length);
-        ret = st->length;
         QTAILQ_REMOVE(&uas->results, st, next);
         g_free(st);
         break;
@@ -687,28 +685,26 @@ static int usb_uas_handle_data(USBDevice *dev, USBPacket *p)
         req = (p->ep->nr == UAS_PIPE_ID_DATA_IN) ? uas->datain : uas->dataout;
         if (req == NULL) {
             fprintf(stderr, "%s: no inflight request\n", __func__);
-            ret = USB_RET_STALL;
+            p->status = USB_RET_STALL;
             break;
         }
         scsi_req_ref(req->req);
         req->data = p;
         usb_uas_copy_data(req);
-        if (p->result == p->iov.size || req->complete) {
+        if (p->actual_length == p->iov.size || req->complete) {
             req->data = NULL;
-            ret = p->result;
         } else {
             req->data_async = true;
-            ret = USB_RET_ASYNC;
+            p->status = USB_RET_ASYNC;
         }
         scsi_req_unref(req->req);
         usb_uas_start_next_transfer(uas);
         break;
     default:
         fprintf(stderr, "%s: invalid endpoint %d\n", __func__, p->ep->nr);
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
 static void usb_uas_handle_destroy(USBDevice *dev)
diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c
index ed9a5ee..08b416d 100644
--- a/hw/usb/dev-wacom.c
+++ b/hw/usb/dev-wacom.c
@@ -250,7 +250,7 @@ static void usb_wacom_handle_reset(USBDevice *dev)
     s->mode = WACOM_MODE_HID;
 }
 
-static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     USBWacomState *s = (USBWacomState *) dev;
@@ -258,10 +258,9 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
 
     ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
     if (ret >= 0) {
-        return ret;
+        return;
     }
 
-    ret = 0;
     switch (request) {
     case WACOM_SET_REPORT:
         if (s->mouse_grabbed) {
@@ -269,61 +268,58 @@ static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
             s->mouse_grabbed = 0;
         }
         s->mode = data[0];
-        ret = 0;
         break;
     case WACOM_GET_REPORT:
         data[0] = 0;
         data[1] = s->mode;
-        ret = 2;
+        p->actual_length = 2;
         break;
     /* USB HID requests */
     case HID_GET_REPORT:
         if (s->mode == WACOM_MODE_HID)
-            ret = usb_mouse_poll(s, data, length);
+            p->actual_length = usb_mouse_poll(s, data, length);
         else if (s->mode == WACOM_MODE_WACOM)
-            ret = usb_wacom_poll(s, data, length);
+            p->actual_length = usb_wacom_poll(s, data, length);
         break;
     case HID_GET_IDLE:
-        ret = 1;
         data[0] = s->idle;
+        p->actual_length = 1;
         break;
     case HID_SET_IDLE:
         s->idle = (uint8_t) (value >> 8);
-        ret = 0;
         break;
     default:
-        ret = USB_RET_STALL;
+        p->status = USB_RET_STALL;
         break;
     }
-    return ret;
 }
 
-static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBWacomState *s = (USBWacomState *) dev;
     uint8_t buf[p->iov.size];
-    int ret = 0;
+    int len = 0;
 
     switch (p->pid) {
     case USB_TOKEN_IN:
         if (p->ep->nr == 1) {
-            if (!(s->changed || s->idle))
-                return USB_RET_NAK;
+            if (!(s->changed || s->idle)) {
+                p->status = USB_RET_NAK;
+                return;
+            }
             s->changed = 0;
             if (s->mode == WACOM_MODE_HID)
-                ret = usb_mouse_poll(s, buf, p->iov.size);
+                len = usb_mouse_poll(s, buf, p->iov.size);
             else if (s->mode == WACOM_MODE_WACOM)
-                ret = usb_wacom_poll(s, buf, p->iov.size);
-            usb_packet_copy(p, buf, ret);
+                len = usb_wacom_poll(s, buf, p->iov.size);
+            usb_packet_copy(p, buf, len);
             break;
         }
         /* Fall through.  */
     case USB_TOKEN_OUT:
     default:
-        ret = USB_RET_STALL;
-        break;
+        p->status = USB_RET_STALL;
     }
-    return ret;
 }
 
 static void usb_wacom_handle_destroy(USBDevice *dev)
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index d9dc576..3a1f513 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -1163,7 +1163,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
     p = container_of(packet, EHCIPacket, packet);
     assert(p->async == EHCI_ASYNC_INFLIGHT);
 
-    if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
+    if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
         trace_usb_ehci_packet_action(p->queue, p, "remove");
         ehci_free_packet(p);
         return;
@@ -1171,7 +1171,7 @@ static void ehci_async_complete_packet(USBPort *port, USBPacket *packet)
 
     trace_usb_ehci_packet_action(p->queue, p, "wakeup");
     p->async = EHCI_ASYNC_FINISHED;
-    p->usb_status = packet->result;
+    p->usb_status = packet->status ? packet->status : packet->actual_length;
 
     if (p->queue->async) {
         qemu_bh_schedule(p->queue->ehci->async_bh);
@@ -1253,7 +1253,6 @@ static void ehci_execute_complete(EHCIQueue *q)
 static int ehci_execute(EHCIPacket *p, const char *action)
 {
     USBEndpoint *ep;
-    int ret;
     int endp;
     bool spd;
 
@@ -1303,17 +1302,22 @@ static int ehci_execute(EHCIPacket *p, const char *action)
     }
 
     trace_usb_ehci_packet_action(p->queue, p, action);
-    ret = usb_handle_packet(p->queue->dev, &p->packet);
-    DPRINTF("submit: qh %x next %x qtd %x pid %x len %zd endp %x ret %d\n",
-            q->qhaddr, q->qh.next, q->qtdaddr, q->pid,
-            q->packet.iov.size, endp, ret);
+    usb_handle_packet(p->queue->dev, &p->packet);
+    DPRINTF("submit: qh 0x%x next 0x%x qtd 0x%x pid 0x%x len %zd endp 0x%x "
+            "status %d actual_length %d\n", p->queue->qhaddr, p->qtd.next,
+            p->qtdaddr, p->pid, p->packet.iov.size, endp, p->packet.status,
+            p->packet.actual_length);
 
-    if (ret > BUFF_SIZE) {
+    if (p->packet.actual_length > BUFF_SIZE) {
         fprintf(stderr, "ret from usb_handle_packet > BUFF_SIZE\n");
         return USB_RET_PROCERR;
     }
 
-    return ret;
+    if (p->packet.status == USB_RET_SUCCESS) {
+        return p->packet.actual_length;
+    } else {
+        return p->packet.status;
+    }
 }
 
 /*  4.7.2
@@ -1370,8 +1374,10 @@ static int ehci_process_itd(EHCIState *ehci,
                 usb_packet_setup(&ehci->ipacket, pid, ep, addr, false,
                                  (itd->transact[i] & ITD_XACT_IOC) != 0);
                 usb_packet_map(&ehci->ipacket, &ehci->isgl);
-                ret = usb_handle_packet(dev, &ehci->ipacket);
+                usb_handle_packet(dev, &ehci->ipacket);
                 usb_packet_unmap(&ehci->ipacket, &ehci->isgl);
+                ret = (ehci->ipacket.status == USB_RET_SUCCESS) ?
+                      ehci->ipacket.actual_length : ehci->ipacket.status;
             } else {
                 DPRINTF("ISOCH: attempt to addess non-iso endpoint\n");
                 ret = USB_RET_NAK;
diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c
index 4f55390..c707f7a 100644
--- a/hw/usb/hcd-musb.c
+++ b/hw/usb/hcd-musb.c
@@ -607,7 +607,6 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
 {
     USBDevice *dev;
     USBEndpoint *uep;
-    int ret;
     int idx = epnum && dir;
     int ttype;
 
@@ -632,15 +631,19 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep,
     ep->packey[dir].ep = ep;
     ep->packey[dir].dir = dir;
 
-    ret = usb_handle_packet(dev, &ep->packey[dir].p);
+    usb_handle_packet(dev, &ep->packey[dir].p);
 
-    if (ret == USB_RET_ASYNC) {
+    if (ep->packey[dir].p.status == USB_RET_ASYNC) {
         usb_device_flush_ep_queue(dev, uep);
         ep->status[dir] = len;
         return;
     }
 
-    ep->status[dir] = ret;
+    if (ep->packey[dir].p.status == USB_RET_SUCCESS) {
+        ep->status[dir] = ep->packey[dir].p.actual_length;
+    } else {
+        ep->status[dir] = ep->packey[dir].p.status;
+    }
     musb_schedule_cb(&s->port, &ep->packey[dir].p);
 }
 
@@ -754,7 +757,6 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
 
     if (ep->status[1] == USB_RET_STALL) {
         ep->status[1] = 0;
-        packey->result = 0;
 
         ep->csr[1] |= MGC_M_RXCSR_H_RXSTALL;
         if (!epnum)
@@ -793,14 +795,12 @@ static void musb_rx_packet_complete(USBPacket *packey, void *opaque)
     /* TODO: check len for over/underruns of an OUT packet?  */
     /* TODO: perhaps make use of e->ext_size[1] here.  */
 
-    packey->result = ep->status[1];
-
     if (!(ep->csr[1] & (MGC_M_RXCSR_H_RXSTALL | MGC_M_RXCSR_DATAERROR))) {
         ep->csr[1] |= MGC_M_RXCSR_FIFOFULL | MGC_M_RXCSR_RXPKTRDY;
         if (!epnum)
             ep->csr[0] |= MGC_M_CSR0_RXPKTRDY;
 
-        ep->rxcount = packey->result; /* XXX: MIN(packey->len, ep->maxp[1]); */
+        ep->rxcount = ep->status[1]; /* XXX: MIN(packey->len, ep->maxp[1]); */
         /* In DMA mode: assert DMA request for this EP */
     }
 
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index 7571e9e..6a39e96 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -807,21 +807,24 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
                          DMA_DIRECTION_TO_DEVICE);
     }
 
-    if (completion) {
-        ret = ohci->usb_packet.result;
-    } else {
+    if (!completion) {
         bool int_req = relative_frame_number == frame_count &&
                        OHCI_BM(iso_td.flags, TD_DI) == 0;
         dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA));
         ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN));
         usb_packet_setup(&ohci->usb_packet, pid, ep, addr, false, int_req);
         usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len);
-        ret = usb_handle_packet(dev, &ohci->usb_packet);
-        if (ret == USB_RET_ASYNC) {
+        usb_handle_packet(dev, &ohci->usb_packet);
+        if (ohci->usb_packet.status == USB_RET_ASYNC) {
             usb_device_flush_ep_queue(dev, ep);
             return 1;
         }
     }
+    if (ohci->usb_packet.status == USB_RET_SUCCESS) {
+        ret = ohci->usb_packet.actual_length;
+    } else {
+        ret = ohci->usb_packet.status;
+    }
 
 #ifdef DEBUG_ISOCH
     printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
@@ -997,7 +1000,6 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
     }
 #endif
     if (completion) {
-        ret = ohci->usb_packet.result;
         ohci->async_td = 0;
         ohci->async_complete = 0;
     } else {
@@ -1017,16 +1019,22 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
         usb_packet_setup(&ohci->usb_packet, pid, ep, addr, !flag_r,
                          OHCI_BM(td.flags, TD_DI) == 0);
         usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, pktlen);
-        ret = usb_handle_packet(dev, &ohci->usb_packet);
+        usb_handle_packet(dev, &ohci->usb_packet);
 #ifdef DEBUG_PACKET
-        DPRINTF("ret=%d\n", ret);
+        DPRINTF("status=%d\n", ohci->usb_packet.status);
 #endif
-        if (ret == USB_RET_ASYNC) {
+        if (ohci->usb_packet.status == USB_RET_ASYNC) {
             usb_device_flush_ep_queue(dev, ep);
             ohci->async_td = addr;
             return 1;
         }
     }
+    if (ohci->usb_packet.status == USB_RET_SUCCESS) {
+        ret = ohci->usb_packet.actual_length;
+    } else {
+        ret = ohci->usb_packet.status;
+    }
+
     if (ret >= 0) {
         if (dir == OHCI_TD_DIR_IN) {
             ohci_copy_td(ohci, &td, ohci->usb_buf, ret,
diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c
index f4b555a..2838d21 100644
--- a/hw/usb/hcd-uhci.c
+++ b/hw/usb/hcd-uhci.c
@@ -780,22 +780,21 @@ static int uhci_handle_td_error(UHCIState *s, UHCI_TD *td, uint32_t td_addr,
 
 static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
 {
-    int len = 0, max_len, ret;
+    int len = 0, max_len;
     uint8_t pid;
 
     max_len = ((td->token >> 21) + 1) & 0x7ff;
     pid = td->token & 0xff;
 
-    ret = async->packet.result;
-
     if (td->ctrl & TD_CTRL_IOS)
         td->ctrl &= ~TD_CTRL_ACTIVE;
 
-    if (ret < 0) {
-        return uhci_handle_td_error(s, td, async->td_addr, ret, int_mask);
+    if (async->packet.status != USB_RET_SUCCESS) {
+        return uhci_handle_td_error(s, td, async->td_addr,
+                                    async->packet.status, int_mask);
     }
 
-    len = async->packet.result;
+    len = async->packet.actual_length;
     td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
 
     /* The NAK bit may have been set by a previous frame, so clear it
@@ -824,7 +823,7 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
 static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
                           UHCI_TD *td, uint32_t td_addr, uint32_t *int_mask)
 {
-    int len = 0, max_len;
+    int ret, max_len;
     bool spd;
     bool queuing = (q != NULL);
     uint8_t pid = td->token & 0xff;
@@ -915,13 +914,14 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
     switch(pid) {
     case USB_TOKEN_OUT:
     case USB_TOKEN_SETUP:
-        len = usb_handle_packet(q->ep->dev, &async->packet);
-        if (len >= 0)
-            len = max_len;
+        usb_handle_packet(q->ep->dev, &async->packet);
+        if (async->packet.status == USB_RET_SUCCESS) {
+            async->packet.actual_length = max_len;
+        }
         break;
 
     case USB_TOKEN_IN:
-        len = usb_handle_packet(q->ep->dev, &async->packet);
+        usb_handle_packet(q->ep->dev, &async->packet);
         break;
 
     default:
@@ -932,8 +932,8 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
         uhci_update_irq(s);
         return TD_RESULT_STOP_FRAME;
     }
- 
-    if (len == USB_RET_ASYNC) {
+
+    if (async->packet.status == USB_RET_ASYNC) {
         uhci_async_link(async);
         if (!queuing) {
             uhci_queue_fill(q, td);
@@ -941,13 +941,11 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr,
         return TD_RESULT_ASYNC_START;
     }
 
-    async->packet.result = len;
-
 done:
-    len = uhci_complete_td(s, td, async, int_mask);
+    ret = uhci_complete_td(s, td, async, int_mask);
     usb_packet_unmap(&async->packet, &async->sgl);
     uhci_async_free(async);
-    return len;
+    return ret;
 }
 
 static void uhci_async_complete(USBPort *port, USBPacket *packet)
@@ -955,7 +953,7 @@ static void uhci_async_complete(USBPort *port, USBPacket *packet)
     UHCIAsync *async = container_of(packet, UHCIAsync, packet);
     UHCIState *s = async->queue->uhci;
 
-    if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
+    if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
         uhci_async_unlink(async);
         uhci_async_cancel(async);
         return;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 900abf5..d4a2e0c 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1388,7 +1388,7 @@ static void xhci_xfer_report(XHCITransfer *xfer)
     XHCIState *xhci = xfer->xhci;
     int i;
 
-    left = xfer->packet.result < 0 ? 0 : xfer->packet.result;
+    left = xfer->packet.status ? 0 : xfer->packet.actual_length;
 
     for (i = 0; i < xfer->trb_count; i++) {
         XHCITRB *trb = &xfer->trbs[i];
@@ -1490,16 +1490,16 @@ static int xhci_setup_packet(XHCITransfer *xfer)
     return 0;
 }
 
-static int xhci_complete_packet(XHCITransfer *xfer, int ret)
+static int xhci_complete_packet(XHCITransfer *xfer)
 {
-    if (ret == USB_RET_ASYNC) {
+    if (xfer->packet.status == USB_RET_ASYNC) {
         trace_usb_xhci_xfer_async(xfer);
         xfer->running_async = 1;
         xfer->running_retry = 0;
         xfer->complete = 0;
         xfer->cancelled = 0;
         return 0;
-    } else if (ret == USB_RET_NAK) {
+    } else if (xfer->packet.status == USB_RET_NAK) {
         trace_usb_xhci_xfer_nak(xfer);
         xfer->running_async = 0;
         xfer->running_retry = 1;
@@ -1513,16 +1513,16 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
         xhci_xfer_unmap(xfer);
     }
 
-    if (ret >= 0) {
-        trace_usb_xhci_xfer_success(xfer, ret);
+    if (xfer->packet.status == USB_RET_SUCCESS) {
+        trace_usb_xhci_xfer_success(xfer, xfer->packet.actual_length);
         xfer->status = CC_SUCCESS;
         xhci_xfer_report(xfer);
         return 0;
     }
 
     /* error */
-    trace_usb_xhci_xfer_error(xfer, ret);
-    switch (ret) {
+    trace_usb_xhci_xfer_error(xfer, xfer->packet.status);
+    switch (xfer->packet.status) {
     case USB_RET_NODEV:
         xfer->status = CC_USB_TRANSACTION_ERROR;
         xhci_xfer_report(xfer);
@@ -1534,7 +1534,8 @@ static int xhci_complete_packet(XHCITransfer *xfer, int ret)
         xhci_stall_ep(xfer);
         break;
     default:
-        fprintf(stderr, "%s: FIXME: ret = %d\n", __FUNCTION__, ret);
+        fprintf(stderr, "%s: FIXME: status = %d\n", __func__,
+                xfer->packet.status);
         FIXME();
     }
     return 0;
@@ -1544,7 +1545,6 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
 {
     XHCITRB *trb_setup, *trb_status;
     uint8_t bmRequestType;
-    int ret;
 
     trb_setup = &xfer->trbs[0];
     trb_status = &xfer->trbs[xfer->trb_count-1];
@@ -1587,9 +1587,9 @@ static int xhci_fire_ctl_transfer(XHCIState *xhci, XHCITransfer *xfer)
     }
     xfer->packet.parameter = trb_setup->parameter;
 
-    ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+    usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
 
-    xhci_complete_packet(xfer, ret);
+    xhci_complete_packet(xfer);
     if (!xfer->running_async && !xfer->running_retry) {
         xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
     }
@@ -1636,7 +1636,6 @@ static void xhci_check_iso_kick(XHCIState *xhci, XHCITransfer *xfer,
 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);
 
@@ -1671,9 +1670,9 @@ static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx
     if (xhci_setup_packet(xfer) < 0) {
         return -1;
     }
-    ret = usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+    usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
 
-    xhci_complete_packet(xfer, ret);
+    xhci_complete_packet(xfer);
     if (!xfer->running_async && !xfer->running_retry) {
         xhci_kick_ep(xhci, xfer->slotid, xfer->epid);
     }
@@ -1711,7 +1710,6 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
 
     if (epctx->retry) {
         XHCITransfer *xfer = epctx->retry;
-        int result;
 
         trace_usb_xhci_xfer_retry(xfer);
         assert(xfer->running_retry);
@@ -1725,19 +1723,19 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid, unsigned int epid
             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);
+            usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+            assert(xfer->packet.status != USB_RET_NAK);
+            xhci_complete_packet(xfer);
         } 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) {
+            usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+            if (xfer->packet.status == USB_RET_NAK) {
                 return;
             }
-            xhci_complete_packet(xfer, result);
+            xhci_complete_packet(xfer);
         }
         assert(!xfer->running_retry);
         epctx->retry = NULL;
@@ -2922,11 +2920,11 @@ static void xhci_complete(USBPort *port, USBPacket *packet)
 {
     XHCITransfer *xfer = container_of(packet, XHCITransfer, packet);
 
-    if (packet->result == USB_RET_REMOVE_FROM_QUEUE) {
+    if (packet->status == USB_RET_REMOVE_FROM_QUEUE) {
         xhci_ep_nuke_one_xfer(xfer);
         return;
     }
-    xhci_complete_packet(xfer, packet->result);
+    xhci_complete_packet(xfer);
     xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid);
 }
 
diff --git a/hw/usb/host-bsd.c b/hw/usb/host-bsd.c
index ec26266..6473e8b 100644
--- a/hw/usb/host-bsd.c
+++ b/hw/usb/host-bsd.c
@@ -121,7 +121,7 @@ static void usb_host_handle_reset(USBDevice *dev)
  * -check device states against transfer requests
  *  and return appropriate response
  */
-static int usb_host_handle_control(USBDevice *dev,
+static void usb_host_handle_control(USBDevice *dev,
                                    USBPacket *p,
                                    int request,
                                    int value,
@@ -139,7 +139,6 @@ static int usb_host_handle_control(USBDevice *dev,
 
         /* specific SET_ADDRESS support */
         dev->addr = value;
-        return 0;
     } else if ((request >> 8) == UT_WRITE_DEVICE &&
                (request & 0xff) == UR_SET_CONFIG) {
 
@@ -151,10 +150,8 @@ static int usb_host_handle_control(USBDevice *dev,
             printf("handle_control: failed to set configuration - %s\n",
                    strerror(errno));
 #endif
-            return USB_RET_STALL;
+            p->status = USB_RET_STALL;
         }
-
-        return 0;
     } else if ((request >> 8) == UT_WRITE_INTERFACE &&
                (request & 0xff) == UR_SET_INTERFACE) {
 
@@ -168,10 +165,8 @@ static int usb_host_handle_control(USBDevice *dev,
             printf("handle_control: failed to set alternate interface - %s\n",
                    strerror(errno));
 #endif
-            return USB_RET_STALL;
+            p->status = USB_RET_STALL;
         }
-
-        return 0;
     } else {
         req.ucr_request.bmRequestType = request >> 8;
         req.ucr_request.bRequest = request & 0xff;
@@ -201,14 +196,14 @@ static int usb_host_handle_control(USBDevice *dev,
             printf("handle_control: error after request - %s\n",
                    strerror(errno));
 #endif
-            return USB_RET_NAK; // STALL
+            p->status = USB_RET_NAK; /* STALL */
         } else {
-            return req.ucr_actlen;
+            p->actual_length = req.ucr_actlen;
         }
     }
 }
 
-static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBHostDevice *s = (USBHostDevice *)dev;
     int ret, fd, mode;
@@ -232,7 +227,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
     fd = ensure_ep_open(s, devep, mode);
     if (fd < 0) {
         sigprocmask(SIG_SETMASK, &old_mask, NULL);
-        return USB_RET_NODEV;
+        p->status = USB_RET_NODEV;
+        return;
     }
 
     if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
@@ -267,12 +263,13 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         switch(errno) {
         case ETIMEDOUT:
         case EINTR:
-            return USB_RET_NAK;
+            p->status = USB_RET_NAK;
+            break;
         default:
-            return USB_RET_STALL;
+            p->status = USB_RET_STALL;
         }
     } else {
-        return ret;
+        p->actual_length = ret;
     }
 }
 
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index 3a258b4..ca3e24a 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -366,28 +366,29 @@ static void async_complete(void *opaque)
         if (p) {
             switch (aurb->urb.status) {
             case 0:
-                p->result += aurb->urb.actual_length;
+                p->actual_length = aurb->urb.actual_length;
+                p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
                 break;
 
             case -EPIPE:
                 set_halt(s, p->pid, p->ep->nr);
-                p->result = USB_RET_STALL;
+                p->status = USB_RET_STALL;
                 break;
 
             case -EOVERFLOW:
-                p->result = USB_RET_BABBLE;
+                p->status = USB_RET_BABBLE;
                 break;
 
             default:
-                p->result = USB_RET_IOERROR;
+                p->status = USB_RET_IOERROR;
                 break;
             }
 
             if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
+                trace_usb_host_req_complete(s->bus_num, s->addr, p, p->status);
                 usb_generic_async_ctrl_complete(&s->dev, p);
             } else if (!aurb->more) {
-                trace_usb_host_req_complete(s->bus_num, s->addr, p, p->result);
+                trace_usb_host_req_complete(s->bus_num, s->addr, p, p->status);
                 usb_packet_complete(&s->dev, p);
             }
         }
@@ -733,27 +734,31 @@ static void usb_host_stop_n_free_iso(USBHostDevice *s, int pid, uint8_t ep)
     clear_iso_started(s, pid, ep);
 }
 
-static int urb_status_to_usb_ret(int status)
+static void urb_status_to_usb_ret(int status, USBPacket *p)
 {
     switch (status) {
     case -EPIPE:
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        break;
     case -EOVERFLOW:
-        return USB_RET_BABBLE;
+        p->status = USB_RET_BABBLE;
+        break;
     default:
-        return USB_RET_IOERROR;
+        p->status = USB_RET_IOERROR;
     }
 }
 
-static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
+static void usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
 {
     AsyncURB *aurb;
-    int i, j, ret, max_packet_size, offset, len = 0;
+    int i, j, max_packet_size, offset, len;
     uint8_t *buf;
 
     max_packet_size = p->ep->max_packet_size;
-    if (max_packet_size == 0)
-        return USB_RET_NAK;
+    if (max_packet_size == 0) {
+        p->status = USB_RET_NAK;
+        return;
+    }
 
     aurb = get_iso_urb(s, p->pid, p->ep->nr);
     if (!aurb) {
@@ -766,18 +771,17 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
         if (in) {
             /* Check urb status  */
             if (aurb[i].urb.status) {
-                len = urb_status_to_usb_ret(aurb[i].urb.status);
+                urb_status_to_usb_ret(aurb[i].urb.status, p);
                 /* Move to the next urb */
                 aurb[i].iso_frame_idx = ISO_FRAME_DESC_PER_URB - 1;
             /* Check frame status */
             } else if (aurb[i].urb.iso_frame_desc[j].status) {
-                len = urb_status_to_usb_ret(
-                                        aurb[i].urb.iso_frame_desc[j].status);
+                urb_status_to_usb_ret(aurb[i].urb.iso_frame_desc[j].status, p);
             /* Check the frame fits */
             } else if (aurb[i].urb.iso_frame_desc[j].actual_length
                        > p->iov.size) {
                 printf("husb: received iso data is larger then packet\n");
-                len = USB_RET_BABBLE;
+                p->status = USB_RET_BABBLE;
             /* All good copy data over */
             } else {
                 len = aurb[i].urb.iso_frame_desc[j].actual_length;
@@ -792,7 +796,8 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
             /* Check the frame fits */
             if (len > max_packet_size) {
                 printf("husb: send iso data is larger then max packet size\n");
-                return USB_RET_NAK;
+                p->status = USB_RET_NAK;
+                return;
             }
 
             /* All good copy data over */
@@ -823,17 +828,16 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
         /* (Re)-submit all fully consumed / filled urbs */
         for (i = 0; i < s->iso_urb_count; i++) {
             if (aurb[i].iso_frame_idx == ISO_FRAME_DESC_PER_URB) {
-                ret = ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]);
-                if (ret < 0) {
+                if (ioctl(s->fd, USBDEVFS_SUBMITURB, &aurb[i]) < 0) {
                     perror("USBDEVFS_SUBMITURB");
-                    if (!in || len == 0) {
+                    if (!in || p->status == USB_RET_SUCCESS) {
                         switch(errno) {
                         case ETIMEDOUT:
-                            len = USB_RET_NAK;
+                            p->status = USB_RET_NAK;
                             break;
                         case EPIPE:
                         default:
-                            len = USB_RET_STALL;
+                            p->status = USB_RET_STALL;
                         }
                     }
                     break;
@@ -843,11 +847,9 @@ static int usb_host_handle_iso_data(USBHostDevice *s, USBPacket *p, int in)
             }
         }
     }
-
-    return len;
 }
 
-static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
+static void usb_host_handle_data(USBDevice *dev, USBPacket *p)
 {
     USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
     struct usbdevfs_urb *urb;
@@ -862,7 +864,8 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
 
     if (!is_valid(s, p->pid, p->ep->nr)) {
         trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
-        return USB_RET_NAK;
+        p->status = USB_RET_NAK;
+        return;
     }
 
     if (p->pid == USB_TOKEN_IN) {
@@ -877,13 +880,15 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
         if (ret < 0) {
             perror("USBDEVFS_CLEAR_HALT");
             trace_usb_host_req_complete(s->bus_num, s->addr, p, USB_RET_NAK);
-            return USB_RET_NAK;
+            p->status = USB_RET_NAK;
+            return;
         }
         clear_halt(s, p->pid, p->ep->nr);
     }
 
     if (is_isoc(s, p->pid, p->ep->nr)) {
-        return usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
+        usb_host_handle_iso_data(s, p, p->pid == USB_TOKEN_IN);
+        return;
     }
 
     v = 0;
@@ -933,17 +938,19 @@ static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
             case ETIMEDOUT:
                 trace_usb_host_req_complete(s->bus_num, s->addr, p,
                                             USB_RET_NAK);
-                return USB_RET_NAK;
+                p->status = USB_RET_NAK;
+                break;
             case EPIPE:
             default:
                 trace_usb_host_req_complete(s->bus_num, s->addr, p,
                                             USB_RET_STALL);
-                return USB_RET_STALL;
+                p->status = USB_RET_STALL;
             }
+            return;
         }
     } while (rem > 0);
 
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
 static int ctrl_error(void)
@@ -955,14 +962,13 @@ static int ctrl_error(void)
     }
 }
 
-static int usb_host_set_address(USBHostDevice *s, int addr)
+static void usb_host_set_address(USBHostDevice *s, int addr)
 {
     trace_usb_host_set_address(s->bus_num, s->addr, addr);
     s->dev.addr = addr;
-    return 0;
 }
 
-static int usb_host_set_config(USBHostDevice *s, int config)
+static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
 {
     int ret, first = 1;
 
@@ -987,14 +993,15 @@ again:
     }
 
     if (ret < 0) {
-        return ctrl_error();
+        p->status = ctrl_error();
+        return;
     }
     usb_host_claim_interfaces(s, config);
     usb_linux_update_endp_table(s);
-    return 0;
 }
 
-static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
+static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
+                                   USBPacket *p)
 {
     struct usbdevfs_setinterface si;
     int i, ret;
@@ -1011,7 +1018,8 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
     }
 
     if (iface >= USB_MAX_INTERFACES) {
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        return;
     }
 
     si.interface  = iface;
@@ -1022,15 +1030,15 @@ static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
             iface, alt, ret, errno);
 
     if (ret < 0) {
-        return ctrl_error();
+        p->status = ctrl_error();
+        return;
     }
 
     s->dev.altsetting[iface] = alt;
     usb_linux_update_endp_table(s);
-    return 0;
 }
 
-static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
+static void usb_host_handle_control(USBDevice *dev, USBPacket *p,
                int request, int value, int index, int length, uint8_t *data)
 {
     USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev);
@@ -1048,19 +1056,19 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
 
     switch (request) {
     case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-        ret = usb_host_set_address(s, value);
-        trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
-        return ret;
+        usb_host_set_address(s, value);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
 
     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        ret = usb_host_set_config(s, value & 0xff);
-        trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
-        return ret;
+        usb_host_set_config(s, value & 0xff, p);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
 
     case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        ret = usb_host_set_interface(s, index, value);
-        trace_usb_host_req_emulated(s->bus_num, s->addr, p, ret);
-        return ret;
+        usb_host_set_interface(s, index, value, p);
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
 
     case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
         if (value == 0) { /* clear halt */
@@ -1068,17 +1076,16 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
             ioctl(s->fd, USBDEVFS_CLEAR_HALT, &index);
             clear_halt(s, pid, index & 0x0f);
             trace_usb_host_req_emulated(s->bus_num, s->addr, p, 0);
-            return 0;
+            return;
         }
     }
 
     /* 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",
                 length, sizeof(dev->data_buf));
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        return;
     }
 
     aurb = async_alloc(s);
@@ -1112,14 +1119,17 @@ static int usb_host_handle_control(USBDevice *dev, USBPacket *p,
 
         switch(errno) {
         case ETIMEDOUT:
-            return USB_RET_NAK;
+            p->status = USB_RET_NAK;
+            break;
         case EPIPE:
         default:
-            return USB_RET_STALL;
+            p->status = USB_RET_STALL;
+            break;
         }
+        return;
     }
 
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
 /* returns 1 on problem encountered or 0 for success */
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index c5cfe0b..cd4388e 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -141,8 +141,8 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
     struct usb_redir_interrupt_packet_header *interrupt_header,
     uint8_t *data, int data_len);
 
-static int usbredir_handle_status(USBRedirDevice *dev,
-                                       int status, int actual_len);
+static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
+    int status);
 
 #define VERSION "qemu usb-redir guest " QEMU_VERSION
 
@@ -443,7 +443,7 @@ static void usbredir_handle_reset(USBDevice *udev)
     usbredirparser_do_write(dev->parser);
 }
 
-static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
+static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
                                      uint8_t ep)
 {
     int status, len;
@@ -500,7 +500,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
                 !dev->endpoint[EP2I(ep)].bufpq_prefilled) {
             if (dev->endpoint[EP2I(ep)].bufpq_size <
                     dev->endpoint[EP2I(ep)].bufpq_target_size) {
-                return usbredir_handle_status(dev, 0, 0);
+                return;
             }
             dev->endpoint[EP2I(ep)].bufpq_prefilled = 1;
         }
@@ -514,7 +514,8 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
             /* Check iso_error for stream errors, otherwise its an underrun */
             status = dev->endpoint[EP2I(ep)].iso_error;
             dev->endpoint[EP2I(ep)].iso_error = 0;
-            return status ? USB_RET_IOERROR : 0;
+            p->status = status ? USB_RET_IOERROR : USB_RET_SUCCESS;
+            return;
         }
         DPRINTF2("iso-token-in ep %02X status %d len %d queue-size: %d\n", ep,
                  isop->status, isop->len, dev->endpoint[EP2I(ep)].bufpq_size);
@@ -522,7 +523,8 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
         status = isop->status;
         if (status != usb_redir_success) {
             bufp_free(dev, isop, ep);
-            return USB_RET_IOERROR;
+            p->status = USB_RET_IOERROR;
+            return;
         }
 
         len = isop->len;
@@ -530,11 +532,11 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
             ERROR("received iso data is larger then packet ep %02X (%d > %d)\n",
                   ep, len, (int)p->iov.size);
             bufp_free(dev, isop, ep);
-            return USB_RET_BABBLE;
+            p->status = USB_RET_BABBLE;
+            return;
         }
         usb_packet_copy(p, isop->data, len);
         bufp_free(dev, isop, ep);
-        return len;
     } else {
         /* If the stream was not started because of a pending error don't
            send the packet to the usb-host */
@@ -554,7 +556,7 @@ static int usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
         dev->endpoint[EP2I(ep)].iso_error = 0;
         DPRINTF2("iso-token-out ep %02X status %d len %zd\n", ep, status,
                  p->iov.size);
-        return usbredir_handle_status(dev, status, p->iov.size);
+        usbredir_handle_status(dev, p, status);
     }
 }
 
@@ -572,7 +574,7 @@ static void usbredir_stop_iso_stream(USBRedirDevice *dev, uint8_t ep)
     usbredir_free_bufpq(dev, ep);
 }
 
-static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
+static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
                                       uint8_t ep)
 {
     struct usb_redir_bulk_packet_header bulk_packet;
@@ -581,7 +583,8 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
     DPRINTF("bulk-out ep %02X len %zd id %"PRIu64"\n", ep, size, p->id);
 
     if (usbredir_already_in_flight(dev, p->id)) {
-        return USB_RET_ASYNC;
+        p->status = USB_RET_ASYNC;
+        return;
     }
 
     bulk_packet.endpoint  = ep;
@@ -608,10 +611,10 @@ static int usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p,
                                         &bulk_packet, buf, size);
     }
     usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
-static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
+static void usbredir_handle_interrupt_data(USBRedirDevice *dev,
                                            USBPacket *p, uint8_t ep)
 {
     if (ep & USB_DIR_IN) {
@@ -643,9 +646,11 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
             status = dev->endpoint[EP2I(ep)].interrupt_error;
             dev->endpoint[EP2I(ep)].interrupt_error = 0;
             if (status) {
-                return usbredir_handle_status(dev, status, 0);
+                usbredir_handle_status(dev, p, status);
+            } else {
+                p->status = USB_RET_NAK;
             }
-            return USB_RET_NAK;
+            return;
         }
         DPRINTF("interrupt-token-in ep %02X status %d len %d\n", ep,
                 intp->status, intp->len);
@@ -653,18 +658,19 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
         status = intp->status;
         if (status != usb_redir_success) {
             bufp_free(dev, intp, ep);
-            return usbredir_handle_status(dev, status, 0);
+            usbredir_handle_status(dev, p, status);
+            return;
         }
 
         len = intp->len;
         if (len > p->iov.size) {
             ERROR("received int data is larger then packet ep %02X\n", ep);
             bufp_free(dev, intp, ep);
-            return USB_RET_BABBLE;
+            p->status = USB_RET_BABBLE;
+            return;
         }
         usb_packet_copy(p, intp->data, len);
         bufp_free(dev, intp, ep);
-        return len;
     } else {
         /* Output interrupt endpoint, normal async operation */
         struct usb_redir_interrupt_packet_header interrupt_packet;
@@ -674,7 +680,8 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
                 p->iov.size, p->id);
 
         if (usbredir_already_in_flight(dev, p->id)) {
-            return USB_RET_ASYNC;
+            p->status = USB_RET_ASYNC;
+            return;
         }
 
         interrupt_packet.endpoint  = ep;
@@ -685,7 +692,7 @@ static int usbredir_handle_interrupt_data(USBRedirDevice *dev,
         usbredirparser_send_interrupt_packet(dev->parser, p->id,
                                         &interrupt_packet, buf, p->iov.size);
         usbredirparser_do_write(dev->parser);
-        return USB_RET_ASYNC;
+        p->status = USB_RET_ASYNC;
     }
 }
 
@@ -705,7 +712,7 @@ static void usbredir_stop_interrupt_receiving(USBRedirDevice *dev,
     usbredir_free_bufpq(dev, ep);
 }
 
-static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
+static void usbredir_handle_data(USBDevice *udev, USBPacket *p)
 {
     USBRedirDevice *dev = DO_UPCAST(USBRedirDevice, dev, udev);
     uint8_t ep;
@@ -718,21 +725,26 @@ static int usbredir_handle_data(USBDevice *udev, USBPacket *p)
     switch (dev->endpoint[EP2I(ep)].type) {
     case USB_ENDPOINT_XFER_CONTROL:
         ERROR("handle_data called for control transfer on ep %02X\n", ep);
-        return USB_RET_NAK;
+        p->status = USB_RET_NAK;
+        break;
     case USB_ENDPOINT_XFER_ISOC:
-        return usbredir_handle_iso_data(dev, p, ep);
+        usbredir_handle_iso_data(dev, p, ep);
+        break;
     case USB_ENDPOINT_XFER_BULK:
         if (p->state == USB_PACKET_SETUP && p->pid == USB_TOKEN_IN &&
                 p->ep->pipeline) {
-            return USB_RET_ADD_TO_QUEUE;
+            p->status = USB_RET_ADD_TO_QUEUE;
+            break;
         }
-        return usbredir_handle_bulk_data(dev, p, ep);
+        usbredir_handle_bulk_data(dev, p, ep);
+        break;
     case USB_ENDPOINT_XFER_INT:
-        return usbredir_handle_interrupt_data(dev, p, ep);
+        usbredir_handle_interrupt_data(dev, p, ep);
+        break;
     default:
         ERROR("handle_data ep %02X has unknown type %d\n", ep,
               dev->endpoint[EP2I(ep)].type);
-        return USB_RET_NAK;
+        p->status = USB_RET_NAK;
     }
 }
 
@@ -743,7 +755,7 @@ static void usbredir_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
     }
 }
 
-static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
+static void usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
                                 int config)
 {
     struct usb_redir_set_configuration_header set_config;
@@ -768,19 +780,19 @@ static int usbredir_set_config(USBRedirDevice *dev, USBPacket *p,
     set_config.configuration = config;
     usbredirparser_send_set_configuration(dev->parser, p->id, &set_config);
     usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
-static int usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
+static void usbredir_get_config(USBRedirDevice *dev, USBPacket *p)
 {
     DPRINTF("get config id %"PRIu64"\n", p->id);
 
     usbredirparser_send_get_configuration(dev->parser, p->id);
     usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
-static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
+static void usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
                                    int interface, int alt)
 {
     struct usb_redir_set_alt_setting_header set_alt;
@@ -808,10 +820,10 @@ static int usbredir_set_interface(USBRedirDevice *dev, USBPacket *p,
     set_alt.alt = alt;
     usbredirparser_send_set_alt_setting(dev->parser, p->id, &set_alt);
     usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
-static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
+static void usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
                                    int interface)
 {
     struct usb_redir_get_alt_setting_header get_alt;
@@ -821,17 +833,18 @@ static int usbredir_get_interface(USBRedirDevice *dev, USBPacket *p,
     get_alt.interface = interface;
     usbredirparser_send_get_alt_setting(dev->parser, p->id, &get_alt);
     usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
-static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
+static void usbredir_handle_control(USBDevice *udev, USBPacket *p,
         int request, int value, int index, int length, uint8_t *data)
 {
     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;
+        p->status = USB_RET_ASYNC;
+        return;
     }
 
     /* Special cases for certain standard device requests */
@@ -839,15 +852,19 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
     case DeviceOutRequest | USB_REQ_SET_ADDRESS:
         DPRINTF("set address %d\n", value);
         dev->dev.addr = value;
-        return 0;
+        return;
     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
-        return usbredir_set_config(dev, p, value & 0xff);
+        usbredir_set_config(dev, p, value & 0xff);
+        return;
     case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-        return usbredir_get_config(dev, p);
+        usbredir_get_config(dev, p);
+        return;
     case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
-        return usbredir_set_interface(dev, p, index, value);
+        usbredir_set_interface(dev, p, index, value);
+        return;
     case InterfaceRequest | USB_REQ_GET_INTERFACE:
-        return usbredir_get_interface(dev, p, index);
+        usbredir_get_interface(dev, p, index);
+        return;
     }
 
     /* Normal ctrl requests, note request is (bRequestType << 8) | bRequest */
@@ -871,7 +888,7 @@ static int usbredir_handle_control(USBDevice *udev, USBPacket *p,
                                            &control_packet, data, length);
     }
     usbredirparser_do_write(dev->parser);
-    return USB_RET_ASYNC;
+    p->status = USB_RET_ASYNC;
 }
 
 /*
@@ -1159,29 +1176,34 @@ error:
  * usbredirparser packet complete callbacks
  */
 
-static int usbredir_handle_status(USBRedirDevice *dev,
-                                       int status, int actual_len)
+static void usbredir_handle_status(USBRedirDevice *dev, USBPacket *p,
+    int status)
 {
     switch (status) {
     case usb_redir_success:
-        return actual_len;
+        p->status = USB_RET_SUCCESS; /* Clear previous ASYNC status */
+        break;
     case usb_redir_stall:
-        return USB_RET_STALL;
+        p->status = USB_RET_STALL;
+        break;
     case usb_redir_cancelled:
         /*
          * 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;
+        p->status = USB_RET_IOERROR;
+        break;
     case usb_redir_inval:
         WARNING("got invalid param error from usb-host?\n");
-        return USB_RET_IOERROR;
+        p->status = USB_RET_IOERROR;
+        break;
     case usb_redir_babble:
-        return USB_RET_BABBLE;
+        p->status = USB_RET_BABBLE;
+        break;
     case usb_redir_ioerror:
     case usb_redir_timeout:
     default:
-        return USB_RET_IOERROR;
+        p->status = USB_RET_IOERROR;
     }
 }
 
@@ -1412,7 +1434,6 @@ static void usbredir_configuration_status(void *priv, uint64_t id,
 {
     USBRedirDevice *dev = priv;
     USBPacket *p;
-    int len = 0;
 
     DPRINTF("set config status %d config %d id %"PRIu64"\n",
             config_status->status, config_status->configuration, id);
@@ -1421,9 +1442,9 @@ static void usbredir_configuration_status(void *priv, uint64_t id,
     if (p) {
         if (dev->dev.setup_buf[0] & USB_DIR_IN) {
             dev->dev.data_buf[0] = config_status->configuration;
-            len = 1;
+            p->actual_length = 1;
         }
-        p->result = usbredir_handle_status(dev, config_status->status, len);
+        usbredir_handle_status(dev, p, config_status->status);
         usb_generic_async_ctrl_complete(&dev->dev, p);
     }
 }
@@ -1433,7 +1454,6 @@ static void usbredir_alt_setting_status(void *priv, uint64_t id,
 {
     USBRedirDevice *dev = priv;
     USBPacket *p;
-    int len = 0;
 
     DPRINTF("alt status %d intf %d alt %d id: %"PRIu64"\n",
             alt_setting_status->status, alt_setting_status->interface,
@@ -1443,10 +1463,9 @@ static void usbredir_alt_setting_status(void *priv, uint64_t id,
     if (p) {
         if (dev->dev.setup_buf[0] & USB_DIR_IN) {
             dev->dev.data_buf[0] = alt_setting_status->alt;
-            len = 1;
+            p->actual_length = 1;
         }
-        p->result =
-            usbredir_handle_status(dev, alt_setting_status->status, len);
+        usbredir_handle_status(dev, p, alt_setting_status->status);
         usb_generic_async_ctrl_complete(&dev->dev, p);
     }
 }
@@ -1522,18 +1541,19 @@ static void usbredir_control_packet(void *priv, uint64_t id,
 
     p = usbredir_find_packet_by_id(dev, 0, id);
     if (p) {
-        len = usbredir_handle_status(dev, control_packet->status, len);
-        if (len > 0) {
+        usbredir_handle_status(dev, p, control_packet->status);
+        if (p->status == USB_RET_SUCCESS) {
             usbredir_log_data(dev, "ctrl data in:", data, data_len);
             if (data_len <= sizeof(dev->dev.data_buf)) {
                 memcpy(dev->dev.data_buf, data, data_len);
             } else {
                 ERROR("ctrl buffer too small (%d > %zu)\n",
                       data_len, sizeof(dev->dev.data_buf));
-                len = USB_RET_STALL;
+                p->status = USB_RET_STALL;
+                len = 0;
             }
         }
-        p->result = len;
+        p->actual_length = len;
         usb_generic_async_ctrl_complete(&dev->dev, p);
     }
     free(data);
@@ -1554,8 +1574,8 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
     p = usbredir_find_packet_by_id(dev, ep, id);
     if (p) {
         size_t size = (p->combined) ? p->combined->iov.size : p->iov.size;
-        len = usbredir_handle_status(dev, bulk_packet->status, len);
-        if (len > 0) {
+        usbredir_handle_status(dev, p, bulk_packet->status);
+        if (p->status == USB_RET_SUCCESS) {
             usbredir_log_data(dev, "bulk data in:", data, data_len);
             if (data_len <= size) {
                 if (p->combined) {
@@ -1567,10 +1587,11 @@ static void usbredir_bulk_packet(void *priv, uint64_t id,
             } else {
                 ERROR("bulk got more data then requested (%d > %zd)\n",
                       data_len, p->iov.size);
-                len = USB_RET_BABBLE;
+                p->status = USB_RET_BABBLE;
+                len = 0;
             }
         }
-        p->result = len;
+        p->actual_length = len;
         if (p->pid == USB_TOKEN_IN && p->ep->pipeline) {
             usb_combined_input_packet_complete(&dev->dev, p);
         } else {
@@ -1636,8 +1657,8 @@ static void usbredir_interrupt_packet(void *priv, uint64_t id,
 
         USBPacket *p = usbredir_find_packet_by_id(dev, ep, id);
         if (p) {
-            p->result = usbredir_handle_status(dev,
-                                               interrupt_packet->status, len);
+            usbredir_handle_status(dev, p, interrupt_packet->status);
+            p->actual_length = len;
             usb_packet_complete(&dev->dev, p);
         }
     }
commit bdd4df332a1bdb20b7fa39ea741f7830e41e1187
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Nov 2 09:37:27 2012 +0100

    spice: fix initialization order
    
    Register displaychangelistener last, after spice is fully initialized,
    otherwise we may hit NULL pointer dereferences when qemu starts calling
    our callbacks.
    
    Commit e250d949feb1334828f27f0d145c35f29c4b7639 triggers this bug.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/qxl.c b/hw/qxl.c
index e7bf3a1..1bc2d32 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -2049,6 +2049,7 @@ static int qxl_init_primary(PCIDevice *dev)
     PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
     VGACommonState *vga = &qxl->vga;
     PortioList *qxl_vga_port_list = g_new(PortioList, 1);
+    int rc;
 
     qxl->id = 0;
     qxl_init_ramsize(qxl);
@@ -2063,9 +2064,14 @@ static int qxl_init_primary(PCIDevice *dev)
     qemu_spice_display_init_common(&qxl->ssd, vga->ds);
 
     qxl0 = qxl;
-    register_displaychangelistener(vga->ds, &display_listener);
 
-    return qxl_init_common(qxl);
+    rc = qxl_init_common(qxl);
+    if (rc != 0) {
+        return rc;
+    }
+
+    register_displaychangelistener(vga->ds, &display_listener);
+    return rc;
 }
 
 static int qxl_init_secondary(PCIDevice *dev)
diff --git a/ui/spice-display.c b/ui/spice-display.c
index fe2fdfb..0cc0116 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -573,7 +573,6 @@ void qemu_spice_display_init(DisplayState *ds)
 {
     assert(sdpy.ds == NULL);
     qemu_spice_display_init_common(&sdpy, ds);
-    register_displaychangelistener(ds, &display_listener);
 
     sdpy.qxl.base.sif = &dpy_interface.base;
     qemu_spice_add_interface(&sdpy.qxl.base);
@@ -581,4 +580,5 @@ void qemu_spice_display_init(DisplayState *ds)
 
     qemu_spice_create_host_memslot(&sdpy);
     qemu_spice_create_host_primary(&sdpy);
+    register_displaychangelistener(ds, &display_listener);
 }
commit 365b1e9e3486aaa55f30df6f16ecafffaef6ec98
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Sep 18 08:52:26 2012 +0200

    pflib: unused, remove it.
    
    Replaced by pixman library.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/Makefile.objs b/Makefile.objs
index 593a592..37be7e2 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -78,7 +78,6 @@ common-obj-y += input.o
 common-obj-y += buffered_file.o migration.o migration-tcp.o
 common-obj-y += qemu-char.o #aio.o
 common-obj-y += block-migration.o iohandler.o
-common-obj-y += pflib.o
 common-obj-y += bitmap.o bitops.o
 common-obj-y += page_cache.o
 
diff --git a/pflib.c b/pflib.c
deleted file mode 100644
index 987e110..0000000
--- a/pflib.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * PixelFormat conversion library.
- *
- * Author: Gerd Hoffmann <kraxel at redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- * Contributions after 2012-01-13 are licensed under the terms of the
- * GNU GPL, version 2 or (at your option) any later version.
- */
-#include "qemu-common.h"
-#include "console.h"
-#include "pflib.h"
-
-typedef struct QemuPixel QemuPixel;
-
-typedef void (*pf_convert)(QemuPfConv *conv,
-                           void *dst, void *src, uint32_t cnt);
-typedef void (*pf_convert_from)(PixelFormat *pf,
-                                QemuPixel *dst, void *src, uint32_t cnt);
-typedef void (*pf_convert_to)(PixelFormat *pf,
-                              void *dst, QemuPixel *src, uint32_t cnt);
-
-struct QemuPfConv {
-    pf_convert        convert;
-    PixelFormat       src;
-    PixelFormat       dst;
-
-    /* for copy_generic() */
-    pf_convert_from   conv_from;
-    pf_convert_to     conv_to;
-    QemuPixel         *conv_buf;
-    uint32_t          conv_cnt;
-};
-
-struct QemuPixel {
-    uint8_t red;
-    uint8_t green;
-    uint8_t blue;
-    uint8_t alpha;
-};
-
-/* ----------------------------------------------------------------------- */
-/* PixelFormat -> QemuPixel conversions                                    */
-
-static void conv_16_to_pixel(PixelFormat *pf,
-                             QemuPixel *dst, void *src, uint32_t cnt)
-{
-    uint16_t *src16 = src;
-
-    while (cnt > 0) {
-        dst->red   = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
-        dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
-        dst->blue  = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
-        dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits);
-        dst++, src16++, cnt--;
-    }
-}
-
-/* assumes pf->{r,g,b,a}bits == 8 */
-static void conv_32_to_pixel_fast(PixelFormat *pf,
-                                  QemuPixel *dst, void *src, uint32_t cnt)
-{
-    uint32_t *src32 = src;
-
-    while (cnt > 0) {
-        dst->red   = (*src32 & pf->rmask) >> pf->rshift;
-        dst->green = (*src32 & pf->gmask) >> pf->gshift;
-        dst->blue  = (*src32 & pf->bmask) >> pf->bshift;
-        dst->alpha = (*src32 & pf->amask) >> pf->ashift;
-        dst++, src32++, cnt--;
-    }
-}
-
-static void conv_32_to_pixel_generic(PixelFormat *pf,
-                                     QemuPixel *dst, void *src, uint32_t cnt)
-{
-    uint32_t *src32 = src;
-
-    while (cnt > 0) {
-        if (pf->rbits < 8) {
-            dst->red   = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits);
-        } else {
-            dst->red   = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8);
-        }
-        if (pf->gbits < 8) {
-            dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits);
-        } else {
-            dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8);
-        }
-        if (pf->bbits < 8) {
-            dst->blue  = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits);
-        } else {
-            dst->blue  = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8);
-        }
-        if (pf->abits < 8) {
-            dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits);
-        } else {
-            dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8);
-        }
-        dst++, src32++, cnt--;
-    }
-}
-
-/* ----------------------------------------------------------------------- */
-/* QemuPixel -> PixelFormat conversions                                    */
-
-static void conv_pixel_to_16(PixelFormat *pf,
-                             void *dst, QemuPixel *src, uint32_t cnt)
-{
-    uint16_t *dst16 = dst;
-
-    while (cnt > 0) {
-        *dst16  = ((uint16_t)src->red   >> (8 - pf->rbits)) << pf->rshift;
-        *dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift;
-        *dst16 |= ((uint16_t)src->blue  >> (8 - pf->bbits)) << pf->bshift;
-        *dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
-        dst16++, src++, cnt--;
-    }
-}
-
-static void conv_pixel_to_32(PixelFormat *pf,
-                             void *dst, QemuPixel *src, uint32_t cnt)
-{
-    uint32_t *dst32 = dst;
-
-    while (cnt > 0) {
-        *dst32  = ((uint32_t)src->red   >> (8 - pf->rbits)) << pf->rshift;
-        *dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift;
-        *dst32 |= ((uint32_t)src->blue  >> (8 - pf->bbits)) << pf->bshift;
-        *dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift;
-        dst32++, src++, cnt--;
-    }
-}
-
-/* ----------------------------------------------------------------------- */
-/* PixelFormat -> PixelFormat conversions                                  */
-
-static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
-{
-    uint32_t bytes = cnt * conv->src.bytes_per_pixel;
-    memcpy(dst, src, bytes);
-}
-
-static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
-{
-    if (conv->conv_cnt < cnt) {
-        conv->conv_cnt = cnt;
-        conv->conv_buf = g_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt);
-    }
-    conv->conv_from(&conv->src, conv->conv_buf, src, cnt);
-    conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt);
-}
-
-/* ----------------------------------------------------------------------- */
-/* public interface                                                        */
-
-QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src)
-{
-    QemuPfConv *conv = g_malloc0(sizeof(QemuPfConv));
-
-    conv->src = *src;
-    conv->dst = *dst;
-
-    if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) {
-        /* formats identical, can simply copy */
-        conv->convert = convert_copy;
-    } else {
-        /* generic two-step conversion: src -> QemuPixel -> dst  */
-        switch (conv->src.bytes_per_pixel) {
-        case 2:
-            conv->conv_from = conv_16_to_pixel;
-            break;
-        case 4:
-            if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) {
-                conv->conv_from = conv_32_to_pixel_fast;
-            } else {
-                conv->conv_from = conv_32_to_pixel_generic;
-            }
-            break;
-        default:
-            goto err;
-        }
-        switch (conv->dst.bytes_per_pixel) {
-        case 2:
-            conv->conv_to = conv_pixel_to_16;
-            break;
-        case 4:
-            conv->conv_to = conv_pixel_to_32;
-            break;
-        default:
-            goto err;
-        }
-        conv->convert = convert_generic;
-    }
-    return conv;
-
-err:
-    g_free(conv);
-    return NULL;
-}
-
-void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt)
-{
-    conv->convert(conv, dst, src, cnt);
-}
-
-void qemu_pf_conv_put(QemuPfConv *conv)
-{
-    if (conv) {
-        g_free(conv->conv_buf);
-        g_free(conv);
-    }
-}
diff --git a/pflib.h b/pflib.h
deleted file mode 100644
index b70c313..0000000
--- a/pflib.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef __QEMU_PFLIB_H
-#define __QEMU_PFLIB_H
-
-/*
- * PixelFormat conversion library.
- *
- * Author: Gerd Hoffmann <kraxel at redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- */
-
-typedef struct QemuPfConv QemuPfConv;
-
-QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src);
-void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt);
-void qemu_pf_conv_put(QemuPfConv *conv);
-
-#endif
commit d9a86569ca3617a495ffb352e9a390747eaa6b24
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Nov 2 09:12:49 2012 +0100

    spice: switch to pixman
    
    Switch over spice-display.c to use the pixman library
    instead of the home-grown pflib bits.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/console.h b/console.h
index 70c9a55..50a0512 100644
--- a/console.h
+++ b/console.h
@@ -377,6 +377,11 @@ static inline pixman_format_code_t ds_get_format(DisplayState *ds)
     return ds->surface->format;
 }
 
+static inline pixman_image_t *ds_get_image(DisplayState *ds)
+{
+    return ds->surface->image;
+}
+
 static inline int ds_get_depth(DisplayState *ds)
 {
     return ds->surface->pf.depth;
diff --git a/qemu-pixman.c b/qemu-pixman.c
index 7547ed7..71a9ea4 100644
--- a/qemu-pixman.c
+++ b/qemu-pixman.c
@@ -51,6 +51,19 @@ void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
                            0, y, 0, 0, 0, 0, width, 1);
 }
 
+pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
+                                          pixman_image_t *image)
+{
+    pixman_image_t *mirror;
+
+    mirror = pixman_image_create_bits(format,
+                                      pixman_image_get_width(image),
+                                      pixman_image_get_height(image),
+                                      NULL,
+                                      pixman_image_get_stride(image));
+    return mirror;
+}
+
 void qemu_pixman_image_unref(pixman_image_t *image)
 {
     if (image == NULL) {
diff --git a/qemu-pixman.h b/qemu-pixman.h
index 7652c41..e267d73 100644
--- a/qemu-pixman.h
+++ b/qemu-pixman.h
@@ -27,6 +27,8 @@ pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format,
                                            int width);
 void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb,
                               int width, int y);
+pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
+                                          pixman_image_t *image);
 void qemu_pixman_image_unref(pixman_image_t *image);
 
 #endif /* QEMU_PIXMAN_H */
diff --git a/ui/spice-display.c b/ui/spice-display.c
index fb99148..fe2fdfb 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -150,9 +150,9 @@ static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
     QXLDrawable *drawable;
     QXLImage *image;
     QXLCommand *cmd;
-    uint8_t *src, *mirror, *dst;
-    int by, bw, bh, offset, bytes;
+    int bw, bh;
     struct timespec time_space;
+    pixman_image_t *dest;
 
     trace_qemu_spice_create_update(
            rect->left, rect->right,
@@ -195,20 +195,15 @@ static void qemu_spice_create_one_update(SimpleSpiceDisplay *ssd,
     image->bitmap.palette = 0;
     image->bitmap.format = SPICE_BITMAP_FMT_32BIT;
 
-    offset =
-        rect->top * ds_get_linesize(ssd->ds) +
-        rect->left * ds_get_bytes_per_pixel(ssd->ds);
-    bytes = ds_get_bytes_per_pixel(ssd->ds) * bw;
-    src = ds_get_data(ssd->ds) + offset;
-    mirror = ssd->ds_mirror + offset;
-    dst = update->bitmap;
-    for (by = 0; by < bh; by++) {
-        memcpy(mirror, src, bytes);
-        qemu_pf_conv_run(ssd->conv, dst, mirror, bw);
-        src += ds_get_linesize(ssd->ds);
-        mirror += ds_get_linesize(ssd->ds);
-        dst += image->bitmap.stride;
-    }
+    dest = pixman_image_create_bits(PIXMAN_x8r8g8b8, bw, bh,
+                                    (void *)update->bitmap, bw * 4);
+    pixman_image_composite(PIXMAN_OP_SRC, ssd->surface, NULL, ssd->mirror,
+                           rect->left, rect->top, 0, 0,
+                           rect->left, rect->top, bw, bh);
+    pixman_image_composite(PIXMAN_OP_SRC, ssd->mirror, NULL, dest,
+                           rect->left, rect->top, 0, 0,
+                           0, 0, bw, bh);
+    pixman_image_unref(dest);
 
     cmd->type = QXL_CMD_DRAW;
     cmd->data = (uintptr_t)drawable;
@@ -229,14 +224,10 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
         return;
     };
 
-    if (ssd->conv == NULL) {
-        PixelFormat dst = qemu_default_pixelformat(32);
-        ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf);
-        assert(ssd->conv);
-    }
-    if (ssd->ds_mirror == NULL) {
-        int size = ds_get_height(ssd->ds) * ds_get_linesize(ssd->ds);
-        ssd->ds_mirror = g_malloc0(size);
+    if (ssd->surface == NULL) {
+        ssd->surface = pixman_image_ref(ds_get_image(ssd->ds));
+        ssd->mirror  = qemu_pixman_mirror_create(ds_get_format(ssd->ds),
+                                                 ds_get_image(ssd->ds));
     }
 
     for (blk = 0; blk < blocks; blk++) {
@@ -244,7 +235,7 @@ static void qemu_spice_create_update(SimpleSpiceDisplay *ssd)
     }
 
     guest = ds_get_data(ssd->ds);
-    mirror = ssd->ds_mirror;
+    mirror = (void *)pixman_image_get_data(ssd->mirror);
     for (y = ssd->dirty.top; y < ssd->dirty.bottom; y++) {
         yoff = y * ds_get_linesize(ssd->ds);
         for (x = ssd->dirty.left; x < ssd->dirty.right; x += blksize) {
@@ -383,10 +374,12 @@ void qemu_spice_display_resize(SimpleSpiceDisplay *ssd)
     dprint(1, "%s:\n", __FUNCTION__);
 
     memset(&ssd->dirty, 0, sizeof(ssd->dirty));
-    qemu_pf_conv_put(ssd->conv);
-    ssd->conv = NULL;
-    g_free(ssd->ds_mirror);
-    ssd->ds_mirror = NULL;
+    if (ssd->surface) {
+        pixman_image_unref(ssd->surface);
+        ssd->surface = NULL;
+        pixman_image_unref(ssd->mirror);
+        ssd->mirror = NULL;
+    }
 
     qemu_mutex_lock(&ssd->lock);
     while ((update = QTAILQ_FIRST(&ssd->updates)) != NULL) {
diff --git a/ui/spice-display.h b/ui/spice-display.h
index d766927..38b6ea9 100644
--- a/ui/spice-display.h
+++ b/ui/spice-display.h
@@ -20,8 +20,7 @@
 #include <spice/qxl_dev.h>
 
 #include "qemu-thread.h"
-#include "console.h"
-#include "pflib.h"
+#include "qemu-pixman.h"
 #include "sysemu.h"
 
 #define NUM_MEMSLOTS 8
@@ -72,13 +71,13 @@ typedef struct SimpleSpiceUpdate SimpleSpiceUpdate;
 
 struct SimpleSpiceDisplay {
     DisplayState *ds;
-    uint8_t *ds_mirror;
     void *buf;
     int bufsize;
     QXLWorker *worker;
     QXLInstance qxl;
     uint32_t unique;
-    QemuPfConv *conv;
+    pixman_image_t *surface;
+    pixman_image_t *mirror;
     int32_t num_surfaces;
 
     QXLRect dirty;
commit e32c25b5f2452c7fed4dbe8962f4a9f4831fbe24
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Oct 30 14:55:12 2012 +0100

    qxl: call dpy_gfx_resize when entering vga mode
    
    When entering vga mode the display size likely changes,
    notify all displaychangelisteners about this.
    
    Probably went unnoticed for a while as one if the first
    things the guest does after leaving qxl native mode and
    entering qxl vga mode is to set the vga video mode.  But
    there is still a small window where qemu can operate on
    stale data, leading to crashes now and then.
    
    https://bugzilla.redhat.com/show_bug.cgi?id=865767
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/qxl.c b/hw/qxl.c
index 063970d..e7bf3a1 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -1069,7 +1069,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d)
     trace_qxl_enter_vga_mode(d->id);
     qemu_spice_create_host_primary(&d->ssd);
     d->mode = QXL_MODE_VGA;
-    memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty));
+    dpy_gfx_resize(d->ssd.ds);
     vga_dirty_log_start(&d->vga);
 }
 
commit 958c2bceba06696e9c223498aaf81d06ce95f608
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Sep 14 22:17:44 2012 +0200

    qxl: fix cursor reset
    
    When resetting the qxl cursor notify the qemu displaystate too.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/qxl.c b/hw/qxl.c
index f19451b..063970d 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -293,6 +293,10 @@ void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
     qemu_mutex_lock(&qxl->track_lock);
     qxl->guest_cursor = 0;
     qemu_mutex_unlock(&qxl->track_lock);
+    if (qxl->ssd.cursor) {
+        cursor_put(qxl->ssd.cursor);
+    }
+    qxl->ssd.cursor = cursor_builtin_hidden();
 }
 
 
commit 511aefb0c60e3063ead76d4ba6aabf619eed18ef
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Nov 1 14:56:00 2012 +0200

    hw/qxl: qxl_send_events: nop if stopped
    
    Added a trace point for easy logging.
    
    RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=870972
    
    Signed-off-by: Alon Levy <alevy at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/qxl.c b/hw/qxl.c
index cc16eaf..f19451b 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -1701,7 +1701,13 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
     uint32_t le_events = cpu_to_le32(events);
 
     trace_qxl_send_events(d->id, events);
-    assert(qemu_spice_display_is_running(&d->ssd));
+    if (!qemu_spice_display_is_running(&d->ssd)) {
+        /* spice-server tracks guest running state and should not do this */
+        fprintf(stderr, "%s: spice-server bug: guest stopped, ignoring\n",
+                __func__);
+        trace_qxl_send_events_vm_stopped(d->id, events);
+        return;
+    }
     old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events);
     if ((old_pending & le_events) == le_events) {
         return;
diff --git a/trace-events b/trace-events
index b84d631..e1a37cc 100644
--- a/trace-events
+++ b/trace-events
@@ -1001,6 +1001,7 @@ qxl_spice_update_area(int qid, uint32_t surface_id, uint32_t left, uint32_t righ
 qxl_spice_update_area_rest(int qid, uint32_t num_dirty_rects, uint32_t clear_dirty_region) "%d #d=%d clear=%d"
 qxl_surfaces_dirty(int qid, int surface, int offset, int size) "%d surface=%d offset=%d size=%d"
 qxl_send_events(int qid, uint32_t events) "%d %d"
+qxl_send_events_vm_stopped(int qid, uint32_t events) "%d %d"
 qxl_set_guest_bug(int qid) "%d"
 qxl_interrupt_client_monitors_config(int qid, int num_heads, void *heads) "%d %d %p"
 qxl_client_monitors_config_unsupported_by_guest(int qid, uint32_t int_mask, void *client_monitors_config) "%d %X %p"
commit 48f4ba671bbb3dd212002d57b72a23375f51619b
Author: Alon Levy <alevy at redhat.com>
Date:   Mon Oct 15 14:54:03 2012 +0200

    hw/qxl: guest bug on primary create with stride %4 != 0
    
    Due to usage of pixman for rendering on all spice surfaces we have
    pixman's requirement that the stride be word aligned. A guest not
    honoring that can crash spice and qemu with it due to failure to create
    a surface (in spice-server). Avoid this early on in primary surface
    creation and offscreen surface creation.
    
    Recently windows guests got odd width support which triggers a non word
    aligned primary surface in 16bit color depth. Off screen surfaces have
    always been word aligned, but doesn't hurt to check them here too.
    
    Signed-off-by: Alon Levy <alevy at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/qxl.c b/hw/qxl.c
index 1f56fcd..cc16eaf 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -447,6 +447,12 @@ static int qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
                               qxl->ssd.num_surfaces);
             return 1;
         }
+        if (cmd->type == QXL_SURFACE_CMD_CREATE &&
+            (cmd->u.surface_create.stride & 0x03) != 0) {
+            qxl_set_guest_bug(qxl, "QXL_CMD_SURFACE stride = %d %% 4 != 0\n",
+                              cmd->u.surface_create.stride);
+            return 1;
+        }
         qemu_mutex_lock(&qxl->track_lock);
         if (cmd->type == QXL_SURFACE_CMD_CREATE) {
             qxl->guest_surfaces.cmds[id] = ext->cmd.data;
@@ -1357,6 +1363,12 @@ static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm,
     trace_qxl_create_guest_primary_rest(qxl->id, sc->stride, sc->type,
                                         sc->flags);
 
+    if ((surface.stride & 0x3) != 0) {
+        qxl_set_guest_bug(qxl, "primary surface stride = %d %% 4 != 0",
+                          surface.stride);
+        return;
+    }
+
     surface.mouse_mode = true;
     surface.group_id   = MEMSLOT_GROUP_GUEST;
     if (loadvm) {


More information about the Spice-commits mailing list