[Spice-commits] 80 commits - backends/rng-random.c configure cpus.c hw/arm hw/block hw/char hw/core hw/display hw/dma hw/i386 hw/intc hw/microblaze hw/net hw/unicore32 hw/usb include/hw include/sysemu include/ui kvm-all.c kvm-stub.c qdev-monitor.c qom/object.c spice-qemu-char.c target-cris/cpu-qom.h target-cris/cpu.c target-cris/helper.c target-i386/cpu.c target-i386/cpu.h trace-events ui/console.c ui/curses.c ui/gtk.c ui/qemu-pixman.c ui/sdl.c ui/spice-display.c ui/vnc.c ui/vnc.h util/oslib-posix.c vl.c

Gerd Hoffmann kraxel at kemper.freedesktop.org
Thu Apr 18 00:13:37 PDT 2013


 backends/rng-random.c               |    3 
 configure                           |   36 
 cpus.c                              |    4 
 hw/arm/musicpal.c                   |    8 
 hw/block/m25p80.c                   |   52 -
 hw/char/virtio-console.c            |   32 
 hw/core/qdev-properties.c           |   33 
 hw/core/qdev.c                      |    8 
 hw/core/stream.c                    |   15 
 hw/display/blizzard.c               |   21 
 hw/display/cirrus_vga.c             |   10 
 hw/display/exynos4210_fimd.c        |   15 
 hw/display/g364fb.c                 |   80 -
 hw/display/jazz_led.c               |   11 
 hw/display/milkymist-vgafb.c        |    9 
 hw/display/omap_lcdc.c              |   93 --
 hw/display/pl110.c                  |    9 
 hw/display/pxa2xx_lcd.c             |    9 
 hw/display/qxl.c                    |   50 -
 hw/display/sm501.c                  |    7 
 hw/display/ssd0303.c                |    9 
 hw/display/ssd0323.c                |    9 
 hw/display/tc6393xb.c               |   10 
 hw/display/tcx.c                    |  143 ---
 hw/display/vga-isa-mm.c             |    4 
 hw/display/vga-isa.c                |    3 
 hw/display/vga-pci.c                |    3 
 hw/display/vga.c                    |   76 -
 hw/display/vga_int.h                |    6 
 hw/display/vmware_vga.c             |  198 ++--
 hw/display/xenfb.c                  |   67 -
 hw/dma/xilinx_axidma.c              |  261 +++++-
 hw/i386/kvmvapic.c                  |    7 
 hw/intc/ioapic_common.c             |    2 
 hw/microblaze/petalogix_ml605_mmu.c |   28 
 hw/net/xilinx_axienet.c             |  255 +++++-
 hw/unicore32/puv3.c                 |    4 
 hw/usb/dev-serial.c                 |    9 
 hw/usb/hcd-xhci.c                   |   79 +
 hw/usb/host-libusb.c                | 1449 ++++++++++++++++++++++++++++++++++++
 hw/usb/host-linux.c                 |   14 
 include/hw/qdev-properties.h        |   10 
 include/hw/stream.h                 |   36 
 include/hw/virtio/virtio-serial.h   |    2 
 include/hw/xilinx.h                 |   21 
 include/sysemu/kvm.h                |   12 
 include/ui/console.h                |   55 -
 include/ui/qemu-pixman.h            |    9 
 include/ui/spice-display.h          |    1 
 kvm-all.c                           |    8 
 kvm-stub.c                          |    4 
 qdev-monitor.c                      |   16 
 qom/object.c                        |    4 
 spice-qemu-char.c                   |  109 +-
 target-cris/cpu-qom.h               |    1 
 target-cris/cpu.c                   |    8 
 target-cris/helper.c                |   14 
 target-i386/cpu.c                   |   89 +-
 target-i386/cpu.h                   |    1 
 trace-events                        |   16 
 ui/console.c                        |  676 +++++++++-------
 ui/curses.c                         |   11 
 ui/gtk.c                            |   36 
 ui/qemu-pixman.c                    |   54 +
 ui/sdl.c                            |   52 -
 ui/spice-display.c                  |   11 
 ui/vnc.c                            |   87 --
 ui/vnc.h                            |    2 
 util/oslib-posix.c                  |   35 
 vl.c                                |   55 -
 70 files changed, 3177 insertions(+), 1399 deletions(-)

New commits:
commit e0a83fc2c1582dc8d4453849852ebe6c258b7c3a
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Tue Apr 2 15:50:00 2013 +0200

    qom: do nothing on unparent of object without parent
    
    Otherwise, device_unparent will fail to get a canonical path of
    the object.
    
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
    Message-id: 1364910600-3418-1-git-send-email-pbonzini at redhat.com
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

diff --git a/qom/object.c b/qom/object.c
index 8818149..093502e 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -362,6 +362,10 @@ static void object_property_del_child(Object *obj, Object *child, Error **errp)
 
 void object_unparent(Object *obj)
 {
+    if (!obj->parent) {
+        return;
+    }
+
     object_ref(obj);
     if (obj->class->unparent) {
         (obj->class->unparent)(obj);
commit 2f7bd829dbaf39eb14706c3e889f28b795e050ca
Author: Andreas Färber <afaerber at suse.de>
Date:   Tue Apr 16 03:50:21 2013 +0200

    qdev: Fix device_add bus assumptions
    
    Drop an unreachable fallback bus assignment to SysBus.
    
    If no ,bus= is specified, only search busses recursively for bus type if
    the DeviceClass has a bus_type specified. Handle resulting NULL cases.
    
    Signed-off-by: Andreas Färber <afaerber at suse.de>
    Signed-off-by: Andreas Färber <afaerber at suse.de>
    Message-id: 1366077021-28882-1-git-send-email-afaerber at suse.de
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

diff --git a/qdev-monitor.c b/qdev-monitor.c
index 9a78ccf..73d7946 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -18,6 +18,7 @@
  */
 
 #include "hw/qdev.h"
+#include "hw/sysbus.h"
 #include "monitor/monitor.h"
 #include "monitor/qdev.h"
 #include "qmp-commands.h"
@@ -415,7 +416,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
     DeviceClass *k;
     const char *driver, *path, *id;
     DeviceState *qdev;
-    BusState *bus;
+    BusState *bus = NULL;
 
     driver = qemu_opt_get(opts, "driver");
     if (!driver) {
@@ -453,7 +454,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
                           driver, object_get_typename(OBJECT(bus)));
             return NULL;
         }
-    } else {
+    } else if (k->bus_type != NULL) {
         bus = qbus_find_recursive(sysbus_get_default(), NULL, k->bus_type);
         if (!bus) {
             qerror_report(QERR_NO_BUS_FOR_DEVICE,
@@ -461,18 +462,17 @@ DeviceState *qdev_device_add(QemuOpts *opts)
             return NULL;
         }
     }
-    if (qdev_hotplug && !bus->allow_hotplug) {
+    if (qdev_hotplug && bus && !bus->allow_hotplug) {
         qerror_report(QERR_BUS_NO_HOTPLUG, bus->name);
         return NULL;
     }
 
-    if (!bus) {
-        bus = sysbus_get_default();
-    }
-
     /* create device, set properties */
     qdev = DEVICE(object_new(driver));
-    qdev_set_parent_bus(qdev, bus);
+
+    if (bus) {
+        qdev_set_parent_bus(qdev, bus);
+    }
 
     id = qemu_opts_id(opts);
     if (id) {
commit acbbc036619092fcd2c882222e1be168bd972b3e
Author: Amit Shah <amit.shah at redhat.com>
Date:   Tue Apr 16 15:58:16 2013 +0530

    rng random backend: check for -EAGAIN errors on read
    
    Not handling EAGAIN triggers the assert
    
    qemu/backends/rng-random.c:44:entropy_available: assertion failed: (len != -1)
    Aborted (core dumped)
    
    This happens when starting a guest with '-device virtio-rng-pci',
    issuing a 'cat /dev/hwrng' in the guest, while also doing 'cat
    /dev/random' on the host.
    
    Reported-by: yunpingzheng <yunzheng at redhat.com>
    Signed-off-by: Amit Shah <amit.shah at redhat.com>
    Message-id: eacda84dfaf2d99cf6d250b678be4e4d6c2088fb.1366108096.git.amit.shah at redhat.com
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

diff --git a/backends/rng-random.c b/backends/rng-random.c
index d5761f2..830360c 100644
--- a/backends/rng-random.c
+++ b/backends/rng-random.c
@@ -41,6 +41,9 @@ static void entropy_available(void *opaque)
     ssize_t len;
 
     len = read(s->fd, buffer, s->size);
+    if (len < 0 && errno == EAGAIN) {
+        return;
+    }
     g_assert(len != -1);
 
     s->receive_func(s->opaque, buffer, len);
commit 7dda5dc82a776a39a7996020c188eb2a29187117
Author: Paolo Bonzini <pbonzini at redhat.com>
Date:   Tue Apr 9 17:43:43 2013 +0200

    migration: initialize RAM to zero
    
    Using qemu_memalign only leaves the RAM zero by chance, because libc
    will usually use mmap to satisfy our huge requests.  But memory will
    not be zero when using MALLOC_PERTURB_ with a nonzero value.  In the
    case of incoming migration, this breaks a recently-introduced
    invariant (commit f1c7279, migration: do not sent zero pages in
    bulk stage, 2013-03-26).
    
    To fix this, use mmap ourselves to get a well-aligned, always zero
    block for the RAM.  Mmap-ed memory is easy to "trim" at the sides.
    
    This also removes the need to do something special on valgrind
    (see commit c2a8238a, Support running QEMU on Valgrind, 2011-10-31),
    thus effectively reverts that patch.
    
    Reviewed-by: Juan Quintela <quintela at redhat.com>
    Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
    Reviewed-by: Markus Armbruster <armbru at redhat.com>
    Message-id: 1365522223-20153-1-git-send-email-pbonzini at redhat.com
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 4e4b819..3efc763 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -40,7 +40,6 @@ extern int daemon(int, int);
       Valgrind does not support alignments larger than 1 MiB,
       therefore we need special code which handles running on Valgrind. */
 #  define QEMU_VMALLOC_ALIGN (512 * 4096)
-#  define CONFIG_VALGRIND
 #elif defined(__linux__) && defined(__s390x__)
    /* Use 1 MiB (segment size) alignment so gmap can be used by KVM. */
 #  define QEMU_VMALLOC_ALIGN (256 * 4096)
@@ -52,12 +51,8 @@ extern int daemon(int, int);
 #include "sysemu/sysemu.h"
 #include "trace.h"
 #include "qemu/sockets.h"
+#include <sys/mman.h>
 
-#if defined(CONFIG_VALGRIND)
-static int running_on_valgrind = -1;
-#else
-#  define running_on_valgrind 0
-#endif
 #ifdef CONFIG_LINUX
 #include <sys/syscall.h>
 #endif
@@ -108,22 +103,28 @@ void *qemu_memalign(size_t alignment, size_t size)
 /* alloc shared memory pages */
 void *qemu_vmalloc(size_t size)
 {
-    void *ptr;
     size_t align = QEMU_VMALLOC_ALIGN;
+    size_t total = size + align - getpagesize();
+    void *ptr = mmap(0, total, PROT_READ | PROT_WRITE,
+                     MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+    size_t offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr;
 
-#if defined(CONFIG_VALGRIND)
-    if (running_on_valgrind < 0) {
-        /* First call, test whether we are running on Valgrind.
-           This is a substitute for RUNNING_ON_VALGRIND from valgrind.h. */
-        const char *ld = getenv("LD_PRELOAD");
-        running_on_valgrind = (ld != NULL && strstr(ld, "vgpreload"));
+    if (ptr == MAP_FAILED) {
+        fprintf(stderr, "Failed to allocate %zu B: %s\n",
+                size, strerror(errno));
+        abort();
     }
-#endif
 
-    if (size < align || running_on_valgrind) {
-        align = getpagesize();
+    ptr += offset;
+    total -= offset;
+
+    if (offset > 0) {
+        munmap(ptr - offset, offset);
     }
-    ptr = qemu_memalign(align, size);
+    if (total > size) {
+        munmap(ptr + size, total - size);
+    }
+
     trace_qemu_vmalloc(size, ptr);
     return ptr;
 }
commit 86c7dba0d0ed1e9e202f77f7414ce0faf2395a90
Merge: 6f8111a 2b2325f
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Tue Apr 16 10:28:58 2013 -0500

    Merge remote-tracking branch 'kraxel/usb.80' into staging
    
    # By Gerd Hoffmann (6) and Hans de Goede (1)
    # Via Gerd Hoffmann
    * kraxel/usb.80:
      use libusb for usb-host
      xhci: fix address device
      xhci: use slotid as device address
      xhci: fix portsc writes
      xhci: add xhci_cap_write
      xhci: remove leftover debug printf
      usb-serial: Remove double call to qemu_chr_add_handlers( NULL )
    
    Message-id: 1366107190-30853-1-git-send-email-kraxel at redhat.com
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit 6f8111a16d9cb3744a7b05726df28ee8cb6d8d30
Merge: 100c533 75c439b
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Tue Apr 16 10:28:51 2013 -0500

    Merge remote-tracking branch 'spice/spice.v69' into staging
    
    # By Hans de Goede (5) and others
    # Via Gerd Hoffmann
    * spice/spice.v69:
      spice-qemu-char: vmc_write: Don't write more bytes then we're asked too
      spice-qemu-char: Remove intermediate buffer
      spice-qemu-char: Add watch support
      spice-qemu-char: Remove #ifdef-ed code for old spice-server compat
      virtio-console: Remove any pending watches on close
      virtio-console: Also throttle when less was written then requested
      spice: (32 bit only) fix surface cmd tracking destruction
      qxl: add 2000x2000 and 2048x2048 video modes
      qxl: add 4k + 8k resolutions
    
    Message-id: 1366106194-28826-1-git-send-email-kraxel at redhat.com
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit 100c533220d70ae9732ba63142d71d1c48688f54
Merge: 398973f bfe528b
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Tue Apr 16 10:28:43 2013 -0500

    Merge remote-tracking branch 'kraxel/pixman.v11' into staging
    
    # By Gerd Hoffmann (22) and Igor Mitsyanko (2)
    # Via Gerd Hoffmann
    * kraxel/pixman.v11: (24 commits)
      qxl: register QemuConsole for secondary cards
      gtk: custom cursor support
      console: allow pinning displaychangelisteners to consoles
      console: add qemu_console_is_*
      xen: re-enable refresh interval reporting for xenfb
      console: gui timer fixes
      console: add GraphicHwOps
      console: make DisplayState private to console.c
      console: move gui_update+gui_setup_refresh from vl.c into console.c
      console: zap g_width + g_height
      console: simplify screendump
      console: give each QemuConsole its own DisplaySurface
      console: rename vga_hw_*, add QemuConsole param
      console: displaystate init revamp
      console: add trace events
      console: switch color_table_rgb to pixman_color_t
      console: use pixman for font rendering
      console: use pixman for fill+blit
      pixman: render vgafont glyphs into pixman images
      pixman: add qemu_pixman_color()
      ...
    
    Message-id: 1366105178-26744-1-git-send-email-kraxel at redhat.com
    Signed-off-by: Anthony Liguori <aliguori at us.ibm.com>

commit 398973fe1f92e65f39f6a26dacc07baa0da632fc
Merge: 095b9c4 b21bfee
Author: Anthony Liguori <aliguori at us.ibm.com>
Date:   Tue Apr 16 10:28:36 2013 -0500

    Merge remote-tracking branch 'afaerber/qom-cpu' into staging
    
    # By Igor Mammedov (8) and others
    # Via Andreas Färber
    * afaerber/qom-cpu:
      target-cris: Override do_interrupt for pre-v32 CPU cores
      qdev: Set device's parent before calling realize() down inheritance chain
      cpu: Pass CPUState to *cpu_synchronize_post*()
      target-i386: Split out CPU creation and features parsing
      target-i386/cpu.c: Coding style fixes
      ioapic: Replace FROM_SYSBUS() with QOM type cast
      kvmvapic: Replace FROM_SYSBUS() with QOM type cast
      target-i386: Split APIC creation from initialization in x86_cpu_realizefn()
      target-i386: Consolidate error propagation in x86_cpu_realizefn()
      qdev: Add qdev property for bool type
      target-i386: Improve -cpu ? features output
      target-i386: Fix including "host" in -cpu ? output

commit 2b2325ff6491224a42e1fec99b1c39fbc521c95c
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Nov 30 16:02:11 2012 +0100

    use libusb for usb-host
    
    Reimplement usb-host on top of libusb.
    Reasons to do this:
    
     (1) Largely rewritten from scratch, nice opportunity to kill historical
         cruft.
     (2) Offload usbfs handling to libusb.
     (3) Have a single portable code base instead of bsd + linux variants.
     (4) Bring usb-host support to any platform supported by libusbx.
    
    For now this goes side-by-side to the existing code.  That is only to
    simplify regression testing though, at the end of the day I want remove
    the old code and support libusb exclusively.  Merge early in 1.5 cycle,
    remove the old code after 1.5 release or something like this.
    
    Thanks to qdev the old and new code can coexist nicely on linux.  Just
    use "-device usb-host-linux" to use the old linux driver instead of the
    libusb one (which takes over the "usb-host" name).
    
    The bsd driver isn't qdev'ified so it isn't that easy for bsd.
    I didn't bother making it runtime switchable, so you have to rebuild
    qemu with --disable-libusb to get back the old code.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/configure b/configure
index 0788e27..4c4f6f6 100755
--- a/configure
+++ b/configure
@@ -226,6 +226,7 @@ trace_file="trace"
 spice=""
 rbd=""
 smartcard_nss=""
+libusb=""
 usb_redir=""
 glx=""
 zlib="yes"
@@ -890,6 +891,10 @@ for opt do
   ;;
   --enable-smartcard-nss) smartcard_nss="yes"
   ;;
+  --disable-libusb) libusb="no"
+  ;;
+  --enable-libusb) libusb="yes"
+  ;;
   --disable-usb-redir) usb_redir="no"
   ;;
   --enable-usb-redir) usb_redir="yes"
@@ -1175,6 +1180,8 @@ echo "  --disable-libiscsi       disable iscsi support"
 echo "  --enable-libiscsi        enable iscsi support"
 echo "  --disable-smartcard-nss  disable smartcard nss support"
 echo "  --enable-smartcard-nss   enable smartcard nss support"
+echo "  --disable-libusb         disable libusb (for usb passthrough)"
+echo "  --enable-libusb          enable libusb (for usb passthrough)"
 echo "  --disable-usb-redir      disable usb network redirection support"
 echo "  --enable-usb-redir       enable usb network redirection support"
 echo "  --disable-guest-agent    disable building of the QEMU Guest Agent"
@@ -3005,6 +3012,23 @@ EOF
     fi
 fi
 
+# check for libusb
+if test "$libusb" != "no" ; then
+    if $pkg_config libusb-1.0 >/dev/null 2>&1 ; then
+        libusb="yes"
+	usb="libusb"
+        libusb_cflags=$($pkg_config --cflags libusb-1.0 2>/dev/null)
+        libusb_libs=$($pkg_config --libs libusb-1.0 2>/dev/null)
+        QEMU_CFLAGS="$QEMU_CFLAGS $libusb_cflags"
+        libs_softmmu="$libs_softmmu $libusb_libs"
+    else
+        if test "$libusb" = "yes"; then
+            feature_not_found "libusb"
+        fi
+        libusb="no"
+    fi
+fi
+
 # check for usbredirparser for usb network redirection support
 if test "$usb_redir" != "no" ; then
     if $pkg_config --atleast-version=0.6 libusbredirparser-0.5 >/dev/null 2>&1 ; then
@@ -3516,6 +3540,7 @@ echo "spice support     $spice ($spice_protocol_version/$spice_server_version)"
 echo "rbd support       $rbd"
 echo "xfsctl support    $xfs"
 echo "nss used          $smartcard_nss"
+echo "libusb            $libusb"
 echo "usb net redir     $usb_redir"
 echo "GLX support       $glx"
 echo "libiscsi support  $libiscsi"
@@ -3823,6 +3848,10 @@ if test "$smartcard_nss" = "yes" ; then
   echo "libcacard_cflags=$libcacard_cflags" >> $config_host_mak
 fi
 
+if test "$libusb" = "yes" ; then
+  echo "CONFIG_USB_LIBUSB=y" >> $config_host_mak
+fi
+
 if test "$usb_redir" = "yes" ; then
   echo "CONFIG_USB_REDIR=y" >> $config_host_mak
 fi
@@ -3907,6 +3936,13 @@ linux)
 bsd)
   echo "HOST_USB=bsd" >> $config_host_mak
 ;;
+libusb)
+  if test "$linux" = "yes"; then
+    echo "HOST_USB=libusb linux legacy" >> $config_host_mak
+  else
+    echo "HOST_USB=libusb legacy" >> $config_host_mak
+  fi
+;;
 *)
   echo "HOST_USB=stub" >> $config_host_mak
 ;;
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
new file mode 100644
index 0000000..29f35b3
--- /dev/null
+++ b/hw/usb/host-libusb.c
@@ -0,0 +1,1449 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *      Support for host device auto connect & disconnect
+ *      Major rewrite to support fully async operation
+ *
+ * Copyright 2008 TJ <linux at tjworld.net>
+ *      Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
+ *      to the legacy /proc/bus/usb USB device discovery and handling
+ *
+ * (c) 2012 Gerd Hoffmann <kraxel at redhat.com>
+ *      Completely rewritten to use libusb instead of usbfs ioctls.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <poll.h>
+#include <libusb.h>
+
+#include "qemu-common.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
+#include "trace.h"
+
+#include "hw/usb.h"
+
+/* ------------------------------------------------------------------------ */
+
+#define TYPE_USB_HOST_DEVICE "usb-host"
+#define USB_HOST_DEVICE(obj) \
+     OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE)
+
+typedef struct USBHostDevice USBHostDevice;
+typedef struct USBHostRequest USBHostRequest;
+typedef struct USBHostIsoXfer USBHostIsoXfer;
+typedef struct USBHostIsoRing USBHostIsoRing;
+
+struct USBAutoFilter {
+    uint32_t bus_num;
+    uint32_t addr;
+    char     *port;
+    uint32_t vendor_id;
+    uint32_t product_id;
+};
+
+enum USBHostDeviceOptions {
+    USB_HOST_OPT_PIPELINE,
+};
+
+struct USBHostDevice {
+    USBDevice parent_obj;
+
+    /* properties */
+    struct USBAutoFilter             match;
+    int32_t                          bootindex;
+    uint32_t                         iso_urb_count;
+    uint32_t                         iso_urb_frames;
+    uint32_t                         options;
+    uint32_t                         loglevel;
+
+    /* state */
+    QTAILQ_ENTRY(USBHostDevice)      next;
+    int                              seen, errcount;
+    int                              bus_num;
+    int                              addr;
+    char                             port[16];
+
+    libusb_device                    *dev;
+    libusb_device_handle             *dh;
+    struct libusb_device_descriptor  ddesc;
+
+    struct {
+        bool                         detached;
+        bool                         claimed;
+    } ifs[USB_MAX_INTERFACES];
+
+    /* callbacks & friends */
+    QEMUBH                           *bh;
+    Notifier                         exit;
+
+    /* request queues */
+    QTAILQ_HEAD(, USBHostRequest)    requests;
+    QTAILQ_HEAD(, USBHostIsoRing)    isorings;
+};
+
+struct USBHostRequest {
+    USBHostDevice                    *host;
+    USBPacket                        *p;
+    bool                             in;
+    struct libusb_transfer           *xfer;
+    unsigned char                    *buffer;
+    unsigned char                    *cbuf;
+    unsigned int                     clen;
+    QTAILQ_ENTRY(USBHostRequest)     next;
+};
+
+struct USBHostIsoXfer {
+    USBHostIsoRing                   *ring;
+    struct libusb_transfer           *xfer;
+    bool                             copy_complete;
+    unsigned int                     packet;
+    QTAILQ_ENTRY(USBHostIsoXfer)     next;
+};
+
+struct USBHostIsoRing {
+    USBHostDevice                    *host;
+    USBEndpoint                      *ep;
+    QTAILQ_HEAD(, USBHostIsoXfer)    unused;
+    QTAILQ_HEAD(, USBHostIsoXfer)    inflight;
+    QTAILQ_HEAD(, USBHostIsoXfer)    copy;
+    QTAILQ_ENTRY(USBHostIsoRing)     next;
+};
+
+static QTAILQ_HEAD(, USBHostDevice) hostdevs =
+    QTAILQ_HEAD_INITIALIZER(hostdevs);
+
+static void usb_host_auto_check(void *unused);
+static void usb_host_release_interfaces(USBHostDevice *s);
+static void usb_host_nodev(USBHostDevice *s);
+static void usb_host_attach_kernel(USBHostDevice *s);
+
+/* ------------------------------------------------------------------------ */
+
+#define CONTROL_TIMEOUT  10000        /* 10 sec    */
+#define BULK_TIMEOUT         0        /* unlimited */
+#define INTR_TIMEOUT         0        /* unlimited */
+
+static const char *speed_name[] = {
+    [LIBUSB_SPEED_UNKNOWN] = "?",
+    [LIBUSB_SPEED_LOW]     = "1.5",
+    [LIBUSB_SPEED_FULL]    = "12",
+    [LIBUSB_SPEED_HIGH]    = "480",
+    [LIBUSB_SPEED_SUPER]   = "5000",
+};
+
+static const unsigned int speed_map[] = {
+    [LIBUSB_SPEED_LOW]     = USB_SPEED_LOW,
+    [LIBUSB_SPEED_FULL]    = USB_SPEED_FULL,
+    [LIBUSB_SPEED_HIGH]    = USB_SPEED_HIGH,
+    [LIBUSB_SPEED_SUPER]   = USB_SPEED_SUPER,
+};
+
+static const unsigned int status_map[] = {
+    [LIBUSB_TRANSFER_COMPLETED] = USB_RET_SUCCESS,
+    [LIBUSB_TRANSFER_ERROR]     = USB_RET_IOERROR,
+    [LIBUSB_TRANSFER_TIMED_OUT] = USB_RET_IOERROR,
+    [LIBUSB_TRANSFER_CANCELLED] = USB_RET_IOERROR,
+    [LIBUSB_TRANSFER_STALL]     = USB_RET_STALL,
+    [LIBUSB_TRANSFER_NO_DEVICE] = USB_RET_NODEV,
+    [LIBUSB_TRANSFER_OVERFLOW]  = USB_RET_BABBLE,
+};
+
+static const char *err_names[] = {
+    [-LIBUSB_ERROR_IO]               = "IO",
+    [-LIBUSB_ERROR_INVALID_PARAM]    = "INVALID_PARAM",
+    [-LIBUSB_ERROR_ACCESS]           = "ACCESS",
+    [-LIBUSB_ERROR_NO_DEVICE]        = "NO_DEVICE",
+    [-LIBUSB_ERROR_NOT_FOUND]        = "NOT_FOUND",
+    [-LIBUSB_ERROR_BUSY]             = "BUSY",
+    [-LIBUSB_ERROR_TIMEOUT]          = "TIMEOUT",
+    [-LIBUSB_ERROR_OVERFLOW]         = "OVERFLOW",
+    [-LIBUSB_ERROR_PIPE]             = "PIPE",
+    [-LIBUSB_ERROR_INTERRUPTED]      = "INTERRUPTED",
+    [-LIBUSB_ERROR_NO_MEM]           = "NO_MEM",
+    [-LIBUSB_ERROR_NOT_SUPPORTED]    = "NOT_SUPPORTED",
+    [-LIBUSB_ERROR_OTHER]            = "OTHER",
+};
+
+static libusb_context *ctx;
+static uint32_t loglevel;
+
+static void usb_host_handle_fd(void *opaque)
+{
+    struct timeval tv = { 0, 0 };
+    libusb_handle_events_timeout(ctx, &tv);
+}
+
+static void usb_host_add_fd(int fd, short events, void *user_data)
+{
+    qemu_set_fd_handler(fd,
+                        (events & POLLIN)  ? usb_host_handle_fd : NULL,
+                        (events & POLLOUT) ? usb_host_handle_fd : NULL,
+                        ctx);
+}
+
+static void usb_host_del_fd(int fd, void *user_data)
+{
+    qemu_set_fd_handler(fd, NULL, NULL, NULL);
+}
+
+static int usb_host_init(void)
+{
+    const struct libusb_pollfd **poll;
+    int i, rc;
+
+    if (ctx) {
+        return 0;
+    }
+    rc = libusb_init(&ctx);
+    if (rc != 0) {
+        return -1;
+    }
+    libusb_set_debug(ctx, loglevel);
+
+    libusb_set_pollfd_notifiers(ctx, usb_host_add_fd,
+                                usb_host_del_fd,
+                                ctx);
+    poll = libusb_get_pollfds(ctx);
+    if (poll) {
+        for (i = 0; poll[i] != NULL; i++) {
+            usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx);
+        }
+    }
+    free(poll);
+    return 0;
+}
+
+static int usb_host_get_port(libusb_device *dev, char *port, size_t len)
+{
+#if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x010000ff)
+    /* have libusb_get_port_path() */
+    uint8_t path[7];
+    size_t off;
+    int rc, i;
+
+    rc = libusb_get_port_path(ctx, dev, path, 7);
+    if (rc < 0) {
+        return 0;
+    }
+    off = snprintf(port, len, "%d", path[0]);
+    for (i = 1; i < rc; i++) {
+        off += snprintf(port+off, len-off, ".%d", path[i]);
+    }
+    return off;
+#else
+    return snprintf(port, len, "FIXME");
+#endif
+}
+
+static void usb_host_libusb_error(const char *func, int rc)
+{
+    const char *errname;
+
+    if (rc >= 0) {
+        return;
+    }
+
+    if (-rc < ARRAY_SIZE(err_names) && err_names[-rc]) {
+        errname = err_names[-rc];
+    } else {
+        errname = "?";
+    }
+    fprintf(stderr, "%s: %d [%s]\n", func, rc, errname);
+}
+
+/* ------------------------------------------------------------------------ */
+
+static bool usb_host_use_combining(USBEndpoint *ep)
+{
+    int type;
+
+    if (!ep->pipeline) {
+        return false;
+    }
+    if (ep->pid != USB_TOKEN_IN) {
+        return false;
+    }
+    type = usb_ep_get_type(ep->dev, ep->pid, ep->nr);
+    if (type != USB_ENDPOINT_XFER_BULK) {
+        return false;
+    }
+    return true;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static USBHostRequest *usb_host_req_alloc(USBHostDevice *s, USBPacket *p,
+                                          bool in, size_t bufsize)
+{
+    USBHostRequest *r = g_new0(USBHostRequest, 1);
+
+    r->host = s;
+    r->p = p;
+    r->in = in;
+    r->xfer = libusb_alloc_transfer(0);
+    if (bufsize) {
+        r->buffer = g_malloc(bufsize);
+    }
+    QTAILQ_INSERT_TAIL(&s->requests, r, next);
+    return r;
+}
+
+static void usb_host_req_free(USBHostRequest *r)
+{
+    if (r->host) {
+        QTAILQ_REMOVE(&r->host->requests, r, next);
+    }
+    libusb_free_transfer(r->xfer);
+    g_free(r->buffer);
+    g_free(r);
+}
+
+static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p)
+{
+    USBHostRequest *r;
+
+    QTAILQ_FOREACH(r, &s->requests, next) {
+        if (r->p == p) {
+            return r;
+        }
+    }
+    return NULL;
+}
+
+static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
+{
+    USBHostRequest *r = xfer->user_data;
+    USBHostDevice  *s = r->host;
+    bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
+
+    if (r->p == NULL) {
+        goto out; /* request was canceled */
+    }
+
+    r->p->status = status_map[xfer->status];
+    r->p->actual_length = xfer->actual_length;
+    if (r->in && xfer->actual_length) {
+        memcpy(r->cbuf, r->buffer + 8, xfer->actual_length);
+    }
+    trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
+                                r->p->status, r->p->actual_length);
+    usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p);
+
+out:
+    usb_host_req_free(r);
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+static void usb_host_req_complete_data(struct libusb_transfer *xfer)
+{
+    USBHostRequest *r = xfer->user_data;
+    USBHostDevice  *s = r->host;
+    bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
+
+    if (r->p == NULL) {
+        goto out; /* request was canceled */
+    }
+
+    r->p->status = status_map[xfer->status];
+    if (r->in && xfer->actual_length) {
+        usb_packet_copy(r->p, r->buffer, xfer->actual_length);
+    }
+    trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
+                                r->p->status, r->p->actual_length);
+    if (usb_host_use_combining(r->p->ep)) {
+        usb_combined_input_packet_complete(USB_DEVICE(s), r->p);
+    } else {
+        usb_packet_complete(USB_DEVICE(s), r->p);
+    }
+
+out:
+    usb_host_req_free(r);
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+static void usb_host_req_abort(USBHostRequest *r)
+{
+    USBHostDevice  *s = r->host;
+    bool inflight = (r->p && r->p->state == USB_RET_ASYNC);
+
+    if (inflight) {
+        r->p->status = USB_RET_NODEV;
+        trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
+                                    r->p->status, r->p->actual_length);
+        if (r->p->ep->nr == 0) {
+            usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p);
+        } else {
+            usb_packet_complete(USB_DEVICE(s), r->p);
+        }
+        r->p = NULL;
+    }
+
+    QTAILQ_REMOVE(&r->host->requests, r, next);
+    r->host = NULL;
+
+    if (inflight) {
+        libusb_cancel_transfer(r->xfer);
+    }
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void usb_host_req_complete_iso(struct libusb_transfer *transfer)
+{
+    USBHostIsoXfer *xfer = transfer->user_data;
+
+    if (!xfer) {
+        /* USBHostIsoXfer released while inflight */
+        g_free(transfer->buffer);
+        libusb_free_transfer(transfer);
+        return;
+    }
+
+    QTAILQ_REMOVE(&xfer->ring->inflight, xfer, next);
+    if (QTAILQ_EMPTY(&xfer->ring->inflight)) {
+        USBHostDevice *s = xfer->ring->host;
+        trace_usb_host_iso_stop(s->bus_num, s->addr, xfer->ring->ep->nr);
+    }
+    if (xfer->ring->ep->pid == USB_TOKEN_IN) {
+        QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next);
+    } else {
+        QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next);
+    }
+}
+
+static USBHostIsoRing *usb_host_iso_alloc(USBHostDevice *s, USBEndpoint *ep)
+{
+    USBHostIsoRing *ring = g_new0(USBHostIsoRing, 1);
+    USBHostIsoXfer *xfer;
+    /* FIXME: check interval (for now assume one xfer per frame) */
+    int packets = s->iso_urb_frames;
+    int i;
+
+    ring->host = s;
+    ring->ep = ep;
+    QTAILQ_INIT(&ring->unused);
+    QTAILQ_INIT(&ring->inflight);
+    QTAILQ_INIT(&ring->copy);
+    QTAILQ_INSERT_TAIL(&s->isorings, ring, next);
+
+    for (i = 0; i < s->iso_urb_count; i++) {
+        xfer = g_new0(USBHostIsoXfer, 1);
+        xfer->ring = ring;
+        xfer->xfer = libusb_alloc_transfer(packets);
+        xfer->xfer->dev_handle = s->dh;
+        xfer->xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
+
+        xfer->xfer->endpoint = ring->ep->nr;
+        if (ring->ep->pid == USB_TOKEN_IN) {
+            xfer->xfer->endpoint |= USB_DIR_IN;
+        }
+        xfer->xfer->callback = usb_host_req_complete_iso;
+        xfer->xfer->user_data = xfer;
+
+        xfer->xfer->num_iso_packets = packets;
+        xfer->xfer->length = ring->ep->max_packet_size * packets;
+        xfer->xfer->buffer = g_malloc0(xfer->xfer->length);
+
+        QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+    }
+
+    return ring;
+}
+
+static USBHostIsoRing *usb_host_iso_find(USBHostDevice *s, USBEndpoint *ep)
+{
+    USBHostIsoRing *ring;
+
+    QTAILQ_FOREACH(ring, &s->isorings, next) {
+        if (ring->ep == ep) {
+            return ring;
+        }
+    }
+    return NULL;
+}
+
+static void usb_host_iso_reset_xfer(USBHostIsoXfer *xfer)
+{
+    libusb_set_iso_packet_lengths(xfer->xfer,
+                                  xfer->ring->ep->max_packet_size);
+    xfer->packet = 0;
+    xfer->copy_complete = false;
+}
+
+static void usb_host_iso_free_xfer(USBHostIsoXfer *xfer, bool inflight)
+{
+    if (inflight) {
+        xfer->xfer->user_data = NULL;
+    } else {
+        g_free(xfer->xfer->buffer);
+        libusb_free_transfer(xfer->xfer);
+    }
+    g_free(xfer);
+}
+
+static void usb_host_iso_free(USBHostIsoRing *ring)
+{
+    USBHostIsoXfer *xfer;
+
+    while ((xfer = QTAILQ_FIRST(&ring->inflight)) != NULL) {
+        QTAILQ_REMOVE(&ring->inflight, xfer, next);
+        usb_host_iso_free_xfer(xfer, true);
+    }
+    while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) {
+        QTAILQ_REMOVE(&ring->unused, xfer, next);
+        usb_host_iso_free_xfer(xfer, false);
+    }
+    while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL) {
+        QTAILQ_REMOVE(&ring->copy, xfer, next);
+        usb_host_iso_free_xfer(xfer, false);
+    }
+
+    QTAILQ_REMOVE(&ring->host->isorings, ring, next);
+    g_free(ring);
+}
+
+static void usb_host_iso_free_all(USBHostDevice *s)
+{
+    USBHostIsoRing *ring;
+
+    while ((ring = QTAILQ_FIRST(&s->isorings)) != NULL) {
+        usb_host_iso_free(ring);
+    }
+}
+
+static bool usb_host_iso_data_copy(USBHostIsoXfer *xfer, USBPacket *p)
+{
+    unsigned int psize;
+    unsigned char *buf;
+
+    buf = libusb_get_iso_packet_buffer_simple(xfer->xfer, xfer->packet);
+    if (p->pid == USB_TOKEN_OUT) {
+        psize = p->iov.size;
+        if (psize > xfer->ring->ep->max_packet_size) {
+            /* should not happen (guest bug) */
+            psize = xfer->ring->ep->max_packet_size;
+        }
+        xfer->xfer->iso_packet_desc[xfer->packet].length = psize;
+    } else {
+        psize = xfer->xfer->iso_packet_desc[xfer->packet].actual_length;
+        if (psize > p->iov.size) {
+            /* should not happen (guest bug) */
+            psize = p->iov.size;
+        }
+    }
+    usb_packet_copy(p, buf, psize);
+    xfer->packet++;
+    xfer->copy_complete = (xfer->packet == xfer->xfer->num_iso_packets);
+    return xfer->copy_complete;
+}
+
+static void usb_host_iso_data_in(USBHostDevice *s, USBPacket *p)
+{
+    USBHostIsoRing *ring;
+    USBHostIsoXfer *xfer;
+    bool disconnect = false;
+    int rc;
+
+    ring = usb_host_iso_find(s, p->ep);
+    if (ring == NULL) {
+        ring = usb_host_iso_alloc(s, p->ep);
+    }
+
+    /* copy data to guest */
+    xfer = QTAILQ_FIRST(&ring->copy);
+    if (xfer != NULL) {
+        if (usb_host_iso_data_copy(xfer, p)) {
+            QTAILQ_REMOVE(&ring->copy, xfer, next);
+            QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+        }
+    }
+
+    /* submit empty bufs to host */
+    while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) {
+        QTAILQ_REMOVE(&ring->unused, xfer, next);
+        usb_host_iso_reset_xfer(xfer);
+        rc = libusb_submit_transfer(xfer->xfer);
+        if (rc != 0) {
+            usb_host_libusb_error("libusb_submit_transfer [iso]", rc);
+            QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+            if (rc == LIBUSB_ERROR_NO_DEVICE) {
+                disconnect = true;
+            }
+            break;
+        }
+        if (QTAILQ_EMPTY(&ring->inflight)) {
+            trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
+        }
+        QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next);
+    }
+
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
+{
+    USBHostIsoRing *ring;
+    USBHostIsoXfer *xfer;
+    bool disconnect = false;
+    int rc, filled = 0;
+
+    ring = usb_host_iso_find(s, p->ep);
+    if (ring == NULL) {
+        ring = usb_host_iso_alloc(s, p->ep);
+    }
+
+    /* copy data from guest */
+    xfer = QTAILQ_FIRST(&ring->copy);
+    while (xfer != NULL && xfer->copy_complete) {
+        filled++;
+        xfer = QTAILQ_NEXT(xfer, next);
+    }
+    if (xfer == NULL) {
+        xfer = QTAILQ_FIRST(&ring->unused);
+        if (xfer == NULL) {
+            trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, p->ep->nr);
+            return;
+        }
+        QTAILQ_REMOVE(&ring->unused, xfer, next);
+        usb_host_iso_reset_xfer(xfer);
+        QTAILQ_INSERT_TAIL(&ring->copy, xfer, next);
+    }
+    usb_host_iso_data_copy(xfer, p);
+
+    if (QTAILQ_EMPTY(&ring->inflight)) {
+        /* wait until half of our buffers are filled
+           before kicking the iso out stream */
+        if (filled*2 < s->iso_urb_count) {
+            return;
+        }
+    }
+
+    /* submit filled bufs to host */
+    while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL &&
+           xfer->copy_complete) {
+        QTAILQ_REMOVE(&ring->copy, xfer, next);
+        rc = libusb_submit_transfer(xfer->xfer);
+        if (rc != 0) {
+            usb_host_libusb_error("libusb_submit_transfer [iso]", rc);
+            QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
+            if (rc == LIBUSB_ERROR_NO_DEVICE) {
+                disconnect = true;
+            }
+            break;
+        }
+        if (QTAILQ_EMPTY(&ring->inflight)) {
+            trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
+        }
+        QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next);
+    }
+
+    if (disconnect) {
+        usb_host_nodev(s);
+    }
+}
+
+/* ------------------------------------------------------------------------ */
+
+static void usb_host_ep_update(USBHostDevice *s)
+{
+    static const char *tname[] = {
+        [USB_ENDPOINT_XFER_CONTROL] = "control",
+        [USB_ENDPOINT_XFER_ISOC]    = "isoc",
+        [USB_ENDPOINT_XFER_BULK]    = "bulk",
+        [USB_ENDPOINT_XFER_INT]     = "int",
+    };
+    USBDevice *udev = USB_DEVICE(s);
+    struct libusb_config_descriptor *conf;
+    const struct libusb_interface_descriptor *intf;
+    const struct libusb_endpoint_descriptor *endp;
+    uint8_t devep, type;
+    int pid, ep;
+    int rc, i, e;
+
+    usb_ep_reset(udev);
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return;
+    }
+    trace_usb_host_parse_config(s->bus_num, s->addr,
+                                conf->bConfigurationValue, true);
+
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        assert(udev->altsetting[i] < conf->interface[i].num_altsetting);
+        intf = &conf->interface[i].altsetting[udev->altsetting[i]];
+        trace_usb_host_parse_interface(s->bus_num, s->addr,
+                                       intf->bInterfaceNumber,
+                                       intf->bAlternateSetting, true);
+        for (e = 0; e < intf->bNumEndpoints; e++) {
+            endp = &intf->endpoint[e];
+
+            devep = endp->bEndpointAddress;
+            pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+            ep = devep & 0xf;
+            type = endp->bmAttributes & 0x3;
+
+            if (ep == 0) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "invalid endpoint address");
+                return;
+            }
+            if (usb_ep_get_type(udev, pid, ep) != USB_ENDPOINT_XFER_INVALID) {
+                trace_usb_host_parse_error(s->bus_num, s->addr,
+                                           "duplicate endpoint address");
+                return;
+            }
+
+            trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
+                                          (devep & USB_DIR_IN) ? "in" : "out",
+                                          tname[type], true);
+            usb_ep_set_max_packet_size(udev, pid, ep,
+                                       endp->wMaxPacketSize);
+            usb_ep_set_type(udev, pid, ep, type);
+            usb_ep_set_ifnum(udev, pid, ep, i);
+            usb_ep_set_halted(udev, pid, ep, 0);
+        }
+    }
+
+    libusb_free_config_descriptor(conf);
+}
+
+static int usb_host_open(USBHostDevice *s, libusb_device *dev)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    int bus_num = libusb_get_bus_number(dev);
+    int addr    = libusb_get_device_address(dev);
+    int rc;
+
+    trace_usb_host_open_started(bus_num, addr);
+
+    if (s->dh != NULL) {
+        goto fail;
+    }
+    rc = libusb_open(dev, &s->dh);
+    if (rc != 0) {
+        goto fail;
+    }
+
+    libusb_get_device_descriptor(dev, &s->ddesc);
+    s->dev     = dev;
+    s->bus_num = bus_num;
+    s->addr    = addr;
+    usb_host_get_port(s->dev, s->port, sizeof(s->port));
+
+    usb_ep_init(udev);
+    usb_host_ep_update(s);
+
+    udev->speed     = speed_map[libusb_get_device_speed(dev)];
+    udev->speedmask = (1 << udev->speed);
+#if 0
+    if (udev->speed == USB_SPEED_HIGH && usb_linux_full_speed_compat(dev)) {
+        udev->speedmask |= USB_SPEED_MASK_FULL;
+    }
+#endif
+
+    if (s->ddesc.iProduct) {
+        libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
+                                           (unsigned char *)udev->product_desc,
+                                           sizeof(udev->product_desc));
+    } else {
+        snprintf(udev->product_desc, sizeof(udev->product_desc),
+                 "host:%d.%d", bus_num, addr);
+    }
+
+    rc = usb_device_attach(udev);
+    if (rc) {
+        goto fail;
+    }
+
+    trace_usb_host_open_success(bus_num, addr);
+    return 0;
+
+fail:
+    trace_usb_host_open_failure(bus_num, addr);
+    if (s->dh != NULL) {
+        libusb_close(s->dh);
+        s->dh = NULL;
+        s->dev = NULL;
+    }
+    return -1;
+}
+
+static void usb_host_abort_xfers(USBHostDevice *s)
+{
+    USBHostRequest *r, *rtmp;
+
+    QTAILQ_FOREACH_SAFE(r, &s->requests, next, rtmp) {
+        usb_host_req_abort(r);
+    }
+}
+
+static int usb_host_close(USBHostDevice *s)
+{
+    USBDevice *udev = USB_DEVICE(s);
+
+    if (s->dh == NULL) {
+        return -1;
+    }
+
+    trace_usb_host_close(s->bus_num, s->addr);
+
+    usb_host_abort_xfers(s);
+    usb_host_iso_free_all(s);
+
+    if (udev->attached) {
+        usb_device_detach(udev);
+    }
+
+    usb_host_release_interfaces(s);
+    libusb_reset_device(s->dh);
+    usb_host_attach_kernel(s);
+    libusb_close(s->dh);
+    s->dh = NULL;
+    s->dev = NULL;
+
+    usb_host_auto_check(NULL);
+    return 0;
+}
+
+static void usb_host_nodev_bh(void *opaque)
+{
+    USBHostDevice *s = opaque;
+    usb_host_close(s);
+}
+
+static void usb_host_nodev(USBHostDevice *s)
+{
+    if (!s->bh) {
+        s->bh = qemu_bh_new(usb_host_nodev_bh, s);
+    }
+    qemu_bh_schedule(s->bh);
+}
+
+static void usb_host_exit_notifier(struct Notifier *n, void *data)
+{
+    USBHostDevice *s = container_of(n, USBHostDevice, exit);
+
+    if (s->dh) {
+        usb_host_release_interfaces(s);
+        usb_host_attach_kernel(s);
+    }
+}
+
+static int usb_host_initfn(USBDevice *udev)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+
+    loglevel = s->loglevel;
+    udev->auto_attach = 0;
+    QTAILQ_INIT(&s->requests);
+    QTAILQ_INIT(&s->isorings);
+
+    s->exit.notify = usb_host_exit_notifier;
+    qemu_add_exit_notifier(&s->exit);
+
+    QTAILQ_INSERT_TAIL(&hostdevs, s, next);
+    add_boot_device_path(s->bootindex, &udev->qdev, NULL);
+    usb_host_auto_check(NULL);
+    return 0;
+}
+
+static void usb_host_handle_destroy(USBDevice *udev)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+
+    qemu_remove_exit_notifier(&s->exit);
+    QTAILQ_REMOVE(&hostdevs, s, next);
+    usb_host_close(s);
+}
+
+static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    USBHostRequest *r;
+
+    if (p->combined) {
+        usb_combined_packet_cancel(udev, p);
+        return;
+    }
+
+    trace_usb_host_req_canceled(s->bus_num, s->addr, p);
+
+    r = usb_host_req_find(s, p);
+    if (r && r->p) {
+        r->p = NULL; /* mark as dead */
+        libusb_cancel_transfer(r->xfer);
+    }
+}
+
+static void usb_host_detach_kernel(USBHostDevice *s)
+{
+    struct libusb_config_descriptor *conf;
+    int rc, i;
+
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return;
+    }
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        rc = libusb_kernel_driver_active(s->dh, i);
+        usb_host_libusb_error("libusb_kernel_driver_active", rc);
+        if (rc != 1) {
+            continue;
+        }
+        trace_usb_host_detach_kernel(s->bus_num, s->addr, i);
+        rc = libusb_detach_kernel_driver(s->dh, i);
+        usb_host_libusb_error("libusb_detach_kernel_driver", rc);
+        s->ifs[i].detached = true;
+    }
+    libusb_free_config_descriptor(conf);
+}
+
+static void usb_host_attach_kernel(USBHostDevice *s)
+{
+    struct libusb_config_descriptor *conf;
+    int rc, i;
+
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return;
+    }
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        if (!s->ifs[i].detached) {
+            continue;
+        }
+        trace_usb_host_attach_kernel(s->bus_num, s->addr, i);
+        libusb_attach_kernel_driver(s->dh, i);
+        s->ifs[i].detached = false;
+    }
+    libusb_free_config_descriptor(conf);
+}
+
+static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    struct libusb_config_descriptor *conf;
+    int rc, i;
+
+    for (i = 0; i < USB_MAX_INTERFACES; i++) {
+        udev->altsetting[i] = 0;
+    }
+    udev->ninterfaces   = 0;
+    udev->configuration = 0;
+
+    if (configuration == 0) {
+        /* address state - ignore */
+        return USB_RET_SUCCESS;
+    }
+
+    usb_host_detach_kernel(s);
+
+    rc = libusb_get_active_config_descriptor(s->dev, &conf);
+    if (rc != 0) {
+        return USB_RET_STALL;
+    }
+
+    for (i = 0; i < conf->bNumInterfaces; i++) {
+        trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i);
+        rc = libusb_claim_interface(s->dh, i);
+        usb_host_libusb_error("libusb_claim_interface", rc);
+        if (rc != 0) {
+            return USB_RET_STALL;
+        }
+        s->ifs[i].claimed = true;
+    }
+
+    udev->ninterfaces   = conf->bNumInterfaces;
+    udev->configuration = configuration;
+
+    libusb_free_config_descriptor(conf);
+    return USB_RET_SUCCESS;
+}
+
+static void usb_host_release_interfaces(USBHostDevice *s)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    int i, rc;
+
+    for (i = 0; i < udev->ninterfaces; i++) {
+        if (!s->ifs[i].claimed) {
+            continue;
+        }
+        trace_usb_host_release_interface(s->bus_num, s->addr, i);
+        rc = libusb_release_interface(s->dh, i);
+        usb_host_libusb_error("libusb_release_interface", rc);
+        s->ifs[i].claimed = false;
+    }
+}
+
+static void usb_host_set_address(USBHostDevice *s, int addr)
+{
+    USBDevice *udev = USB_DEVICE(s);
+
+    trace_usb_host_set_address(s->bus_num, s->addr, addr);
+    udev->addr = addr;
+}
+
+static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
+{
+    int rc;
+
+    trace_usb_host_set_config(s->bus_num, s->addr, config);
+
+    usb_host_release_interfaces(s);
+    usb_host_detach_kernel(s);
+    rc = libusb_set_configuration(s->dh, config);
+    if (rc != 0) {
+        usb_host_libusb_error("libusb_set_configuration", rc);
+        p->status = USB_RET_STALL;
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+    p->status = usb_host_claim_interfaces(s, config);
+    if (p->status != USB_RET_SUCCESS) {
+        return;
+    }
+    usb_host_ep_update(s);
+}
+
+static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
+                                   USBPacket *p)
+{
+    USBDevice *udev = USB_DEVICE(s);
+    int rc;
+
+    trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
+
+    usb_host_iso_free_all(s);
+
+    if (iface >= USB_MAX_INTERFACES) {
+        p->status = USB_RET_STALL;
+        return;
+    }
+
+    rc = libusb_set_interface_alt_setting(s->dh, iface, alt);
+    if (rc != 0) {
+        usb_host_libusb_error("libusb_set_interface_alt_setting", rc);
+        p->status = USB_RET_STALL;
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+
+    udev->altsetting[iface] = alt;
+    usb_host_ep_update(s);
+}
+
+static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
+                                    int request, int value, int index,
+                                    int length, uint8_t *data)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    USBHostRequest *r;
+    int rc;
+
+    trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
+
+    if (s->dh == NULL) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+    }
+
+    switch (request) {
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        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:
+        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:
+        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 */
+            int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+            libusb_clear_halt(s->dh, index);
+            usb_ep_set_halted(udev, pid, index & 0x0f, 0);
+            trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+            return;
+        }
+    }
+
+    r = usb_host_req_alloc(s, p, (request >> 8) & USB_DIR_IN, length + 8);
+    r->cbuf = data;
+    r->clen = length;
+    memcpy(r->buffer, udev->setup_buf, 8);
+    if (!r->in) {
+        memcpy(r->buffer + 8, r->cbuf, r->clen);
+    }
+
+    libusb_fill_control_transfer(r->xfer, s->dh, r->buffer,
+                                 usb_host_req_complete_ctrl, r,
+                                 CONTROL_TIMEOUT);
+    rc = libusb_submit_transfer(r->xfer);
+    if (rc != 0) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+
+    p->status = USB_RET_ASYNC;
+}
+
+static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+    USBHostRequest *r;
+    size_t size;
+    int ep, rc;
+
+    if (usb_host_use_combining(p->ep) && p->state == USB_PACKET_SETUP) {
+        p->status = USB_RET_ADD_TO_QUEUE;
+        return;
+    }
+
+    trace_usb_host_req_data(s->bus_num, s->addr, p,
+                            p->pid == USB_TOKEN_IN,
+                            p->ep->nr, p->iov.size);
+
+    if (s->dh == NULL) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+    }
+    if (p->ep->halted) {
+        p->status = USB_RET_STALL;
+        trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
+        return;
+    }
+
+    switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) {
+    case USB_ENDPOINT_XFER_BULK:
+        size = usb_packet_size(p);
+        r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, size);
+        if (!r->in) {
+            usb_packet_copy(p, r->buffer, size);
+        }
+        ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
+        libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
+                                  r->buffer, size,
+                                  usb_host_req_complete_data, r,
+                                  BULK_TIMEOUT);
+        break;
+    case USB_ENDPOINT_XFER_INT:
+        r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size);
+        if (!r->in) {
+            usb_packet_copy(p, r->buffer, p->iov.size);
+        }
+        ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
+        libusb_fill_interrupt_transfer(r->xfer, s->dh, ep,
+                                       r->buffer, p->iov.size,
+                                       usb_host_req_complete_data, r,
+                                       INTR_TIMEOUT);
+        break;
+    case USB_ENDPOINT_XFER_ISOC:
+        if (p->pid == USB_TOKEN_IN) {
+            usb_host_iso_data_in(s, p);
+        } else {
+            usb_host_iso_data_out(s, p);
+        }
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        return;
+    default:
+        p->status = USB_RET_STALL;
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        return;
+    }
+
+    rc = libusb_submit_transfer(r->xfer);
+    if (rc != 0) {
+        p->status = USB_RET_NODEV;
+        trace_usb_host_req_complete(s->bus_num, s->addr, p,
+                                    p->status, p->actual_length);
+        if (rc == LIBUSB_ERROR_NO_DEVICE) {
+            usb_host_nodev(s);
+        }
+        return;
+    }
+
+    p->status = USB_RET_ASYNC;
+}
+
+static void usb_host_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
+{
+    if (usb_host_use_combining(ep)) {
+        usb_ep_combine_input_packets(ep);
+    }
+}
+
+static void usb_host_handle_reset(USBDevice *udev)
+{
+    USBHostDevice *s = USB_HOST_DEVICE(udev);
+
+    trace_usb_host_reset(s->bus_num, s->addr);
+
+    if (udev->configuration == 0) {
+        return;
+    }
+    usb_host_release_interfaces(s);
+    libusb_reset_device(s->dh);
+    usb_host_claim_interfaces(s, 0);
+    usb_host_ep_update(s);
+}
+
+static const VMStateDescription vmstate_usb_host = {
+    .name = "usb-host",
+    .unmigratable = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_USB_DEVICE(parent_obj, USBHostDevice),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property usb_host_dev_properties[] = {
+    DEFINE_PROP_UINT32("hostbus",  USBHostDevice, match.bus_num,    0),
+    DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr,       0),
+    DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
+    DEFINE_PROP_HEX32("vendorid",  USBHostDevice, match.vendor_id,  0),
+    DEFINE_PROP_HEX32("productid", USBHostDevice, match.product_id, 0),
+    DEFINE_PROP_UINT32("isobufs",  USBHostDevice, iso_urb_count,    4),
+    DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames,   32),
+    DEFINE_PROP_INT32("bootindex", USBHostDevice, bootindex,        -1),
+    DEFINE_PROP_UINT32("loglevel",  USBHostDevice, loglevel,
+                       LIBUSB_LOG_LEVEL_WARNING),
+    DEFINE_PROP_BIT("pipeline",    USBHostDevice, options,
+                    USB_HOST_OPT_PIPELINE, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_host_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->init           = usb_host_initfn;
+    uc->product_desc   = "USB Host Device";
+    uc->cancel_packet  = usb_host_cancel_packet;
+    uc->handle_data    = usb_host_handle_data;
+    uc->handle_control = usb_host_handle_control;
+    uc->handle_reset   = usb_host_handle_reset;
+    uc->handle_destroy = usb_host_handle_destroy;
+    uc->flush_ep_queue = usb_host_flush_ep_queue;
+    dc->vmsd = &vmstate_usb_host;
+    dc->props = usb_host_dev_properties;
+}
+
+static TypeInfo usb_host_dev_info = {
+    .name          = TYPE_USB_HOST_DEVICE,
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBHostDevice),
+    .class_init    = usb_host_class_initfn,
+};
+
+static void usb_host_register_types(void)
+{
+    type_register_static(&usb_host_dev_info);
+}
+
+type_init(usb_host_register_types)
+
+/* ------------------------------------------------------------------------ */
+
+static QEMUTimer *usb_auto_timer;
+static VMChangeStateEntry *usb_vmstate;
+
+static void usb_host_vm_state(void *unused, int running, RunState state)
+{
+    if (running) {
+        usb_host_auto_check(unused);
+    }
+}
+
+static void usb_host_auto_check(void *unused)
+{
+    struct USBHostDevice *s;
+    struct USBAutoFilter *f;
+    libusb_device **devs;
+    struct libusb_device_descriptor ddesc;
+    int unconnected = 0;
+    int i, n;
+
+    if (usb_host_init() != 0) {
+        return;
+    }
+
+    if (runstate_is_running()) {
+        n = libusb_get_device_list(ctx, &devs);
+        for (i = 0; i < n; i++) {
+            if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) {
+                continue;
+            }
+            if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) {
+                continue;
+            }
+            QTAILQ_FOREACH(s, &hostdevs, next) {
+                f = &s->match;
+                if (f->bus_num > 0 &&
+                    f->bus_num != libusb_get_bus_number(devs[i])) {
+                    continue;
+                }
+                if (f->addr > 0 &&
+                    f->addr != libusb_get_device_address(devs[i])) {
+                    continue;
+                }
+                if (f->port != NULL) {
+                    char port[16] = "-";
+                    usb_host_get_port(devs[i], port, sizeof(port));
+                    if (strcmp(f->port, port) != 0) {
+                        continue;
+                    }
+                }
+                if (f->vendor_id > 0 &&
+                    f->vendor_id != ddesc.idVendor) {
+                    continue;
+                }
+                if (f->product_id > 0 &&
+                    f->product_id != ddesc.idProduct) {
+                    continue;
+                }
+
+                /* We got a match */
+                s->seen++;
+                if (s->errcount >= 3) {
+                    continue;
+                }
+                if (s->dh != NULL) {
+                    continue;
+                }
+                if (usb_host_open(s, devs[i]) < 0) {
+                    s->errcount++;
+                    continue;
+                }
+                break;
+            }
+        }
+        libusb_free_device_list(devs, 1);
+
+        QTAILQ_FOREACH(s, &hostdevs, next) {
+            if (s->dh == NULL) {
+                unconnected++;
+            }
+            if (s->seen == 0) {
+                if (s->dh) {
+                    usb_host_close(s);
+                }
+                s->errcount = 0;
+            }
+            s->seen = 0;
+        }
+
+#if 0
+        if (unconnected == 0) {
+            /* nothing to watch */
+            if (usb_auto_timer) {
+                qemu_del_timer(usb_auto_timer);
+                trace_usb_host_auto_scan_disabled();
+            }
+            return;
+        }
+#endif
+    }
+
+    if (!usb_vmstate) {
+        usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL);
+    }
+    if (!usb_auto_timer) {
+        usb_auto_timer = qemu_new_timer_ms(rt_clock, usb_host_auto_check, NULL);
+        if (!usb_auto_timer) {
+            return;
+        }
+        trace_usb_host_auto_scan_enabled();
+    }
+    qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
+}
+
+void usb_host_info(Monitor *mon, const QDict *qdict)
+{
+    libusb_device **devs;
+    struct libusb_device_descriptor ddesc;
+    char port[16];
+    int i, n;
+
+    if (usb_host_init() != 0) {
+        return;
+    }
+
+    n = libusb_get_device_list(ctx, &devs);
+    for (i = 0; i < n; i++) {
+        if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) {
+            continue;
+        }
+        if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) {
+            continue;
+        }
+        usb_host_get_port(devs[i], port, sizeof(port));
+        monitor_printf(mon, "  Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
+                       libusb_get_bus_number(devs[i]),
+                       libusb_get_device_address(devs[i]),
+                       port,
+                       speed_name[libusb_get_device_speed(devs[i])]);
+        monitor_printf(mon, "    Class %02x:", ddesc.bDeviceClass);
+        monitor_printf(mon, " USB device %04x:%04x",
+                       ddesc.idVendor, ddesc.idProduct);
+        if (ddesc.iProduct) {
+            libusb_device_handle *handle;
+            if (libusb_open(devs[i], &handle) == 0) {
+                unsigned char name[64] = "";
+                libusb_get_string_descriptor_ascii(handle,
+                                                   ddesc.iProduct,
+                                                   name, sizeof(name));
+                libusb_close(handle);
+                monitor_printf(mon, ", %s", name);
+            }
+        }
+        monitor_printf(mon, "\n");
+    }
+    libusb_free_device_list(devs, 1);
+}
diff --git a/hw/usb/host-linux.c b/hw/usb/host-linux.c
index b67aeba..8994668 100644
--- a/hw/usb/host-linux.c
+++ b/hw/usb/host-linux.c
@@ -45,6 +45,12 @@
 #include "hw/usb/desc.h"
 #include "hw/usb/host.h"
 
+#ifdef CONFIG_USB_LIBUSB
+# define DEVNAME "usb-host-linux"
+#else
+# define DEVNAME "usb-host"
+#endif
+
 /* We redefine it to avoid version problems */
 struct usb_ctrltransfer {
     uint8_t  bRequestType;
@@ -1487,7 +1493,7 @@ static int usb_host_initfn(USBDevice *dev)
 }
 
 static const VMStateDescription vmstate_usb_host = {
-    .name = "usb-host",
+    .name = DEVNAME,
     .version_id = 1,
     .minimum_version_id = 1,
     .post_load = usb_host_post_load,
@@ -1527,7 +1533,7 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data)
 }
 
 static const TypeInfo usb_host_dev_info = {
-    .name          = "usb-host",
+    .name          = DEVNAME,
     .parent        = TYPE_USB_DEVICE,
     .instance_size = sizeof(USBHostDevice),
     .class_init    = usb_host_class_initfn,
@@ -1767,6 +1773,8 @@ static void usb_host_auto_check(void *unused)
     qemu_mod_timer(usb_auto_timer, qemu_get_clock_ms(rt_clock) + 2000);
 }
 
+#ifndef CONFIG_USB_LIBUSB
+
 /**********************/
 /* USB host device info */
 
@@ -1898,3 +1906,5 @@ void usb_host_info(Monitor *mon, const QDict *qdict)
                        bus, addr, f->port ? f->port : "*", vid, pid);
     }
 }
+
+#endif
diff --git a/trace-events b/trace-events
index 54b7d90..bf6bd85 100644
--- a/trace-events
+++ b/trace-events
@@ -425,11 +425,15 @@ usb_host_open_success(int bus, int addr) "dev %d:%d"
 usb_host_open_failure(int bus, int addr) "dev %d:%d"
 usb_host_disconnect(int bus, int addr) "dev %d:%d"
 usb_host_close(int bus, int addr) "dev %d:%d"
+usb_host_attach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d"
+usb_host_detach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d"
 usb_host_set_address(int bus, int addr, int config) "dev %d:%d, address %d"
 usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d"
 usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d"
 usb_host_claim_interfaces(int bus, int addr, int config, int nif) "dev %d:%d, config %d, nif %d"
+usb_host_claim_interface(int bus, int addr, int config, int interface) "dev %d:%d, config %d, if %d"
 usb_host_release_interfaces(int bus, int addr) "dev %d:%d"
+usb_host_release_interface(int bus, int addr, int interface) "dev %d:%d, if %d"
 usb_host_req_control(int bus, int addr, void *p, int req, int value, int index) "dev %d:%d, packet %p, req 0x%x, value %d, index %d"
 usb_host_req_data(int bus, int addr, void *p, int in, int ep, int size) "dev %d:%d, packet %p, in %d, ep %d, size %d"
 usb_host_req_complete(int bus, int addr, void *p, int status, int length) "dev %d:%d, packet %p, status %d, length %d"
commit a67188743bc30a3ad1358b8cd0a2a3cb64c10ff9
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Apr 5 15:18:52 2013 +0200

    xhci: fix address device
    
    Zero-initialize the set-address dummy USBPacket,
    also add buffer to avoid sanity checks triggering.
    
    https://bugzilla.redhat.com/show_bug.cgi?id=929019
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index e489059..a26b78e 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2139,8 +2139,12 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
         slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
     } else {
         USBPacket p;
+        uint8_t buf[1];
+
         slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
         usb_device_reset(dev);
+        memset(&p, 0, sizeof(p));
+        usb_packet_addbuf(&p, buf, sizeof(buf));
         usb_packet_setup(&p, USB_TOKEN_OUT,
                          usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
                          0, false, false);
commit af203be36deb234550c3c8d2cbafbaef0f08ae1b
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Mar 21 10:59:05 2013 +0100

    xhci: use slotid as device address
    
    Is good enougth for unique device addresses and avoids the need for any
    state for device addressing.  Makes live migration support easier.  Also
    makes device->slot lookups trivial.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index bb0cf1e..e489059 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -408,7 +408,6 @@ typedef struct XHCISlot {
     bool enabled;
     dma_addr_t ctx;
     USBPort *uport;
-    unsigned int devaddr;
     XHCIEPContext * eps[31];
 } XHCISlot;
 
@@ -452,7 +451,6 @@ struct XHCIState {
     MemoryRegion mem_oper;
     MemoryRegion mem_runtime;
     MemoryRegion mem_doorbell;
-    unsigned int devaddr;
 
     /* properties */
     uint32_t numports_2;
@@ -2141,16 +2139,14 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
         slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
     } else {
         USBPacket p;
-        slot->devaddr = xhci->devaddr++;
-        slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slot->devaddr;
-        DPRINTF("xhci: device address is %d\n", slot->devaddr);
+        slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
         usb_device_reset(dev);
         usb_packet_setup(&p, USB_TOKEN_OUT,
                          usb_ep_get(dev, USB_TOKEN_OUT, 0), 0,
                          0, false, false);
         usb_device_handle_control(dev, &p,
                                   DeviceOutRequest | USB_REQ_SET_ADDRESS,
-                                  slot->devaddr, 0, 0, NULL);
+                                  slotid, 0, 0, NULL);
         assert(p.status != USB_RET_ASYNC);
     }
 
@@ -2674,7 +2670,6 @@ static void xhci_reset(DeviceState *dev)
     xhci->dcbaap_low = 0;
     xhci->dcbaap_high = 0;
     xhci->config = 0;
-    xhci->devaddr = 2;
 
     for (i = 0; i < xhci->numslots; i++) {
         xhci_disable_slot(xhci, i+1);
@@ -3212,20 +3207,6 @@ static USBPortOps xhci_uport_ops = {
     .child_detach = xhci_child_detach,
 };
 
-static int xhci_find_slotid(XHCIState *xhci, USBDevice *dev)
-{
-    XHCISlot *slot;
-    int slotid;
-
-    for (slotid = 1; slotid <= xhci->numslots; slotid++) {
-        slot = &xhci->slots[slotid-1];
-        if (slot->devaddr == dev->addr) {
-            return slotid;
-        }
-    }
-    return 0;
-}
-
 static int xhci_find_epid(USBEndpoint *ep)
 {
     if (ep->nr == 0) {
@@ -3245,7 +3226,7 @@ static void xhci_wakeup_endpoint(USBBus *bus, USBEndpoint *ep,
     int slotid;
 
     DPRINTF("%s\n", __func__);
-    slotid = xhci_find_slotid(xhci, ep->dev);
+    slotid = ep->dev->addr;
     if (slotid == 0 || !xhci->slots[slotid-1].enabled) {
         DPRINTF("%s: oops, no slot for dev %d\n", __func__, ep->dev->addr);
         return;
commit bdfce20df113522f389b4483ffd9d5b336e3c774
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Apr 5 14:55:28 2013 +0200

    xhci: fix portsc writes
    
    Check for port reset first and skip everything else then.
    Add sanity checks for PLS updates.
    Add PLC notification when entering PLS_U0 state.
    
    This gets host-initiated port resume going on win8.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index 7f740d9..bb0cf1e 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2592,6 +2592,7 @@ static void xhci_port_notify(XHCIPort *port, uint32_t bits)
     if ((port->portsc & bits) == bits) {
         return;
     }
+    trace_usb_xhci_port_notify(port->portnr, bits);
     port->portsc |= bits;
     if (!xhci_running(port->xhci)) {
         return;
@@ -2798,29 +2799,56 @@ static void xhci_port_write(void *ptr, hwaddr reg,
                             uint64_t val, unsigned size)
 {
     XHCIPort *port = ptr;
-    uint32_t portsc;
+    uint32_t portsc, notify;
 
     trace_usb_xhci_port_write(port->portnr, reg, val);
 
     switch (reg) {
     case 0x00: /* PORTSC */
+        /* write-1-to-start bits */
+        if (val & PORTSC_PR) {
+            xhci_port_reset(port);
+            break;
+        }
+
         portsc = port->portsc;
+        notify = 0;
         /* write-1-to-clear bits*/
         portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC|
                            PORTSC_PRC|PORTSC_PLC|PORTSC_CEC));
         if (val & PORTSC_LWS) {
             /* overwrite PLS only when LWS=1 */
-            uint32_t pls = get_field(val, PORTSC_PLS);
-            set_field(&portsc, pls, PORTSC_PLS);
-            trace_usb_xhci_port_link(port->portnr, pls);
+            uint32_t old_pls = get_field(port->portsc, PORTSC_PLS);
+            uint32_t new_pls = get_field(val, PORTSC_PLS);
+            switch (new_pls) {
+            case PLS_U0:
+                if (old_pls != PLS_U0) {
+                    set_field(&portsc, new_pls, PORTSC_PLS);
+                    trace_usb_xhci_port_link(port->portnr, new_pls);
+                    notify = PORTSC_PLC;
+                }
+                break;
+            case PLS_U3:
+                if (old_pls < PLS_U3) {
+                    set_field(&portsc, new_pls, PORTSC_PLS);
+                    trace_usb_xhci_port_link(port->portnr, new_pls);
+                }
+                break;
+            case PLS_RESUME:
+                /* windows does this for some reason, don't spam stderr */
+                break;
+            default:
+                fprintf(stderr, "%s: ignore pls write (old %d, new %d)\n",
+                        __func__, old_pls, new_pls);
+                break;
+            }
         }
         /* read/write bits */
         portsc &= ~(PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE);
         portsc |= (val & (PORTSC_PP|PORTSC_WCE|PORTSC_WDE|PORTSC_WOE));
         port->portsc = portsc;
-        /* write-1-to-start bits */
-        if (val & PORTSC_PR) {
-            xhci_port_reset(port);
+        if (notify) {
+            xhci_port_notify(port, notify);
         }
         break;
     case 0x04: /* PORTPMSC */
diff --git a/trace-events b/trace-events
index 412f7e4..54b7d90 100644
--- a/trace-events
+++ b/trace-events
@@ -362,6 +362,7 @@ usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char
 usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
 usb_xhci_port_reset(uint32_t port) "port %d"
 usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d"
+usb_xhci_port_notify(uint32_t port, uint32_t pls) "port %d, bits %x"
 usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
 usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
 usb_xhci_slot_address(uint32_t slotid) "slotid %d"
commit 6d3bc22e31bcee74dc1e05a5370cabb33b7c3fda
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Apr 5 11:29:14 2013 +0200

    xhci: add xhci_cap_write
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index c0dbc54..7f740d9 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -3079,8 +3079,15 @@ static void xhci_doorbell_write(void *ptr, hwaddr reg,
     }
 }
 
+static void xhci_cap_write(void *opaque, hwaddr addr, uint64_t val,
+                           unsigned width)
+{
+    /* nothing */
+}
+
 static const MemoryRegionOps xhci_cap_ops = {
     .read = xhci_cap_read,
+    .write = xhci_cap_write,
     .valid.min_access_size = 1,
     .valid.max_access_size = 4,
     .impl.min_access_size = 4,
commit 94ae9eece7c8192170a4159804e152fa32e9eacf
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Apr 5 13:56:53 2013 +0200

    xhci: remove leftover debug printf
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index efd4b0d..c0dbc54 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -2526,7 +2526,6 @@ static void xhci_process_commands(XHCIState *xhci)
             }
             break;
         case CR_SET_TR_DEQUEUE:
-            fprintf(stderr, "%s: CR_SET_TR_DEQUEUE\n", __func__);
             slotid = xhci_get_slot(xhci, &event, &trb);
             if (slotid) {
                 unsigned int epid = (trb.control >> TRB_CR_EPID_SHIFT)
commit 7598b41cfa13b2469b9411eee237a5c551e0ffaf
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Fri Apr 5 11:30:25 2013 +0200

    usb-serial: Remove double call to qemu_chr_add_handlers( NULL )
    
    usb-serial has a qdev chardev property, and hw/qdev-properties-system.c
    already contains:
    
    static void release_chr(Object *obj, const char *name, void *opaque)
    {
        DeviceState *dev = DEVICE(obj);
        Property *prop = opaque;
        CharDriverState **ptr = qdev_get_prop_ptr(dev, prop);
        CharDriverState *chr = *ptr;
    
        if (chr) {
            qemu_chr_add_handlers(chr, NULL, NULL, NULL, NULL);
            qemu_chr_fe_release(chr);
        }
    }
    
    So doing the qemu_chr_add_handlers(s->cs, NULL, NULL, NULL, NULL); from
    the usb handle_destroy function too will lead to it being done twice.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index dd0a608..2fc8a3b 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -410,13 +410,6 @@ static void usb_serial_handle_data(USBDevice *dev, USBPacket *p)
     }
 }
 
-static void usb_serial_handle_destroy(USBDevice *dev)
-{
-    USBSerialState *s = (USBSerialState *)dev;
-
-    qemu_chr_add_handlers(s->cs, NULL, NULL, NULL, NULL);
-}
-
 static int usb_serial_can_read(void *opaque)
 {
     USBSerialState *s = opaque;
@@ -595,7 +588,6 @@ static void usb_serial_class_initfn(ObjectClass *klass, void *data)
     uc->handle_reset   = usb_serial_handle_reset;
     uc->handle_control = usb_serial_handle_control;
     uc->handle_data    = usb_serial_handle_data;
-    uc->handle_destroy = usb_serial_handle_destroy;
     dc->vmsd = &vmstate_usb_serial;
     dc->props = serial_properties;
 }
@@ -623,7 +615,6 @@ static void usb_braille_class_initfn(ObjectClass *klass, void *data)
     uc->handle_reset   = usb_serial_handle_reset;
     uc->handle_control = usb_serial_handle_control;
     uc->handle_data    = usb_serial_handle_data;
-    uc->handle_destroy = usb_serial_handle_destroy;
     dc->vmsd = &vmstate_usb_serial;
     dc->props = braille_properties;
 }
commit 75c439bc65c07d76f5e74c734ed5432bc6114a3b
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Fri Apr 5 11:30:24 2013 +0200

    spice-qemu-char: vmc_write: Don't write more bytes then we're asked too
    
    This one took me eons to debug, but I've finally found it now, oh well.
    
    The usage of the MIN macro in this line:
        last_out = MIN(len, qemu_chr_be_can_write(scd->chr));
    
    Causes qemu_chr_be_can_write to be called *twice*, since the MIN macro
    evaluates its arguments twice (bad MIN macro, bad!). And the result of
    the call can change between the 2 calls since the guest may have consumed
    some data from the virtio ringbuffer between the calls!
    
    When this happens it is possible for qemu_chr_be_can_write to return less
    then len in the call made for the comparision, and then to return more then
    len in the actual call for the return-value of MIN, after which we will end
    up writing len data + some extra garbage, not good.
    
    This patch fixes this by only calling qemu_chr_be_can_write once.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index ff95fcb..f10970c 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -35,7 +35,8 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
     uint8_t* p = (uint8_t*)buf;
 
     while (len > 0) {
-        last_out = MIN(len, qemu_chr_be_can_write(scd->chr));
+        int can_write = qemu_chr_be_can_write(scd->chr);
+        last_out = MIN(len, can_write);
         if (last_out <= 0) {
             break;
         }
commit b010cec86b9a4a0b63162cd27e37c2d99e90ed66
Author: Alon Levy <alevy at redhat.com>
Date:   Fri Apr 5 11:30:23 2013 +0200

    spice-qemu-char: Remove intermediate buffer
    
    virtio-serial's buffer is valid when it calls us, and we don't
    access it otherwise: vmc_read is only called in response to wakeup,
    or else we set datalen=0 and throttle. Then vmc_read is called back,
    we return 0 (not accessing the buffer) and set the timer to unthrottle.
    
    Also make datalen int and not ssize_t (to fit spice_chr_write signature).
    
    HdG: Update to apply to spice-qemu-char with new gio-channel based
    flowcontrol support.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index 7e551bf..ff95fcb 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -14,9 +14,8 @@ typedef struct SpiceCharDriver {
     char                  *subtype;
     bool                  active;
     bool                  blocked;
-    uint8_t               *buffer;
-    uint8_t               *datapos;
-    ssize_t               bufsize, datalen;
+    const uint8_t         *datapos;
+    int                   datalen;
     QLIST_ENTRY(SpiceCharDriver) next;
 } SpiceCharDriver;
 
@@ -186,12 +185,7 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     int read_bytes;
 
     assert(s->datalen == 0);
-    if (s->bufsize < len) {
-        s->bufsize = len;
-        s->buffer = g_realloc(s->buffer, s->bufsize);
-    }
-    memcpy(s->buffer, buf, len);
-    s->datapos = s->buffer;
+    s->datapos = buf;
     s->datalen = len;
     spice_server_char_device_wakeup(&s->sin);
     read_bytes = len - s->datalen;
commit ae893e5e818878caf433d716d37be9df297403fe
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Fri Apr 5 11:30:22 2013 +0200

    spice-qemu-char: Add watch support
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index be19917..7e551bf 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -13,12 +13,18 @@ typedef struct SpiceCharDriver {
     SpiceCharDeviceInstance     sin;
     char                  *subtype;
     bool                  active;
+    bool                  blocked;
     uint8_t               *buffer;
     uint8_t               *datapos;
     ssize_t               bufsize, datalen;
     QLIST_ENTRY(SpiceCharDriver) next;
 } SpiceCharDriver;
 
+typedef struct SpiceCharSource {
+    GSource               source;
+    SpiceCharDriver       *scd;
+} SpiceCharSource;
+
 static QLIST_HEAD(, SpiceCharDriver) spice_chars =
     QLIST_HEAD_INITIALIZER(spice_chars);
 
@@ -54,9 +60,10 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
         scd->datapos += bytes;
         scd->datalen -= bytes;
         assert(scd->datalen >= 0);
-        if (scd->datalen == 0) {
-            scd->datapos = 0;
-        }
+    }
+    if (scd->datalen == 0) {
+        scd->datapos = 0;
+        scd->blocked = false;
     }
     trace_spice_vmc_read(bytes, len);
     return bytes;
@@ -129,10 +136,54 @@ static void vmc_unregister_interface(SpiceCharDriver *scd)
     trace_spice_vmc_unregister_interface(scd);
 }
 
+static gboolean spice_char_source_prepare(GSource *source, gint *timeout)
+{
+    SpiceCharSource *src = (SpiceCharSource *)source;
+
+    *timeout = -1;
+
+    return !src->scd->blocked;
+}
+
+static gboolean spice_char_source_check(GSource *source)
+{
+    SpiceCharSource *src = (SpiceCharSource *)source;
+
+    return !src->scd->blocked;
+}
+
+static gboolean spice_char_source_dispatch(GSource *source,
+    GSourceFunc callback, gpointer user_data)
+{
+    GIOFunc func = (GIOFunc)callback;
+
+    return func(NULL, G_IO_OUT, user_data);
+}
+
+GSourceFuncs SpiceCharSourceFuncs = {
+    .prepare  = spice_char_source_prepare,
+    .check    = spice_char_source_check,
+    .dispatch = spice_char_source_dispatch,
+};
+
+static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+    SpiceCharDriver *scd = chr->opaque;
+    SpiceCharSource *src;
+
+    assert(cond == G_IO_OUT);
+
+    src = (SpiceCharSource *)g_source_new(&SpiceCharSourceFuncs,
+                                          sizeof(SpiceCharSource));
+    src->scd = scd;
+
+    return (GSource *)src;
+}
 
 static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     SpiceCharDriver *s = chr->opaque;
+    int read_bytes;
 
     assert(s->datalen == 0);
     if (s->bufsize < len) {
@@ -143,7 +194,14 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     s->datapos = s->buffer;
     s->datalen = len;
     spice_server_char_device_wakeup(&s->sin);
-    return len;
+    read_bytes = len - s->datalen;
+    if (read_bytes != len) {
+        /* We'll get passed in the unconsumed data with the next call */
+        s->datalen = 0;
+        s->datapos = NULL;
+        s->blocked = true;
+    }
+    return read_bytes;
 }
 
 static void spice_chr_close(struct CharDriverState *chr)
@@ -199,6 +257,7 @@ static CharDriverState *chr_open(const char *subtype)
     s->sin.subtype = g_strdup(subtype);
     chr->opaque = s;
     chr->chr_write = spice_chr_write;
+    chr->chr_add_watch = spice_chr_add_watch;
     chr->chr_close = spice_chr_close;
     chr->chr_set_fe_open = spice_chr_set_fe_open;
 
commit 52fe0e75b757c7c54fd3b967c9ff70d337790195
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Fri Apr 5 11:30:21 2013 +0200

    spice-qemu-char: Remove #ifdef-ed code for old spice-server compat
    
    We now require spice-server to be >= 0.12.0 so this is no longer needed.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index c9403de..be19917 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -85,21 +85,6 @@ static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
 
-#if SPICE_SERVER_VERSION < 0x000901
-    /*
-     * spice-server calls the state callback for the agent channel when the
-     * spice client connects / disconnects. Given that not the client but
-     * the server is doing the parsing of the messages this is wrong as the
-     * server is still listening. Worse, this causes the parser in the server
-     * to go out of sync, so we ignore state calls for subtype vdagent
-     * spicevmc chardevs. For the full story see:
-     * http://lists.freedesktop.org/archives/spice-devel/2011-July/004837.html
-     */
-    if (strcmp(sin->subtype, "vdagent") == 0) {
-        return;
-    }
-#endif
-
     if ((scd->chr->be_open && connected) ||
         (!scd->chr->be_open && !connected)) {
         return;
@@ -224,7 +209,6 @@ static CharDriverState *chr_open(const char *subtype)
 
 CharDriverState *qemu_chr_open_spice_vmc(const char *type)
 {
-    CharDriverState *chr;
     const char **psubtype = spice_server_char_device_recognized_subtypes();
 
     if (type == NULL) {
@@ -243,16 +227,7 @@ CharDriverState *qemu_chr_open_spice_vmc(const char *type)
         return NULL;
     }
 
-    chr = chr_open(type);
-
-#if SPICE_SERVER_VERSION < 0x000901
-    /* See comment in vmc_state() */
-    if (strcmp(type, "vdagent") == 0) {
-        qemu_chr_generic_open(chr);
-    }
-#endif
-
-    return chr;
+    return chr_open(type);
 }
 
 #if SPICE_SERVER_VERSION >= 0x000c02
commit c3d6b96ebba18d016be26ffa475feea0d81801a2
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Fri Apr 5 11:30:20 2013 +0200

    virtio-console: Remove any pending watches on close
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 061f4bd..6759e51 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -18,6 +18,7 @@
 typedef struct VirtConsole {
     VirtIOSerialPort port;
     CharDriverState *chr;
+    guint watch;
 } VirtConsole;
 
 /*
@@ -29,6 +30,7 @@ static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
 {
     VirtConsole *vcon = opaque;
 
+    vcon->watch = 0;
     virtio_serial_throttle_port(&vcon->port, false);
     return FALSE;
 }
@@ -61,8 +63,10 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
             ret = 0;
         if (!k->is_console) {
             virtio_serial_throttle_port(port, true);
-            qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked,
-                                  vcon);
+            if (!vcon->watch) {
+                vcon->watch = qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT,
+                                                    chr_write_unblocked, vcon);
+            }
         }
     }
     return ret;
@@ -106,6 +110,10 @@ static void chr_event(void *opaque, int event)
         virtio_serial_open(&vcon->port);
         break;
     case CHR_EVENT_CLOSED:
+        if (vcon->watch) {
+            g_source_remove(vcon->watch);
+            vcon->watch = 0;
+        }
         virtio_serial_close(&vcon->port);
         break;
     }
@@ -130,6 +138,17 @@ static int virtconsole_initfn(VirtIOSerialPort *port)
     return 0;
 }
 
+static int virtconsole_exitfn(VirtIOSerialPort *port)
+{
+    VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
+
+    if (vcon->watch) {
+        g_source_remove(vcon->watch);
+    }
+
+    return 0;
+}
+
 static Property virtconsole_properties[] = {
     DEFINE_PROP_CHR("chardev", VirtConsole, chr),
     DEFINE_PROP_END_OF_LIST(),
@@ -142,6 +161,7 @@ static void virtconsole_class_init(ObjectClass *klass, void *data)
 
     k->is_console = true;
     k->init = virtconsole_initfn;
+    k->exit = virtconsole_exitfn;
     k->have_data = flush_buf;
     k->set_guest_connected = set_guest_connected;
     dc->props = virtconsole_properties;
commit f9fb0532fb0c7155c0616614dc12ecccf93f8afb
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Fri Apr 5 11:30:19 2013 +0200

    virtio-console: Also throttle when less was written then requested
    
    This is necessary so that we get properly woken up to write the rest.
    
    This patch also changes the len argument to the have_data callback, to
    avoid doing an unsigned signed comparison.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>
    Acked-by: Amit Shah <amit.shah at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 5035030..061f4bd 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -34,7 +34,8 @@ static gboolean chr_write_unblocked(GIOChannel *chan, GIOCondition cond,
 }
 
 /* Callback function that's called when the guest sends us data */
-static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
+static ssize_t flush_buf(VirtIOSerialPort *port,
+                         const uint8_t *buf, ssize_t len)
 {
     VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port);
     ssize_t ret;
@@ -47,7 +48,7 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
     ret = qemu_chr_fe_write(vcon->chr, buf, len);
     trace_virtio_console_flush_buf(port->id, len, ret);
 
-    if (ret <= 0) {
+    if (ret < len) {
         VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
         /*
@@ -56,7 +57,8 @@ static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
          * we had a finer-grained message, like -EPIPE, we could close
          * this connection.
          */
-        ret = 0;
+        if (ret < 0)
+            ret = 0;
         if (!k->is_console) {
             virtio_serial_throttle_port(port, true);
             qemu_chr_fe_add_watch(vcon->chr, G_IO_OUT, chr_write_unblocked,
diff --git a/include/hw/virtio/virtio-serial.h b/include/hw/virtio/virtio-serial.h
index 7c71304..1d2040b 100644
--- a/include/hw/virtio/virtio-serial.h
+++ b/include/hw/virtio/virtio-serial.h
@@ -104,7 +104,7 @@ typedef struct VirtIOSerialPortClass {
      * 'len'.  In this case, throttling will be enabled for this port.
      */
     ssize_t (*have_data)(VirtIOSerialPort *port, const uint8_t *buf,
-                         size_t len);
+                         ssize_t len);
 } VirtIOSerialPortClass;
 
 /*
commit 8bb9f51ca243551fb838a3a6a2983016ed2bbb73
Author: Alon Levy <alevy at redhat.com>
Date:   Wed Mar 13 17:58:35 2013 +0200

    spice: (32 bit only) fix surface cmd tracking destruction
    
    No change for 64 bit arches, but for 32 bit previously we zeroed half
    the surfaces cmd array, instead of all of it.
    
    Signed-off-by: Alon Levy <alevy at redhat.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 1f7c8fe..cb47995 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -230,7 +230,7 @@ static void qxl_spice_destroy_surfaces_complete(PCIQXLDevice *qxl)
     trace_qxl_spice_destroy_surfaces_complete(qxl->id);
     qemu_mutex_lock(&qxl->track_lock);
     memset(qxl->guest_surfaces.cmds, 0,
-           sizeof(qxl->guest_surfaces.cmds) * qxl->ssd.num_surfaces);
+           sizeof(qxl->guest_surfaces.cmds[0]) * qxl->ssd.num_surfaces);
     qxl->guest_surfaces.count = 0;
     qemu_mutex_unlock(&qxl->track_lock);
 }
commit 5c74fb27f94821057c7929a8244cabe86adf2b8d
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Apr 4 10:15:34 2013 +0200

    qxl: add 2000x2000 and 2048x2048 video modes
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 9d8ab58..1f7c8fe 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -109,7 +109,9 @@ static QXLMode qxl_modes[] = {
     /* these modes need more than 8 MB video memory */
     QXL_MODE_EX(1920, 1200),
     QXL_MODE_EX(1920, 1440),
+    QXL_MODE_EX(2000, 2000),
     QXL_MODE_EX(2048, 1536),
+    QXL_MODE_EX(2048, 2048),
     QXL_MODE_EX(2560, 1440),
     QXL_MODE_EX(2560, 1600),
     /* these modes need more than 16 MB video memory */
commit 095b9c4860b1351e4a0322e43708f39c79c1f34b
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:34:50 2013 +1000

    m25p80: Add debug message for no bdrv
    
    If there is no backing bdrv, let the debugging developer know about it.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index d853a2e..efcc7f4 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -606,6 +606,7 @@ static int m25p80_init(SSISlave *ss)
             return 1;
         }
     } else {
+        DB_PRINT_L(0, "No BDRV - binding to RAM\n");
         memset(s->storage, 0xFF, s->size);
     }
 
commit 28097d02078cfb708c2034f90394936209785f2e
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:34:11 2013 +1000

    m25p80.c: Multiple debug verbosity levels
    
    The debug printfs on every page program/read is extremely verbose. Add
    a second level of debug for this.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 44fc428..d853a2e 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -26,15 +26,17 @@
 #include "hw/ssi.h"
 #include "hw/devices.h"
 
-#ifdef M25P80_ERR_DEBUG
-#define DB_PRINT(...) do { \
-    fprintf(stderr,  ": %s: ", __func__); \
-    fprintf(stderr, ## __VA_ARGS__); \
-    } while (0);
-#else
-    #define DB_PRINT(...)
+#ifndef M25P80_ERR_DEBUG
+#define M25P80_ERR_DEBUG 0
 #endif
 
+#define DB_PRINT_L(level, ...) do { \
+    if (M25P80_ERR_DEBUG > (level)) { \
+        fprintf(stderr,  ": %s: ", __func__); \
+        fprintf(stderr, ## __VA_ARGS__); \
+    } \
+} while (0);
+
 /* Fields for FlashPartInfo->flags */
 
 /* erase capabilities */
@@ -317,7 +319,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
         abort();
     }
 
-    DB_PRINT("offset = %#x, len = %d\n", offset, len);
+    DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len);
     if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
         qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by"
                       " device\n", len);
@@ -350,8 +352,8 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
     }
 
     if ((prev ^ data) & data) {
-        DB_PRINT("programming zero to one! addr=%" PRIx64 "  %" PRIx8
-                 " -> %" PRIx8 "\n", addr, prev, data);
+        DB_PRINT_L(1, "programming zero to one! addr=%" PRIx64 "  %" PRIx8
+                   " -> %" PRIx8 "\n", addr, prev, data);
     }
 
     if (s->pi->flags & WR_1) {
@@ -404,7 +406,7 @@ static void complete_collecting_data(Flash *s)
 static void decode_new_cmd(Flash *s, uint32_t value)
 {
     s->cmd_in_progress = value;
-    DB_PRINT("decoded new command:%x\n", value);
+    DB_PRINT_L(0, "decoded new command:%x\n", value);
 
     switch (value) {
 
@@ -484,7 +486,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
         break;
 
     case JEDEC_READ:
-        DB_PRINT("populated jedec code\n");
+        DB_PRINT_L(0, "populated jedec code\n");
         s->data[0] = (s->pi->jedec >> 16) & 0xff;
         s->data[1] = (s->pi->jedec >> 8) & 0xff;
         s->data[2] = s->pi->jedec & 0xff;
@@ -501,7 +503,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
 
     case BULK_ERASE:
         if (s->write_enable) {
-            DB_PRINT("chip erase\n");
+            DB_PRINT_L(0, "chip erase\n");
             flash_erase(s, 0, BULK_ERASE);
         } else {
             qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write "
@@ -527,7 +529,7 @@ static int m25p80_cs(SSISlave *ss, bool select)
         flash_sync_dirty(s, -1);
     }
 
-    DB_PRINT("%sselect\n", select ? "de" : "");
+    DB_PRINT_L(0, "%sselect\n", select ? "de" : "");
 
     return 0;
 }
@@ -540,16 +542,16 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
     switch (s->state) {
 
     case STATE_PAGE_PROGRAM:
-        DB_PRINT("page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n",
-                 s->cur_addr, (uint8_t)tx);
+        DB_PRINT_L(1, "page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n",
+                   s->cur_addr, (uint8_t)tx);
         flash_write8(s, s->cur_addr, (uint8_t)tx);
         s->cur_addr++;
         break;
 
     case STATE_READ:
         r = s->storage[s->cur_addr];
-        DB_PRINT("READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr,
-                 (uint8_t)r);
+        DB_PRINT_L(1, "READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr,
+                   (uint8_t)r);
         s->cur_addr = (s->cur_addr + 1) % s->size;
         break;
 
@@ -595,7 +597,7 @@ static int m25p80_init(SSISlave *ss)
     dinfo = drive_get_next(IF_MTD);
 
     if (dinfo && dinfo->bdrv) {
-        DB_PRINT("Binding to IF_MTD drive\n");
+        DB_PRINT_L(0, "Binding to IF_MTD drive\n");
         s->bdrv = dinfo->bdrv;
         /* FIXME: Move to late init */
         if (bdrv_read(s->bdrv, 0, s->storage, DIV_ROUND_UP(s->size,
commit e9711b4d52ed07b9b754f9aa10b4a8c741815d0a
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:33:32 2013 +1000

    m25p80: Convert guest errors to LOG_GUEST_ERROR
    
    Some of the debug printfs in m25p80 are really guest errors.
    Changed over to qemu_log_mask(LOG_GUEST_ERROR accordingly.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index c395e98..44fc428 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -319,11 +319,12 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
 
     DB_PRINT("offset = %#x, len = %d\n", offset, len);
     if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
-        hw_error("m25p80: %d erase size not supported by device\n", len);
+        qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by"
+                      " device\n", len);
     }
 
     if (!s->write_enable) {
-        DB_PRINT("erase with write protect!\n");
+        qemu_log_mask(LOG_GUEST_ERROR, "M25P80: erase with write protect!\n");
         return;
     }
     memset(s->storage + offset, 0xff, len);
@@ -345,7 +346,7 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
     uint8_t prev = s->storage[s->cur_addr];
 
     if (!s->write_enable) {
-        DB_PRINT("write with write protect!\n");
+        qemu_log_mask(LOG_GUEST_ERROR, "M25P80: write with write protect!\n");
     }
 
     if ((prev ^ data) & data) {
@@ -503,13 +504,14 @@ static void decode_new_cmd(Flash *s, uint32_t value)
             DB_PRINT("chip erase\n");
             flash_erase(s, 0, BULK_ERASE);
         } else {
-            DB_PRINT("chip erase with write protect!\n");
+            qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write "
+                          "protect!\n");
         }
         break;
     case NOP:
         break;
     default:
-        DB_PRINT("Unknown cmd %x\n", value);
+        qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value);
         break;
     }
 }
commit 3bec0c7d461a2f0139d60e959a80641487835d5a
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:32:53 2013 +1000

    m25p80: Fix debug messages.
    
    Some dodgy casts were making a mess of these msgs.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 55e9d0d..c395e98 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -319,7 +319,7 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd)
 
     DB_PRINT("offset = %#x, len = %d\n", offset, len);
     if ((s->pi->flags & capa_to_assert) != capa_to_assert) {
-        hw_error("m25p80: %dk erase size not supported by device\n", len);
+        hw_error("m25p80: %d erase size not supported by device\n", len);
     }
 
     if (!s->write_enable) {
@@ -349,8 +349,8 @@ void flash_write8(Flash *s, uint64_t addr, uint8_t data)
     }
 
     if ((prev ^ data) & data) {
-        DB_PRINT("programming zero to one! addr=%lx  %x -> %x\n",
-                  addr, prev, data);
+        DB_PRINT("programming zero to one! addr=%" PRIx64 "  %" PRIx8
+                 " -> %" PRIx8 "\n", addr, prev, data);
     }
 
     if (s->pi->flags & WR_1) {
@@ -538,15 +538,16 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
     switch (s->state) {
 
     case STATE_PAGE_PROGRAM:
-        DB_PRINT("page program cur_addr=%lx data=%x\n", s->cur_addr,
-                 (uint8_t)tx);
+        DB_PRINT("page program cur_addr=%#" PRIx64 " data=%" PRIx8 "\n",
+                 s->cur_addr, (uint8_t)tx);
         flash_write8(s, s->cur_addr, (uint8_t)tx);
         s->cur_addr++;
         break;
 
     case STATE_READ:
         r = s->storage[s->cur_addr];
-        DB_PRINT("READ 0x%lx=%x\n", s->cur_addr, r);
+        DB_PRINT("READ 0x%" PRIx64 "=%" PRIx8 "\n", s->cur_addr,
+                 (uint8_t)r);
         s->cur_addr = (s->cur_addr + 1) % s->size;
         break;
 
commit 42bb9c9178ae7ac4c439172b1ae99cc29188a5c6
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:28:35 2013 +1000

    stream: Remove app argument hack
    
    The uint32_t *app argument doesn't exist in real hardware. It was a hack in
    xilinx_axidma/enet to fake the (secondary) control stream connection. Removed
    the argument and added the second stream to axienet/dma.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/core/stream.c b/hw/core/stream.c
index 5397a8d..e6a05a5 100644
--- a/hw/core/stream.c
+++ b/hw/core/stream.c
@@ -1,11 +1,11 @@
 #include "hw/stream.h"
 
 size_t
-stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app)
+stream_push(StreamSlave *sink, uint8_t *buf, size_t len)
 {
     StreamSlaveClass *k =  STREAM_SLAVE_GET_CLASS(sink);
 
-    return k->push(sink, buf, len, app);
+    return k->push(sink, buf, len);
 }
 
 bool
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index a5bf102..1c23762 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -35,6 +35,7 @@
 
 #define TYPE_XILINX_AXI_DMA "xlnx.axi-dma"
 #define TYPE_XILINX_AXI_DMA_DATA_STREAM "xilinx-axi-dma-data-stream"
+#define TYPE_XILINX_AXI_DMA_CONTROL_STREAM "xilinx-axi-dma-control-stream"
 
 #define XILINX_AXI_DMA(obj) \
      OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA)
@@ -43,12 +44,19 @@
      OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\
      TYPE_XILINX_AXI_DMA_DATA_STREAM)
 
+#define XILINX_AXI_DMA_CONTROL_STREAM(obj) \
+     OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\
+     TYPE_XILINX_AXI_DMA_CONTROL_STREAM)
+
 #define R_DMACR             (0x00 / 4)
 #define R_DMASR             (0x04 / 4)
 #define R_CURDESC           (0x08 / 4)
 #define R_TAILDESC          (0x10 / 4)
 #define R_MAX               (0x30 / 4)
 
+#define CONTROL_PAYLOAD_WORDS 5
+#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t)))
+
 typedef struct XilinxAXIDMA XilinxAXIDMA;
 typedef struct XilinxAXIDMAStreamSlave XilinxAXIDMAStreamSlave;
 
@@ -73,7 +81,7 @@ struct SDesc {
     uint64_t reserved;
     uint32_t control;
     uint32_t status;
-    uint32_t app[6];
+    uint8_t app[CONTROL_PAYLOAD_SIZE];
 };
 
 enum {
@@ -101,6 +109,7 @@ struct Stream {
     int pos;
     unsigned int complete_cnt;
     uint32_t regs[R_MAX];
+    uint8_t app[20];
 };
 
 struct XilinxAXIDMAStreamSlave {
@@ -113,8 +122,10 @@ struct XilinxAXIDMA {
     SysBusDevice busdev;
     MemoryRegion iomem;
     uint32_t freqhz;
-    StreamSlave *tx_dev;
+    StreamSlave *tx_data_dev;
+    StreamSlave *tx_control_dev;
     XilinxAXIDMAStreamSlave rx_data_dev;
+    XilinxAXIDMAStreamSlave rx_control_dev;
 
     struct Stream streams[2];
 
@@ -185,7 +196,6 @@ static void stream_desc_show(struct SDesc *d)
 static void stream_desc_load(struct Stream *s, hwaddr addr)
 {
     struct SDesc *d = &s->desc;
-    int i;
 
     cpu_physical_memory_read(addr, (void *) d, sizeof *d);
 
@@ -194,24 +204,17 @@ static void stream_desc_load(struct Stream *s, hwaddr addr)
     d->nxtdesc = le64_to_cpu(d->nxtdesc);
     d->control = le32_to_cpu(d->control);
     d->status = le32_to_cpu(d->status);
-    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
-        d->app[i] = le32_to_cpu(d->app[i]);
-    }
 }
 
 static void stream_desc_store(struct Stream *s, hwaddr addr)
 {
     struct SDesc *d = &s->desc;
-    int i;
 
     /* Convert from host endianness into LE.  */
     d->buffer_address = cpu_to_le64(d->buffer_address);
     d->nxtdesc = cpu_to_le64(d->nxtdesc);
     d->control = cpu_to_le32(d->control);
     d->status = cpu_to_le32(d->status);
-    for (i = 0; i < ARRAY_SIZE(d->app); i++) {
-        d->app[i] = cpu_to_le32(d->app[i]);
-    }
     cpu_physical_memory_write(addr, (void *) d, sizeof *d);
 }
 
@@ -263,13 +266,12 @@ static void stream_complete(struct Stream *s)
     }
 }
 
-static void stream_process_mem2s(struct Stream *s,
-                                 StreamSlave *tx_dev)
+static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev,
+                                 StreamSlave *tx_control_dev)
 {
     uint32_t prev_d;
     unsigned char txbuf[16 * 1024];
     unsigned int txlen;
-    uint32_t app[6];
 
     if (!stream_running(s) || stream_idle(s)) {
         return;
@@ -285,7 +287,7 @@ static void stream_process_mem2s(struct Stream *s,
 
         if (stream_desc_sof(&s->desc)) {
             s->pos = 0;
-            memcpy(app, s->desc.app, sizeof app);
+            stream_push(tx_control_dev, s->desc.app, sizeof(s->desc.app));
         }
 
         txlen = s->desc.control & SDESC_CTRL_LEN_MASK;
@@ -299,7 +301,7 @@ static void stream_process_mem2s(struct Stream *s,
         s->pos += txlen;
 
         if (stream_desc_eof(&s->desc)) {
-            stream_push(tx_dev, txbuf, s->pos, app);
+            stream_push(tx_data_dev, txbuf, s->pos);
             s->pos = 0;
             stream_complete(s);
         }
@@ -319,7 +321,7 @@ static void stream_process_mem2s(struct Stream *s,
 }
 
 static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf,
-                                   size_t len, uint32_t *app)
+                                   size_t len)
 {
     uint32_t prev_d;
     unsigned int rxlen;
@@ -350,12 +352,8 @@ static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf,
 
         /* Update the descriptor.  */
         if (!len) {
-            int i;
-
             stream_complete(s);
-            for (i = 0; i < 5; i++) {
-                s->desc.app[i] = app[i];
-            }
+            memcpy(s->desc.app, s->app, sizeof(s->desc.app));
             s->desc.status |= SDESC_STATUS_EOF;
         }
 
@@ -386,6 +384,22 @@ static void xilinx_axidma_reset(DeviceState *dev)
     }
 }
 
+static size_t
+xilinx_axidma_control_stream_push(StreamSlave *obj, unsigned char *buf,
+                                  size_t len)
+{
+    XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(obj);
+    struct Stream *s = &cs->dma->streams[1];
+
+    if (len != CONTROL_PAYLOAD_SIZE) {
+        hw_error("AXI DMA requires %d byte control stream payload\n",
+                 (int)CONTROL_PAYLOAD_SIZE);
+    }
+
+    memcpy(s->app, buf, len);
+    return len;
+}
+
 static bool
 xilinx_axidma_data_stream_can_push(StreamSlave *obj,
                                    StreamCanPushNotifyFn notify,
@@ -404,17 +418,13 @@ xilinx_axidma_data_stream_can_push(StreamSlave *obj,
 }
 
 static size_t
-xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len,
-                               uint32_t *app)
+xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len)
 {
     XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj);
     struct Stream *s = &ds->dma->streams[1];
     size_t ret;
 
-    if (!app) {
-        hw_error("No stream app data!\n");
-    }
-    ret = stream_process_s2mem(s, buf, len, app);
+    ret = stream_process_s2mem(s, buf, len);
     stream_update_irq(s);
     return ret;
 }
@@ -495,7 +505,7 @@ static void axidma_write(void *opaque, hwaddr addr,
             s->regs[addr] = value;
             s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle.  */
             if (!sid) {
-                stream_process_mem2s(s, d->tx_dev);
+                stream_process_mem2s(s, d->tx_data_dev, d->tx_control_dev);
             }
             break;
         default:
@@ -521,14 +531,19 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp)
 {
     XilinxAXIDMA *s = XILINX_AXI_DMA(dev);
     XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(&s->rx_data_dev);
+    XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(
+                                                            &s->rx_control_dev);
     Error *local_errp = NULL;
 
     object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA,
                              (Object **)&ds->dma, &local_errp);
+    object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA,
+                             (Object **)&cs->dma, &local_errp);
     if (local_errp) {
         goto xilinx_axidma_realize_fail;
     }
     object_property_set_link(OBJECT(ds), OBJECT(s), "dma", &local_errp);
+    object_property_set_link(OBJECT(cs), OBJECT(s), "dma", &local_errp);
     if (local_errp) {
         goto xilinx_axidma_realize_fail;
     }
@@ -556,12 +571,21 @@ static void xilinx_axidma_init(Object *obj)
     Error *errp = NULL;
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
-                             (Object **) &s->tx_dev, NULL);
+                             (Object **) &s->tx_data_dev, &errp);
+    assert_no_error(errp);
+    object_property_add_link(obj, "axistream-control-connected",
+                             TYPE_STREAM_SLAVE,
+                             (Object **) &s->tx_control_dev, &errp);
+    assert_no_error(errp);
 
     object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_DMA_DATA_STREAM);
+    object_initialize(&s->rx_control_dev, TYPE_XILINX_AXI_DMA_CONTROL_STREAM);
     object_property_add_child(OBJECT(s), "axistream-connected-target",
                               (Object *)&s->rx_data_dev, &errp);
     assert_no_error(errp);
+    object_property_add_child(OBJECT(s), "axistream-control-connected-target",
+                              (Object *)&s->rx_control_dev, &errp);
+    assert_no_error(errp);
 
     sysbus_init_irq(sbd, &s->streams[0].irq);
     sysbus_init_irq(sbd, &s->streams[1].irq);
@@ -590,6 +614,10 @@ static StreamSlaveClass xilinx_axidma_data_stream_class = {
     .can_push = xilinx_axidma_data_stream_can_push,
 };
 
+static StreamSlaveClass xilinx_axidma_control_stream_class = {
+    .push = xilinx_axidma_control_stream_push,
+};
+
 static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data)
 {
     StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
@@ -618,10 +646,23 @@ static const TypeInfo xilinx_axidma_data_stream_info = {
     }
 };
 
+static const TypeInfo xilinx_axidma_control_stream_info = {
+    .name          = TYPE_XILINX_AXI_DMA_CONTROL_STREAM,
+    .parent        = TYPE_OBJECT,
+    .instance_size = sizeof(struct XilinxAXIDMAStreamSlave),
+    .class_init    = xilinx_axidma_stream_class_init,
+    .class_data    = &xilinx_axidma_control_stream_class,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_STREAM_SLAVE },
+        { }
+    }
+};
+
 static void xilinx_axidma_register_types(void)
 {
     type_register_static(&axidma_info);
     type_register_static(&xilinx_axidma_data_stream_info);
+    type_register_static(&xilinx_axidma_control_stream_info);
 }
 
 type_init(xilinx_axidma_register_types)
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 75ff2ac..3340468 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -79,7 +79,7 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
     const char *cpu_model = args->cpu_model;
     MemoryRegion *address_space_mem = get_system_memory();
     DeviceState *dev, *dma, *eth0;
-    Object *peer;
+    Object *ds, *cs;
     MicroBlazeCPU *cpu;
     SysBusDevice *busdev;
     CPUMBState *env;
@@ -140,15 +140,20 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
     object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma),
                               NULL);
 
-    peer = object_property_get_link(OBJECT(dma),
-                                    "axistream-connected-target", NULL);
-    xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(peer),
-                            0x82780000, irq[3], 0x1000, 0x1000);
-
-    peer = object_property_get_link(OBJECT(eth0),
-                                    "axistream-connected-target", NULL);
-    xilinx_axidma_init(dma, STREAM_SLAVE(peer), 0x84600000, irq[1], irq[0],
-                       100 * 1000000);
+    ds = object_property_get_link(OBJECT(dma),
+                                  "axistream-connected-target", NULL);
+    cs = object_property_get_link(OBJECT(dma),
+                                  "axistream-control-connected-target", NULL);
+    xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(ds),
+                            STREAM_SLAVE(cs), 0x82780000, irq[3], 0x1000,
+                            0x1000);
+
+    ds = object_property_get_link(OBJECT(eth0),
+                                  "axistream-connected-target", NULL);
+    cs = object_property_get_link(OBJECT(eth0),
+                                  "axistream-control-connected-target", NULL);
+    xilinx_axidma_init(dma, STREAM_SLAVE(ds), STREAM_SLAVE(cs), 0x84600000,
+                       irq[1], irq[0], 100 * 1000000);
 
     {
         SSIBus *spi;
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 544c3ec..8989e95 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -34,6 +34,7 @@
 
 #define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet"
 #define TYPE_XILINX_AXI_ENET_DATA_STREAM "xilinx-axienet-data-stream"
+#define TYPE_XILINX_AXI_ENET_CONTROL_STREAM "xilinx-axienet-control-stream"
 
 #define XILINX_AXI_ENET(obj) \
      OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET)
@@ -42,12 +43,19 @@
      OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\
      TYPE_XILINX_AXI_ENET_DATA_STREAM)
 
+#define XILINX_AXI_ENET_CONTROL_STREAM(obj) \
+     OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\
+     TYPE_XILINX_AXI_ENET_CONTROL_STREAM)
+
 /* Advertisement control register. */
 #define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
 #define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
 #define ADVERTISE_100HALF       0x0080  /* Try for 100mbps half-duplex */
 #define ADVERTISE_100FULL       0x0100  /* Try for 100mbps full-duplex */
 
+#define CONTROL_PAYLOAD_WORDS 5
+#define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t)))
+
 struct PHY {
     uint32_t regs[32];
 
@@ -329,8 +337,10 @@ struct XilinxAXIEnet {
     SysBusDevice busdev;
     MemoryRegion iomem;
     qemu_irq irq;
-    StreamSlave *tx_dev;
+    StreamSlave *tx_data_dev;
+    StreamSlave *tx_control_dev;
     XilinxAXIEnetStreamSlave rx_data_dev;
+    XilinxAXIEnetStreamSlave rx_control_dev;
     NICState *nic;
     NICConf conf;
 
@@ -381,11 +391,14 @@ struct XilinxAXIEnet {
     /* 32K x 1 lookup filter.  */
     uint32_t ext_mtable[1024];
 
+    uint32_t hdr[CONTROL_PAYLOAD_WORDS];
 
     uint8_t *rxmem;
-    uint32_t *rxapp;
     uint32_t rxsize;
     uint32_t rxpos;
+
+    uint8_t rxapp[CONTROL_PAYLOAD_SIZE];
+    uint32_t rxappsize;
 };
 
 static void axienet_rx_reset(XilinxAXIEnet *s)
@@ -670,14 +683,22 @@ static void axienet_eth_rx_notify(void *opaque)
 {
     XilinxAXIEnet *s = XILINX_AXI_ENET(opaque);
 
-    while (s->rxsize && stream_can_push(s->tx_dev, axienet_eth_rx_notify, s)) {
-        size_t ret = stream_push(s->tx_dev, (void *)s->rxmem + s->rxpos,
-                                 s->rxsize, s->rxapp);
+    while (s->rxappsize && stream_can_push(s->tx_control_dev,
+                                           axienet_eth_rx_notify, s)) {
+        size_t ret = stream_push(s->tx_control_dev,
+                                 (void *)s->rxapp + CONTROL_PAYLOAD_SIZE
+                                 - s->rxappsize, s->rxappsize);
+        s->rxappsize -= ret;
+    }
+
+    while (s->rxsize && stream_can_push(s->tx_data_dev,
+                                        axienet_eth_rx_notify, s)) {
+        size_t ret = stream_push(s->tx_data_dev, (void *)s->rxmem + s->rxpos,
+                                 s->rxsize);
         s->rxsize -= ret;
         s->rxpos += ret;
         if (!s->rxsize) {
             s->regs[R_IS] |= IS_RX_COMPLETE;
-            g_free(s->rxapp);
         }
     }
     enet_update_irq(s);
@@ -689,7 +710,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
     static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
                                               0xff, 0xff, 0xff};
     static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
-    uint32_t app[6] = {0};
+    uint32_t app[CONTROL_PAYLOAD_WORDS] = {0};
     int promisc = s->fmi & (1 << 31);
     int unicast, broadcast, multicast, ip_multicast = 0;
     uint32_t csum32;
@@ -822,7 +843,11 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
 
     s->rxsize = size;
     s->rxpos = 0;
-    s->rxapp = g_memdup(app, sizeof(app));
+    for (i = 0; i < ARRAY_SIZE(app); ++i) {
+        app[i] = cpu_to_le32(app[i]);
+    }
+    s->rxappsize = CONTROL_PAYLOAD_SIZE;
+    memcpy(s->rxapp, app, s->rxappsize);
     axienet_eth_rx_notify(s);
 
     enet_update_irq(s);
@@ -838,8 +863,27 @@ static void eth_cleanup(NetClientState *nc)
 }
 
 static size_t
-xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
-                                uint32_t *hdr)
+xilinx_axienet_control_stream_push(StreamSlave *obj, uint8_t *buf, size_t len)
+{
+    int i;
+    XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM(obj);
+    XilinxAXIEnet *s = cs->enet;
+
+    if (len != CONTROL_PAYLOAD_SIZE) {
+        hw_error("AXI Enet requires %d byte control stream payload\n",
+                 (int)CONTROL_PAYLOAD_SIZE);
+    }
+
+    memcpy(s->hdr, buf, len);
+
+    for (i = 0; i < ARRAY_SIZE(s->hdr); ++i) {
+        s->hdr[i] = le32_to_cpu(s->hdr[i]);
+    }
+    return len;
+}
+
+static size_t
+xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size)
 {
     XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(obj);
     XilinxAXIEnet *s = ds->enet;
@@ -856,16 +900,16 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
         }
     }
 
-    if (hdr[0] & 1) {
-        unsigned int start_off = hdr[1] >> 16;
-        unsigned int write_off = hdr[1] & 0xffff;
+    if (s->hdr[0] & 1) {
+        unsigned int start_off = s->hdr[1] >> 16;
+        unsigned int write_off = s->hdr[1] & 0xffff;
         uint32_t tmp_csum;
         uint16_t csum;
 
         tmp_csum = net_checksum_add(size - start_off,
                                     (uint8_t *)buf + start_off);
         /* Accumulate the seed.  */
-        tmp_csum += hdr[2] & 0xffff;
+        tmp_csum += s->hdr[2] & 0xffff;
 
         /* Fold the 32bit partial checksum.  */
         csum = net_checksum_finish(tmp_csum);
@@ -896,14 +940,19 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp)
 {
     XilinxAXIEnet *s = XILINX_AXI_ENET(dev);
     XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(&s->rx_data_dev);
+    XilinxAXIEnetStreamSlave *cs = XILINX_AXI_ENET_CONTROL_STREAM(
+                                                            &s->rx_control_dev);
     Error *local_errp = NULL;
 
     object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet",
                              (Object **) &ds->enet, &local_errp);
+    object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet",
+                             (Object **) &cs->enet, &local_errp);
     if (local_errp) {
         goto xilinx_enet_realize_fail;
     }
     object_property_set_link(OBJECT(ds), OBJECT(s), "enet", &local_errp);
+    object_property_set_link(OBJECT(cs), OBJECT(s), "enet", &local_errp);
     if (local_errp) {
         goto xilinx_enet_realize_fail;
     }
@@ -934,13 +983,21 @@ static void xilinx_enet_init(Object *obj)
     Error *errp = NULL;
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
-                             (Object **) &s->tx_dev, &errp);
+                             (Object **) &s->tx_data_dev, &errp);
+    assert_no_error(errp);
+    object_property_add_link(obj, "axistream-control-connected",
+                             TYPE_STREAM_SLAVE,
+                             (Object **) &s->tx_control_dev, &errp);
     assert_no_error(errp);
 
     object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_ENET_DATA_STREAM);
+    object_initialize(&s->rx_control_dev, TYPE_XILINX_AXI_ENET_CONTROL_STREAM);
     object_property_add_child(OBJECT(s), "axistream-connected-target",
                               (Object *)&s->rx_data_dev, &errp);
     assert_no_error(errp);
+    object_property_add_child(OBJECT(s), "axistream-control-connected-target",
+                              (Object *)&s->rx_control_dev, &errp);
+    assert_no_error(errp);
 
     sysbus_init_irq(sbd, &s->irq);
 
@@ -992,10 +1049,23 @@ static const TypeInfo xilinx_enet_data_stream_info = {
     }
 };
 
+static const TypeInfo xilinx_enet_control_stream_info = {
+    .name          = TYPE_XILINX_AXI_ENET_CONTROL_STREAM,
+    .parent        = TYPE_OBJECT,
+    .instance_size = sizeof(struct XilinxAXIEnetStreamSlave),
+    .class_init    = xilinx_enet_stream_class_init,
+    .class_data    = xilinx_axienet_control_stream_push,
+    .interfaces = (InterfaceInfo[]) {
+            { TYPE_STREAM_SLAVE },
+            { }
+    }
+};
+
 static void xilinx_enet_register_types(void)
 {
     type_register_static(&xilinx_enet_info);
     type_register_static(&xilinx_enet_data_stream_info);
+    type_register_static(&xilinx_enet_control_stream_info);
 }
 
 type_init(xilinx_enet_register_types)
diff --git a/include/hw/stream.h b/include/hw/stream.h
index 5bc47a9..35eb083 100644
--- a/include/hw/stream.h
+++ b/include/hw/stream.h
@@ -43,12 +43,11 @@ typedef struct StreamSlaveClass {
      * @buf: Data to write
      * @len: Maximum number of bytes to write
      */
-    size_t (*push)(StreamSlave *obj, unsigned char *buf, size_t len,
-                                                    uint32_t *app);
+    size_t (*push)(StreamSlave *obj, unsigned char *buf, size_t len);
 } StreamSlaveClass;
 
 size_t
-stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app);
+stream_push(StreamSlave *sink, uint8_t *buf, size_t len);
 
 bool
 stream_can_push(StreamSlave *sink, StreamCanPushNotifyFn notify,
diff --git a/include/hw/xilinx.h b/include/hw/xilinx.h
index 6c1ee21..0c0251a 100644
--- a/include/hw/xilinx.h
+++ b/include/hw/xilinx.h
@@ -55,16 +55,19 @@ xilinx_ethlite_create(NICInfo *nd, hwaddr base, qemu_irq irq,
 }
 
 static inline void
-xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *peer,
-                        hwaddr base, qemu_irq irq, int txmem, int rxmem)
+xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *ds,
+                        StreamSlave *cs, hwaddr base, qemu_irq irq, int txmem,
+                        int rxmem)
 {
     Error *errp = NULL;
 
     qdev_set_nic_properties(dev, nd);
     qdev_prop_set_uint32(dev, "rxmem", rxmem);
     qdev_prop_set_uint32(dev, "txmem", txmem);
-    object_property_set_link(OBJECT(dev), OBJECT(peer), "axistream-connected",
-                             &errp);
+    object_property_set_link(OBJECT(dev), OBJECT(ds),
+                             "axistream-connected", &errp);
+    object_property_set_link(OBJECT(dev), OBJECT(cs),
+                             "axistream-control-connected", &errp);
     assert_no_error(errp);
     qdev_init_nofail(dev);
     sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
@@ -72,14 +75,16 @@ xilinx_axiethernet_init(DeviceState *dev, NICInfo *nd, StreamSlave *peer,
 }
 
 static inline void
-xilinx_axidma_init(DeviceState *dev, StreamSlave *peer, hwaddr base,
-                   qemu_irq irq, qemu_irq irq2, int freqhz)
+xilinx_axidma_init(DeviceState *dev, StreamSlave *ds, StreamSlave *cs,
+                   hwaddr base, qemu_irq irq, qemu_irq irq2, int freqhz)
 {
     Error *errp = NULL;
 
     qdev_prop_set_uint32(dev, "freqhz", freqhz);
-    object_property_set_link(OBJECT(dev), OBJECT(peer), "axistream-connected",
-                             &errp);
+    object_property_set_link(OBJECT(dev), OBJECT(ds),
+                             "axistream-connected", &errp);
+    object_property_set_link(OBJECT(dev), OBJECT(cs),
+                             "axistream-control-connected", &errp);
     assert_no_error(errp);
     qdev_init_nofail(dev);
 
commit 3630ae952a17c2107db25f397233536ef874558e
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:27:55 2013 +1000

    xilinx_axienet/dma: Implement rx path flow control
    
    Implement flow control for the RX data path from xilinx_axienet->xilinx_axidma.
    On short return from axidma, then ethernet sets up the notify callback to resume
    transfer from where it left off.
    
    This also allows the ethernet to track whether there is an in progress transaction
    and return false from ethernet can_receive() as appropriate.
    
    If the DMA backs up or is disabled it waits for enablement. When the rx stream IO
    region is touched, the can_push() notify function is called if set.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 80ce57f..a5bf102 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -117,6 +117,9 @@ struct XilinxAXIDMA {
     XilinxAXIDMAStreamSlave rx_data_dev;
 
     struct Stream streams[2];
+
+    StreamCanPushNotifyFn notify;
+    void *notify_opaque;
 };
 
 /*
@@ -315,16 +318,16 @@ static void stream_process_mem2s(struct Stream *s,
     }
 }
 
-static void stream_process_s2mem(struct Stream *s,
-                                 unsigned char *buf, size_t len, uint32_t *app)
+static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf,
+                                   size_t len, uint32_t *app)
 {
     uint32_t prev_d;
     unsigned int rxlen;
-    int pos = 0;
+    size_t pos = 0;
     int sof = 1;
 
     if (!stream_running(s) || stream_idle(s)) {
-        return;
+        return 0;
     }
 
     while (len) {
@@ -369,6 +372,8 @@ static void stream_process_s2mem(struct Stream *s,
             break;
         }
     }
+
+    return pos;
 }
 
 static void xilinx_axidma_reset(DeviceState *dev)
@@ -381,19 +386,37 @@ static void xilinx_axidma_reset(DeviceState *dev)
     }
 }
 
+static bool
+xilinx_axidma_data_stream_can_push(StreamSlave *obj,
+                                   StreamCanPushNotifyFn notify,
+                                   void *notify_opaque)
+{
+    XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj);
+    struct Stream *s = &ds->dma->streams[1];
+
+    if (!stream_running(s) || stream_idle(s)) {
+        ds->dma->notify = notify;
+        ds->dma->notify_opaque = notify_opaque;
+        return false;
+    }
+
+    return true;
+}
+
 static size_t
 xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len,
                                uint32_t *app)
 {
     XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj);
     struct Stream *s = &ds->dma->streams[1];
+    size_t ret;
 
     if (!app) {
         hw_error("No stream app data!\n");
     }
-    stream_process_s2mem(s, buf, len, app);
+    ret = stream_process_s2mem(s, buf, len, app);
     stream_update_irq(s);
-    return len;
+    return ret;
 }
 
 static uint64_t axidma_read(void *opaque, hwaddr addr,
@@ -481,6 +504,10 @@ static void axidma_write(void *opaque, hwaddr addr,
             s->regs[addr] = value;
             break;
     }
+    if (sid == 1 && d->notify) {
+        d->notify(d->notify_opaque);
+        d->notify = NULL;
+    }
     stream_update_irq(s);
 }
 
@@ -558,11 +585,17 @@ static void axidma_class_init(ObjectClass *klass, void *data)
     dc->props = axidma_properties;
 }
 
+static StreamSlaveClass xilinx_axidma_data_stream_class = {
+    .push = xilinx_axidma_data_stream_push,
+    .can_push = xilinx_axidma_data_stream_can_push,
+};
+
 static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data)
 {
     StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 
-    ssc->push = data;
+    ssc->push = ((StreamSlaveClass *)data)->push;
+    ssc->can_push = ((StreamSlaveClass *)data)->can_push;
 }
 
 static const TypeInfo axidma_info = {
@@ -578,7 +611,7 @@ static const TypeInfo xilinx_axidma_data_stream_info = {
     .parent        = TYPE_OBJECT,
     .instance_size = sizeof(struct XilinxAXIDMAStreamSlave),
     .class_init    = xilinx_axidma_stream_class_init,
-    .class_data    = xilinx_axidma_data_stream_push,
+    .class_data    = &xilinx_axidma_data_stream_class,
     .interfaces = (InterfaceInfo[]) {
         { TYPE_STREAM_SLAVE },
         { }
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 6d27546..544c3ec 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -383,6 +383,9 @@ struct XilinxAXIEnet {
 
 
     uint8_t *rxmem;
+    uint32_t *rxapp;
+    uint32_t rxsize;
+    uint32_t rxpos;
 };
 
 static void axienet_rx_reset(XilinxAXIEnet *s)
@@ -645,7 +648,7 @@ static int eth_can_rx(NetClientState *nc)
     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
 
     /* RX enabled?  */
-    return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
+    return !s->rxsize && !axienet_rx_resetting(s) && axienet_rx_enabled(s);
 }
 
 static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
@@ -663,6 +666,23 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
     return match;
 }
 
+static void axienet_eth_rx_notify(void *opaque)
+{
+    XilinxAXIEnet *s = XILINX_AXI_ENET(opaque);
+
+    while (s->rxsize && stream_can_push(s->tx_dev, axienet_eth_rx_notify, s)) {
+        size_t ret = stream_push(s->tx_dev, (void *)s->rxmem + s->rxpos,
+                                 s->rxsize, s->rxapp);
+        s->rxsize -= ret;
+        s->rxpos += ret;
+        if (!s->rxsize) {
+            s->regs[R_IS] |= IS_RX_COMPLETE;
+            g_free(s->rxapp);
+        }
+    }
+    enet_update_irq(s);
+}
+
 static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
 {
     XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
@@ -800,9 +820,11 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
     /* Good frame.  */
     app[2] |= 1 << 6;
 
-    stream_push(s->tx_dev, (void *)s->rxmem, size, app);
+    s->rxsize = size;
+    s->rxpos = 0;
+    s->rxapp = g_memdup(app, sizeof(app));
+    axienet_eth_rx_notify(s);
 
-    s->regs[R_IS] |= IS_RX_COMPLETE;
     enet_update_irq(s);
     return size;
 }
commit 35e60bfdbc14ce31bba55cc82144f8a2a82ede68
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:27:16 2013 +1000

    stream: Add flow control API
    
    Add basic flow control to stream. A stream slave may return short, indicating
    that it is not capable of accepting any more data at the present time. Polling
    or a callback can be used via the can_push() function to determine when the
    slave can receive again.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/core/stream.c b/hw/core/stream.c
index a07d6a5..5397a8d 100644
--- a/hw/core/stream.c
+++ b/hw/core/stream.c
@@ -1,11 +1,20 @@
 #include "hw/stream.h"
 
-void
+size_t
 stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app)
 {
     StreamSlaveClass *k =  STREAM_SLAVE_GET_CLASS(sink);
 
-    k->push(sink, buf, len, app);
+    return k->push(sink, buf, len, app);
+}
+
+bool
+stream_can_push(StreamSlave *sink, StreamCanPushNotifyFn notify,
+                void *notify_opaque)
+{
+    StreamSlaveClass *k =  STREAM_SLAVE_GET_CLASS(sink);
+
+    return k->can_push ? k->can_push(sink, notify, notify_opaque) : true;
 }
 
 static const TypeInfo stream_slave_info = {
diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 2bbfea1..80ce57f 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -381,7 +381,7 @@ static void xilinx_axidma_reset(DeviceState *dev)
     }
 }
 
-static void
+static size_t
 xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len,
                                uint32_t *app)
 {
@@ -393,6 +393,7 @@ xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len,
     }
     stream_process_s2mem(s, buf, len, app);
     stream_update_irq(s);
+    return len;
 }
 
 static uint64_t axidma_read(void *opaque, hwaddr addr,
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index f4638b2..6d27546 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -815,7 +815,7 @@ static void eth_cleanup(NetClientState *nc)
     g_free(s);
 }
 
-static void
+static size_t
 xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
                                 uint32_t *hdr)
 {
@@ -824,13 +824,13 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
 
     /* TX enable ?  */
     if (!(s->tc & TC_TX)) {
-        return;
+        return size;
     }
 
     /* Jumbo or vlan sizes ?  */
     if (!(s->tc & TC_JUM)) {
         if (size > 1518 && size <= 1522 && !(s->tc & TC_VLAN)) {
-            return;
+            return size;
         }
     }
 
@@ -858,6 +858,8 @@ xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
     s->stats.tx_bytes += size;
     s->regs[R_IS] |= IS_TX_COMPLETE;
     enet_update_irq(s);
+
+    return size;
 }
 
 static NetClientInfo net_xilinx_enet_info = {
diff --git a/include/hw/stream.h b/include/hw/stream.h
index f6137d6..5bc47a9 100644
--- a/include/hw/stream.h
+++ b/include/hw/stream.h
@@ -18,14 +18,41 @@ typedef struct StreamSlave {
     Object Parent;
 } StreamSlave;
 
+typedef void (*StreamCanPushNotifyFn)(void *opaque);
+
 typedef struct StreamSlaveClass {
     InterfaceClass parent;
-
-    void (*push)(StreamSlave *obj, unsigned char *buf, size_t len,
+    /**
+     * can push - determine if a stream slave is capable of accepting at least
+     * one byte of data. Returns false if cannot accept. If not implemented, the
+     * slave is assumed to always be capable of recieveing.
+     * @notify: Optional callback that the slave will call when the slave is
+     * capable of recieving again. Only called if false is returned.
+     * @notify_opaque: opaque data to pass to notify call.
+     */
+    bool (*can_push)(StreamSlave *obj, StreamCanPushNotifyFn notify,
+                     void *notify_opaque);
+    /**
+     * push - push data to a Stream slave. The number of bytes pushed is
+     * returned. If the slave short returns, the master must wait before trying
+     * again, the slave may continue to just return 0 waiting for the vm time to
+     * advance. The can_push() function can be used to trap the point in time
+     * where the slave is ready to recieve again, otherwise polling on a QEMU
+     * timer will work.
+     * @obj: Stream slave to push to
+     * @buf: Data to write
+     * @len: Maximum number of bytes to write
+     */
+    size_t (*push)(StreamSlave *obj, unsigned char *buf, size_t len,
                                                     uint32_t *app);
 } StreamSlaveClass;
 
-void
+size_t
 stream_push(StreamSlave *sink, uint8_t *buf, size_t len, uint32_t *app);
 
+bool
+stream_can_push(StreamSlave *sink, StreamCanPushNotifyFn notify,
+                void *notify_opaque);
+
+
 #endif /* STREAM_H */
commit 210914e29975d17e635f9e8c1f7478c0ed7a208f
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:26:37 2013 +1000

    xilinx_axidma: Fix rx/tx halted bit.
    
    If there is no DMA buffer descriptor, the DMA halts, not idles.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 02700ea..2bbfea1 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -276,7 +276,7 @@ static void stream_process_mem2s(struct Stream *s,
         stream_desc_load(s, s->regs[R_CURDESC]);
 
         if (s->desc.status & SDESC_STATUS_COMPLETE) {
-            s->regs[R_DMASR] |= DMASR_IDLE;
+            s->regs[R_DMASR] |= DMASR_HALTED;
             break;
         }
 
@@ -331,7 +331,7 @@ static void stream_process_s2mem(struct Stream *s,
         stream_desc_load(s, s->regs[R_CURDESC]);
 
         if (s->desc.status & SDESC_STATUS_COMPLETE) {
-            s->regs[R_DMASR] |= DMASR_IDLE;
+            s->regs[R_DMASR] |= DMASR_HALTED;
             break;
         }
 
commit e1500e35c22be345cd04f77fa6993770b689eaa9
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:25:57 2013 +1000

    xilinx_axidma: Create Proxy object for stream
    
    Create a separate child object to proxy the stream slave connection. This is
    setup for future work where a second stream slave connection is needed. The
    new child object is created at qdev init time and is linked back to the parent
    (the ethernet device itself) automatically.
    
    Stream slave masters differentiate which slave connection they are connected to
    by linking to the proxy object rather than the parent.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 2c95765..02700ea 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -34,10 +34,15 @@
 #define D(x)
 
 #define TYPE_XILINX_AXI_DMA "xlnx.axi-dma"
+#define TYPE_XILINX_AXI_DMA_DATA_STREAM "xilinx-axi-dma-data-stream"
 
 #define XILINX_AXI_DMA(obj) \
      OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA)
 
+#define XILINX_AXI_DMA_DATA_STREAM(obj) \
+     OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\
+     TYPE_XILINX_AXI_DMA_DATA_STREAM)
+
 #define R_DMACR             (0x00 / 4)
 #define R_DMASR             (0x04 / 4)
 #define R_CURDESC           (0x08 / 4)
@@ -45,6 +50,7 @@
 #define R_MAX               (0x30 / 4)
 
 typedef struct XilinxAXIDMA XilinxAXIDMA;
+typedef struct XilinxAXIDMAStreamSlave XilinxAXIDMAStreamSlave;
 
 enum {
     DMACR_RUNSTOP = 1,
@@ -97,11 +103,18 @@ struct Stream {
     uint32_t regs[R_MAX];
 };
 
+struct XilinxAXIDMAStreamSlave {
+    Object parent;
+
+    struct XilinxAXIDMA *dma;
+};
+
 struct XilinxAXIDMA {
     SysBusDevice busdev;
     MemoryRegion iomem;
     uint32_t freqhz;
     StreamSlave *tx_dev;
+    XilinxAXIDMAStreamSlave rx_data_dev;
 
     struct Stream streams[2];
 };
@@ -369,10 +382,11 @@ static void xilinx_axidma_reset(DeviceState *dev)
 }
 
 static void
-axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app)
+xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len,
+                               uint32_t *app)
 {
-    XilinxAXIDMA *d = XILINX_AXI_DMA(obj);
-    struct Stream *s = &d->streams[1];
+    XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj);
+    struct Stream *s = &ds->dma->streams[1];
 
     if (!app) {
         hw_error("No stream app data!\n");
@@ -478,6 +492,19 @@ static const MemoryRegionOps axidma_ops = {
 static void xilinx_axidma_realize(DeviceState *dev, Error **errp)
 {
     XilinxAXIDMA *s = XILINX_AXI_DMA(dev);
+    XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(&s->rx_data_dev);
+    Error *local_errp = NULL;
+
+    object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA,
+                             (Object **)&ds->dma, &local_errp);
+    if (local_errp) {
+        goto xilinx_axidma_realize_fail;
+    }
+    object_property_set_link(OBJECT(ds), OBJECT(s), "dma", &local_errp);
+    if (local_errp) {
+        goto xilinx_axidma_realize_fail;
+    }
+
     int i;
 
     for (i = 0; i < 2; i++) {
@@ -486,16 +513,28 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp)
         s->streams[i].ptimer = ptimer_init(s->streams[i].bh);
         ptimer_set_freq(s->streams[i].ptimer, s->freqhz);
     }
+    return;
+
+xilinx_axidma_realize_fail:
+    if (!*errp) {
+        *errp = local_errp;
+    }
 }
 
 static void xilinx_axidma_init(Object *obj)
 {
     XilinxAXIDMA *s = XILINX_AXI_DMA(obj);
     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    Error *errp = NULL;
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                              (Object **) &s->tx_dev, NULL);
 
+    object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_DMA_DATA_STREAM);
+    object_property_add_child(OBJECT(s), "axistream-connected-target",
+                              (Object *)&s->rx_data_dev, &errp);
+    assert_no_error(errp);
+
     sysbus_init_irq(sbd, &s->streams[0].irq);
     sysbus_init_irq(sbd, &s->streams[1].irq);
 
@@ -512,12 +551,17 @@ static Property axidma_properties[] = {
 static void axidma_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 
     dc->realize = xilinx_axidma_realize,
     dc->reset = xilinx_axidma_reset;
     dc->props = axidma_properties;
-    ssc->push = axidma_push;
+}
+
+static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data)
+{
+    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
+
+    ssc->push = data;
 }
 
 static const TypeInfo axidma_info = {
@@ -526,6 +570,14 @@ static const TypeInfo axidma_info = {
     .instance_size = sizeof(XilinxAXIDMA),
     .class_init    = axidma_class_init,
     .instance_init = xilinx_axidma_init,
+};
+
+static const TypeInfo xilinx_axidma_data_stream_info = {
+    .name          = TYPE_XILINX_AXI_DMA_DATA_STREAM,
+    .parent        = TYPE_OBJECT,
+    .instance_size = sizeof(struct XilinxAXIDMAStreamSlave),
+    .class_init    = xilinx_axidma_stream_class_init,
+    .class_data    = xilinx_axidma_data_stream_push,
     .interfaces = (InterfaceInfo[]) {
         { TYPE_STREAM_SLAVE },
         { }
@@ -535,6 +587,7 @@ static const TypeInfo axidma_info = {
 static void xilinx_axidma_register_types(void)
 {
     type_register_static(&axidma_info);
+    type_register_static(&xilinx_axidma_data_stream_info);
 }
 
 type_init(xilinx_axidma_register_types)
diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 0bad362..75ff2ac 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -140,8 +140,10 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
     object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma),
                               NULL);
 
-    xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(dma),
-                                   0x82780000, irq[3], 0x1000, 0x1000);
+    peer = object_property_get_link(OBJECT(dma),
+                                    "axistream-connected-target", NULL);
+    xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(peer),
+                            0x82780000, irq[3], 0x1000, 0x1000);
 
     peer = object_property_get_link(OBJECT(eth0),
                                     "axistream-connected-target", NULL);
commit 55b3e0c2f8fb7ef6d7929de23f3ae2a3d805ebff
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:25:18 2013 +1000

    xilinx_axienet: Create Proxy object for stream
    
    Create a separate child object to proxy the stream slave connection. This is
    setup for future work where a second stream slave connection is needed. The
    new child object is created at qdev init time and is linked back to the parent
    (the ethernet device itself) automatically.
    
    Stream slave masters differentiate which slave connection they are connected to
    by linking to the proxy object rather than the parent.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index a5e9b35..0bad362 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -79,6 +79,7 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
     const char *cpu_model = args->cpu_model;
     MemoryRegion *address_space_mem = get_system_memory();
     DeviceState *dev, *dma, *eth0;
+    Object *peer;
     MicroBlazeCPU *cpu;
     SysBusDevice *busdev;
     CPUMBState *env;
@@ -142,7 +143,9 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
     xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(dma),
                                    0x82780000, irq[3], 0x1000, 0x1000);
 
-    xilinx_axidma_init(dma, STREAM_SLAVE(eth0), 0x84600000, irq[1], irq[0],
+    peer = object_property_get_link(OBJECT(eth0),
+                                    "axistream-connected-target", NULL);
+    xilinx_axidma_init(dma, STREAM_SLAVE(peer), 0x84600000, irq[1], irq[0],
                        100 * 1000000);
 
     {
diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index aa358cc..f4638b2 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -33,10 +33,15 @@
 #define DPHY(x)
 
 #define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet"
+#define TYPE_XILINX_AXI_ENET_DATA_STREAM "xilinx-axienet-data-stream"
 
 #define XILINX_AXI_ENET(obj) \
      OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET)
 
+#define XILINX_AXI_ENET_DATA_STREAM(obj) \
+     OBJECT_CHECK(XilinxAXIEnetStreamSlave, (obj),\
+     TYPE_XILINX_AXI_ENET_DATA_STREAM)
+
 /* Advertisement control register. */
 #define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
 #define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
@@ -311,13 +316,21 @@ struct TEMAC  {
     void *parent;
 };
 
+typedef struct XilinxAXIEnetStreamSlave XilinxAXIEnetStreamSlave;
 typedef struct XilinxAXIEnet XilinxAXIEnet;
 
+struct XilinxAXIEnetStreamSlave {
+    Object parent;
+
+    struct XilinxAXIEnet *enet;
+} ;
+
 struct XilinxAXIEnet {
     SysBusDevice busdev;
     MemoryRegion iomem;
     qemu_irq irq;
     StreamSlave *tx_dev;
+    XilinxAXIEnetStreamSlave rx_data_dev;
     NICState *nic;
     NICConf conf;
 
@@ -803,9 +816,11 @@ static void eth_cleanup(NetClientState *nc)
 }
 
 static void
-axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr)
+xilinx_axienet_data_stream_push(StreamSlave *obj, uint8_t *buf, size_t size,
+                                uint32_t *hdr)
 {
-    XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+    XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(obj);
+    XilinxAXIEnet *s = ds->enet;
 
     /* TX enable ?  */
     if (!(s->tc & TC_TX)) {
@@ -856,6 +871,18 @@ static NetClientInfo net_xilinx_enet_info = {
 static void xilinx_enet_realize(DeviceState *dev, Error **errp)
 {
     XilinxAXIEnet *s = XILINX_AXI_ENET(dev);
+    XilinxAXIEnetStreamSlave *ds = XILINX_AXI_ENET_DATA_STREAM(&s->rx_data_dev);
+    Error *local_errp = NULL;
+
+    object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet",
+                             (Object **) &ds->enet, &local_errp);
+    if (local_errp) {
+        goto xilinx_enet_realize_fail;
+    }
+    object_property_set_link(OBJECT(ds), OBJECT(s), "enet", &local_errp);
+    if (local_errp) {
+        goto xilinx_enet_realize_fail;
+    }
 
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
     s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
@@ -868,6 +895,12 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp)
     s->TEMAC.parent = s;
 
     s->rxmem = g_malloc(s->c_rxmem);
+    return;
+
+xilinx_enet_realize_fail:
+    if (!*errp) {
+        *errp = local_errp;
+    }
 }
 
 static void xilinx_enet_init(Object *obj)
@@ -880,6 +913,11 @@ static void xilinx_enet_init(Object *obj)
                              (Object **) &s->tx_dev, &errp);
     assert_no_error(errp);
 
+    object_initialize(&s->rx_data_dev, TYPE_XILINX_AXI_ENET_DATA_STREAM);
+    object_property_add_child(OBJECT(s), "axistream-connected-target",
+                              (Object *)&s->rx_data_dev, &errp);
+    assert_no_error(errp);
+
     sysbus_init_irq(sbd, &s->irq);
 
     memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000);
@@ -897,12 +935,17 @@ static Property xilinx_enet_properties[] = {
 static void xilinx_enet_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 
     dc->realize = xilinx_enet_realize;
     dc->props = xilinx_enet_properties;
     dc->reset = xilinx_axienet_reset;
-    ssc->push = axienet_stream_push;
+}
+
+static void xilinx_enet_stream_class_init(ObjectClass *klass, void *data)
+{
+    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
+
+    ssc->push = data;
 }
 
 static const TypeInfo xilinx_enet_info = {
@@ -911,6 +954,14 @@ static const TypeInfo xilinx_enet_info = {
     .instance_size = sizeof(XilinxAXIEnet),
     .class_init    = xilinx_enet_class_init,
     .instance_init = xilinx_enet_init,
+};
+
+static const TypeInfo xilinx_enet_data_stream_info = {
+    .name          = TYPE_XILINX_AXI_ENET_DATA_STREAM,
+    .parent        = TYPE_OBJECT,
+    .instance_size = sizeof(struct XilinxAXIEnetStreamSlave),
+    .class_init    = xilinx_enet_stream_class_init,
+    .class_data    = xilinx_axienet_data_stream_push,
     .interfaces = (InterfaceInfo[]) {
             { TYPE_STREAM_SLAVE },
             { }
@@ -920,6 +971,7 @@ static const TypeInfo xilinx_enet_info = {
 static void xilinx_enet_register_types(void)
 {
     type_register_static(&xilinx_enet_info);
+    type_register_static(&xilinx_enet_data_stream_info);
 }
 
 type_init(xilinx_enet_register_types)
commit b19ceaad0dee6bca619ba2de19003b50e1a8cb01
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:24:39 2013 +1000

    petalogix_ml605_mmu: Attach ethernet to machine
    
    Explicitly make the ethernet a child of the machine. This is needed to set
    and use links pre-realize. Also makes the ethernet initialization consistent
    with its peer DMA.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Reviewed-by: Andreas Färber <afaerber at suse.de>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index 70e7886..a5e9b35 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -134,6 +134,8 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
     dma = qdev_create(NULL, "xlnx.axi-dma");
 
     /* FIXME: attach to the sysbus instead */
+    object_property_add_child(qdev_get_machine(), "xilinx-eth", OBJECT(eth0),
+                              NULL);
     object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma),
                               NULL);
 
commit 54ff2a399fc9453fa75f9223d7d2cd912f17fb4b
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:23:59 2013 +1000

    petalogix_ml605_mmu: Fix machine node attachment
    
    Just attach devices straight to the root machine node, rather than the
    "unattached node"
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Reviewed-by: Andreas Färber <afaerber at suse.de>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c
index ae7ff44..70e7886 100644
--- a/hw/microblaze/petalogix_ml605_mmu.c
+++ b/hw/microblaze/petalogix_ml605_mmu.c
@@ -134,8 +134,8 @@ petalogix_ml605_init(QEMUMachineInitArgs *args)
     dma = qdev_create(NULL, "xlnx.axi-dma");
 
     /* FIXME: attach to the sysbus instead */
-    object_property_add_child(container_get(qdev_get_machine(), "/unattached"),
-                                  "xilinx-dma", OBJECT(dma), NULL);
+    object_property_add_child(qdev_get_machine(), "xilinx-dma", OBJECT(dma),
+                              NULL);
 
     xilinx_axiethernet_init(eth0, &nd_table[0], STREAM_SLAVE(dma),
                                    0x82780000, irq[3], 0x1000, 0x1000);
commit e65436634dc4f3b43ba914b7e38fe696853da3ca
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:23:20 2013 +1000

    xilinx_axidma: converted init->realize
    
    The prescribed transition from SysBusDevice::init to Device::realize. I'm going
    with Andreas suggestion to move the sysbus foo to Object::init for early IRQ
    visibility.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 91b8f07..2c95765 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -27,6 +27,7 @@
 #include "hw/ptimer.h"
 #include "qemu/log.h"
 #include "hw/qdev-addr.h"
+#include "qapi/qmp/qerror.h"
 
 #include "hw/stream.h"
 
@@ -474,33 +475,33 @@ static const MemoryRegionOps axidma_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
-static int xilinx_axidma_init(SysBusDevice *dev)
+static void xilinx_axidma_realize(DeviceState *dev, Error **errp)
 {
     XilinxAXIDMA *s = XILINX_AXI_DMA(dev);
     int i;
 
-    sysbus_init_irq(dev, &s->streams[0].irq);
-    sysbus_init_irq(dev, &s->streams[1].irq);
-
-    memory_region_init_io(&s->iomem, &axidma_ops, s,
-                          "xlnx.axi-dma", R_MAX * 4 * 2);
-    sysbus_init_mmio(dev, &s->iomem);
-
     for (i = 0; i < 2; i++) {
         s->streams[i].nr = i;
         s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]);
         s->streams[i].ptimer = ptimer_init(s->streams[i].bh);
         ptimer_set_freq(s->streams[i].ptimer, s->freqhz);
     }
-    return 0;
 }
 
-static void xilinx_axidma_initfn(Object *obj)
+static void xilinx_axidma_init(Object *obj)
 {
     XilinxAXIDMA *s = XILINX_AXI_DMA(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                              (Object **) &s->tx_dev, NULL);
+
+    sysbus_init_irq(sbd, &s->streams[0].irq);
+    sysbus_init_irq(sbd, &s->streams[1].irq);
+
+    memory_region_init_io(&s->iomem, &axidma_ops, s,
+                          "xlnx.axi-dma", R_MAX * 4 * 2);
+    sysbus_init_mmio(sbd, &s->iomem);
 }
 
 static Property axidma_properties[] = {
@@ -511,10 +512,9 @@ static Property axidma_properties[] = {
 static void axidma_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
     StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 
-    k->init = xilinx_axidma_init;
+    dc->realize = xilinx_axidma_realize,
     dc->reset = xilinx_axidma_reset;
     dc->props = axidma_properties;
     ssc->push = axidma_push;
@@ -525,7 +525,7 @@ static const TypeInfo axidma_info = {
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(XilinxAXIDMA),
     .class_init    = axidma_class_init,
-    .instance_init = xilinx_axidma_initfn,
+    .instance_init = xilinx_axidma_init,
     .interfaces = (InterfaceInfo[]) {
         { TYPE_STREAM_SLAVE },
         { }
commit 897374db7db10a9c17fd4da4e089705b553b4ee1
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:22:41 2013 +1000

    xilinx_axidma: Register reset properly
    
    Register the reset function as the Device::reset function rather than
    explicitly call it from the sysbus::init.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 0b5bb44..91b8f07 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -357,6 +357,16 @@ static void stream_process_s2mem(struct Stream *s,
     }
 }
 
+static void xilinx_axidma_reset(DeviceState *dev)
+{
+    int i;
+    XilinxAXIDMA *s = XILINX_AXI_DMA(dev);
+
+    for (i = 0; i < 2; i++) {
+        stream_reset(&s->streams[i]);
+    }
+}
+
 static void
 axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app)
 {
@@ -477,7 +487,6 @@ static int xilinx_axidma_init(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->iomem);
 
     for (i = 0; i < 2; i++) {
-        stream_reset(&s->streams[i]);
         s->streams[i].nr = i;
         s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]);
         s->streams[i].ptimer = ptimer_init(s->streams[i].bh);
@@ -506,6 +515,7 @@ static void axidma_class_init(ObjectClass *klass, void *data)
     StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 
     k->init = xilinx_axidma_init;
+    dc->reset = xilinx_axidma_reset;
     dc->props = axidma_properties;
     ssc->push = axidma_push;
 }
commit cbde584f58b484a8200eb578a5ba81057488f0eb
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:22:02 2013 +1000

    xilinx_axidma: Defined and use type cast macro
    
    Standard QOM cast macro. Replaces usages of FROM_SYSBUS
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 4dd8a6b..0b5bb44 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -32,6 +32,11 @@
 
 #define D(x)
 
+#define TYPE_XILINX_AXI_DMA "xlnx.axi-dma"
+
+#define XILINX_AXI_DMA(obj) \
+     OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA)
+
 #define R_DMACR             (0x00 / 4)
 #define R_DMASR             (0x04 / 4)
 #define R_CURDESC           (0x08 / 4)
@@ -355,7 +360,7 @@ static void stream_process_s2mem(struct Stream *s,
 static void
 axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app)
 {
-    XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj));
+    XilinxAXIDMA *d = XILINX_AXI_DMA(obj);
     struct Stream *s = &d->streams[1];
 
     if (!app) {
@@ -461,7 +466,7 @@ static const MemoryRegionOps axidma_ops = {
 
 static int xilinx_axidma_init(SysBusDevice *dev)
 {
-    XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev);
+    XilinxAXIDMA *s = XILINX_AXI_DMA(dev);
     int i;
 
     sysbus_init_irq(dev, &s->streams[0].irq);
@@ -483,7 +488,7 @@ static int xilinx_axidma_init(SysBusDevice *dev)
 
 static void xilinx_axidma_initfn(Object *obj)
 {
-    XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+    XilinxAXIDMA *s = XILINX_AXI_DMA(obj);
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                              (Object **) &s->tx_dev, NULL);
@@ -506,7 +511,7 @@ static void axidma_class_init(ObjectClass *klass, void *data)
 }
 
 static const TypeInfo axidma_info = {
-    .name          = "xlnx.axi-dma",
+    .name          = TYPE_XILINX_AXI_DMA,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(XilinxAXIDMA),
     .class_init    = axidma_class_init,
commit 42e8a283f50da698e8bff7ed36b8b39e21d70eaa
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:21:23 2013 +1000

    xilinx_axidma: typedef XilinxAXIDMA struct
    
    Typedef xilinx_axidma's object state struct to shorten the repeated usages of
    struct XilinxAXIDMA.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c
index 8db1a74..4dd8a6b 100644
--- a/hw/dma/xilinx_axidma.c
+++ b/hw/dma/xilinx_axidma.c
@@ -38,6 +38,8 @@
 #define R_TAILDESC          (0x10 / 4)
 #define R_MAX               (0x30 / 4)
 
+typedef struct XilinxAXIDMA XilinxAXIDMA;
+
 enum {
     DMACR_RUNSTOP = 1,
     DMACR_TAILPTR_MODE = 2,
@@ -353,7 +355,7 @@ static void stream_process_s2mem(struct Stream *s,
 static void
 axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app)
 {
-    struct XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj));
+    XilinxAXIDMA *d = FROM_SYSBUS(typeof(*d), SYS_BUS_DEVICE(obj));
     struct Stream *s = &d->streams[1];
 
     if (!app) {
@@ -366,7 +368,7 @@ axidma_push(StreamSlave *obj, unsigned char *buf, size_t len, uint32_t *app)
 static uint64_t axidma_read(void *opaque, hwaddr addr,
                             unsigned size)
 {
-    struct XilinxAXIDMA *d = opaque;
+    XilinxAXIDMA *d = opaque;
     struct Stream *s;
     uint32_t r = 0;
     int sid;
@@ -401,7 +403,7 @@ static uint64_t axidma_read(void *opaque, hwaddr addr,
 static void axidma_write(void *opaque, hwaddr addr,
                          uint64_t value, unsigned size)
 {
-    struct XilinxAXIDMA *d = opaque;
+    XilinxAXIDMA *d = opaque;
     struct Stream *s;
     int sid;
 
@@ -459,7 +461,7 @@ static const MemoryRegionOps axidma_ops = {
 
 static int xilinx_axidma_init(SysBusDevice *dev)
 {
-    struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev);
+    XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev);
     int i;
 
     sysbus_init_irq(dev, &s->streams[0].irq);
@@ -481,14 +483,14 @@ static int xilinx_axidma_init(SysBusDevice *dev)
 
 static void xilinx_axidma_initfn(Object *obj)
 {
-    struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+    XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                              (Object **) &s->tx_dev, NULL);
 }
 
 static Property axidma_properties[] = {
-    DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000),
+    DEFINE_PROP_UINT32("freqhz", XilinxAXIDMA, freqhz, 50000000),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -506,7 +508,7 @@ static void axidma_class_init(ObjectClass *klass, void *data)
 static const TypeInfo axidma_info = {
     .name          = "xlnx.axi-dma",
     .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(struct XilinxAXIDMA),
+    .instance_size = sizeof(XilinxAXIDMA),
     .class_init    = axidma_class_init,
     .instance_init = xilinx_axidma_initfn,
     .interfaces = (InterfaceInfo[]) {
commit b2d9dfe94cc9a27e0b59d892ae8e3ec223ff9514
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:20:44 2013 +1000

    xilinx_axienet: converted init->realize
    
    The prescribed transition from SysBusDevice::init to Device::realize. Im going
    with Andreas suggestion to move the sysbus foo to Object::init for early IRQ
    visibility.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Reviewed-by: Andreas Färber <afaerber at suse.de>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index afc3d82..aa358cc 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -853,18 +853,13 @@ static NetClientInfo net_xilinx_enet_info = {
     .cleanup = eth_cleanup,
 };
 
-static int xilinx_enet_init(SysBusDevice *dev)
+static void xilinx_enet_realize(DeviceState *dev, Error **errp)
 {
     XilinxAXIEnet *s = XILINX_AXI_ENET(dev);
 
-    sysbus_init_irq(dev, &s->irq);
-
-    memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000);
-    sysbus_init_mmio(dev, &s->iomem);
-
     qemu_macaddr_default_if_unset(&s->conf.macaddr);
     s->nic = qemu_new_nic(&net_xilinx_enet_info, &s->conf,
-                          object_get_typename(OBJECT(dev)), dev->qdev.id, s);
+                          object_get_typename(OBJECT(dev)), dev->id, s);
     qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
 
     tdk_init(&s->TEMAC.phy);
@@ -873,18 +868,22 @@ static int xilinx_enet_init(SysBusDevice *dev)
     s->TEMAC.parent = s;
 
     s->rxmem = g_malloc(s->c_rxmem);
-
-    return 0;
 }
 
-static void xilinx_enet_initfn(Object *obj)
+static void xilinx_enet_init(Object *obj)
 {
     XilinxAXIEnet *s = XILINX_AXI_ENET(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
     Error *errp = NULL;
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
                              (Object **) &s->tx_dev, &errp);
     assert_no_error(errp);
+
+    sysbus_init_irq(sbd, &s->irq);
+
+    memory_region_init_io(&s->iomem, &enet_ops, s, "enet", 0x40000);
+    sysbus_init_mmio(sbd, &s->iomem);
 }
 
 static Property xilinx_enet_properties[] = {
@@ -898,10 +897,9 @@ static Property xilinx_enet_properties[] = {
 static void xilinx_enet_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
-    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
     StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 
-    k->init = xilinx_enet_init;
+    dc->realize = xilinx_enet_realize;
     dc->props = xilinx_enet_properties;
     dc->reset = xilinx_axienet_reset;
     ssc->push = axienet_stream_push;
@@ -912,7 +910,7 @@ static const TypeInfo xilinx_enet_info = {
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(XilinxAXIEnet),
     .class_init    = xilinx_enet_class_init,
-    .instance_init = xilinx_enet_initfn,
+    .instance_init = xilinx_enet_init,
     .interfaces = (InterfaceInfo[]) {
             { TYPE_STREAM_SLAVE },
             { }
commit 9ee0ceb7a8e643a0db232f42ddee2ed325dd4d86
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:20:06 2013 +1000

    xilinx_axienet: Register reset properly
    
    Register the reset function and the Device::reset function rather than
    explicitly call it from the sysbus::init.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Reviewed-by: Andreas Färber <afaerber at suse.de>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 6cb9a9b..afc3d82 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -402,8 +402,10 @@ static inline int axienet_newfunc_enabled(XilinxAXIEnet *s)
     return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN);
 }
 
-static void axienet_reset(XilinxAXIEnet *s)
+static void xilinx_axienet_reset(DeviceState *d)
 {
+    XilinxAXIEnet *s = XILINX_AXI_ENET(d);
+
     axienet_rx_reset(s);
     axienet_tx_reset(s);
 
@@ -871,7 +873,6 @@ static int xilinx_enet_init(SysBusDevice *dev)
     s->TEMAC.parent = s;
 
     s->rxmem = g_malloc(s->c_rxmem);
-    axienet_reset(s);
 
     return 0;
 }
@@ -902,6 +903,7 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data)
 
     k->init = xilinx_enet_init;
     dc->props = xilinx_enet_properties;
+    dc->reset = xilinx_axienet_reset;
     ssc->push = axienet_stream_push;
 }
 
commit f0e7a81c0ca122126e92a9f06b9599f04a0eaebb
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:19:27 2013 +1000

    xilinx_axienet: Defined and use type cast macro
    
    Standard QOM cast macro. Replaces usages of FROM_SYSBUS
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Reviewed-by: Andreas Färber <afaerber at suse.de>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 4cd048f..6cb9a9b 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -32,6 +32,11 @@
 
 #define DPHY(x)
 
+#define TYPE_XILINX_AXI_ENET "xlnx.axi-ethernet"
+
+#define XILINX_AXI_ENET(obj) \
+     OBJECT_CHECK(XilinxAXIEnet, (obj), TYPE_XILINX_AXI_ENET)
+
 /* Advertisement control register. */
 #define ADVERTISE_10HALF        0x0020  /* Try for 10mbps half-duplex  */
 #define ADVERTISE_10FULL        0x0040  /* Try for 10mbps full-duplex  */
@@ -848,7 +853,7 @@ static NetClientInfo net_xilinx_enet_info = {
 
 static int xilinx_enet_init(SysBusDevice *dev)
 {
-    XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), dev);
+    XilinxAXIEnet *s = XILINX_AXI_ENET(dev);
 
     sysbus_init_irq(dev, &s->irq);
 
@@ -873,7 +878,7 @@ static int xilinx_enet_init(SysBusDevice *dev)
 
 static void xilinx_enet_initfn(Object *obj)
 {
-    XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+    XilinxAXIEnet *s = XILINX_AXI_ENET(obj);
     Error *errp = NULL;
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
@@ -901,7 +906,7 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data)
 }
 
 static const TypeInfo xilinx_enet_info = {
-    .name          = "xlnx.axi-ethernet",
+    .name          = TYPE_XILINX_AXI_ENET,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(XilinxAXIEnet),
     .class_init    = xilinx_enet_class_init,
commit 545129e58965dcedf229dac3ed7ea1235a9838f2
Author: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
Date:   Tue Apr 16 10:18:47 2013 +1000

    xilinx_axienet: typedef XilinxAXIEnet struct
    
    Typedef xilinx_axienets object state struct to shorten the repeated usages of
    struct XilinxAXIEnet.
    
    Signed-off-by: Peter Crosthwaite <peter.crosthwaite at xilinx.com>
    Reviewed-by: Andreas Färber <afaerber at suse.de>
    Acked-by: Edgar E. Iglesias <edgar.iglesias at xilinx.com>
    Signed-off-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>

diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c
index 07c4bad..4cd048f 100644
--- a/hw/net/xilinx_axienet.c
+++ b/hw/net/xilinx_axienet.c
@@ -306,6 +306,8 @@ struct TEMAC  {
     void *parent;
 };
 
+typedef struct XilinxAXIEnet XilinxAXIEnet;
+
 struct XilinxAXIEnet {
     SysBusDevice busdev;
     MemoryRegion iomem;
@@ -365,37 +367,37 @@ struct XilinxAXIEnet {
     uint8_t *rxmem;
 };
 
-static void axienet_rx_reset(struct XilinxAXIEnet *s)
+static void axienet_rx_reset(XilinxAXIEnet *s)
 {
     s->rcw[1] = RCW1_JUM | RCW1_FCS | RCW1_RX | RCW1_VLAN;
 }
 
-static void axienet_tx_reset(struct XilinxAXIEnet *s)
+static void axienet_tx_reset(XilinxAXIEnet *s)
 {
     s->tc = TC_JUM | TC_TX | TC_VLAN;
 }
 
-static inline int axienet_rx_resetting(struct XilinxAXIEnet *s)
+static inline int axienet_rx_resetting(XilinxAXIEnet *s)
 {
     return s->rcw[1] & RCW1_RST;
 }
 
-static inline int axienet_rx_enabled(struct XilinxAXIEnet *s)
+static inline int axienet_rx_enabled(XilinxAXIEnet *s)
 {
     return s->rcw[1] & RCW1_RX;
 }
 
-static inline int axienet_extmcf_enabled(struct XilinxAXIEnet *s)
+static inline int axienet_extmcf_enabled(XilinxAXIEnet *s)
 {
     return !!(s->regs[R_RAF] & RAF_EMCF_EN);
 }
 
-static inline int axienet_newfunc_enabled(struct XilinxAXIEnet *s)
+static inline int axienet_newfunc_enabled(XilinxAXIEnet *s)
 {
     return !!(s->regs[R_RAF] & RAF_NEWFUNC_EN);
 }
 
-static void axienet_reset(struct XilinxAXIEnet *s)
+static void axienet_reset(XilinxAXIEnet *s)
 {
     axienet_rx_reset(s);
     axienet_tx_reset(s);
@@ -406,7 +408,7 @@ static void axienet_reset(struct XilinxAXIEnet *s)
     s->emmc = EMMC_LINKSPEED_100MB;
 }
 
-static void enet_update_irq(struct XilinxAXIEnet *s)
+static void enet_update_irq(XilinxAXIEnet *s)
 {
     s->regs[R_IP] = s->regs[R_IS] & s->regs[R_IE];
     qemu_set_irq(s->irq, !!s->regs[R_IP]);
@@ -414,7 +416,7 @@ static void enet_update_irq(struct XilinxAXIEnet *s)
 
 static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
 {
-    struct XilinxAXIEnet *s = opaque;
+    XilinxAXIEnet *s = opaque;
     uint32_t r = 0;
     addr >>= 2;
 
@@ -506,7 +508,7 @@ static uint64_t enet_read(void *opaque, hwaddr addr, unsigned size)
 static void enet_write(void *opaque, hwaddr addr,
                        uint64_t value, unsigned size)
 {
-    struct XilinxAXIEnet *s = opaque;
+    XilinxAXIEnet *s = opaque;
     struct TEMAC *t = &s->TEMAC;
 
     addr >>= 2;
@@ -620,7 +622,7 @@ static const MemoryRegionOps enet_ops = {
 
 static int eth_can_rx(NetClientState *nc)
 {
-    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
+    XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
 
     /* RX enabled?  */
     return !axienet_rx_resetting(s) && axienet_rx_enabled(s);
@@ -643,7 +645,7 @@ static int enet_match_addr(const uint8_t *buf, uint32_t f0, uint32_t f1)
 
 static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
 {
-    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
+    XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
     static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
                                               0xff, 0xff, 0xff};
     static const unsigned char sa_ipmcast[3] = {0x01, 0x00, 0x52};
@@ -788,7 +790,7 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size)
 static void eth_cleanup(NetClientState *nc)
 {
     /* FIXME.  */
-    struct XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
+    XilinxAXIEnet *s = qemu_get_nic_opaque(nc);
     g_free(s->rxmem);
     g_free(s);
 }
@@ -796,7 +798,7 @@ static void eth_cleanup(NetClientState *nc)
 static void
 axienet_stream_push(StreamSlave *obj, uint8_t *buf, size_t size, uint32_t *hdr)
 {
-    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+    XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
 
     /* TX enable ?  */
     if (!(s->tc & TC_TX)) {
@@ -846,7 +848,7 @@ static NetClientInfo net_xilinx_enet_info = {
 
 static int xilinx_enet_init(SysBusDevice *dev)
 {
-    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), dev);
+    XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), dev);
 
     sysbus_init_irq(dev, &s->irq);
 
@@ -871,7 +873,7 @@ static int xilinx_enet_init(SysBusDevice *dev)
 
 static void xilinx_enet_initfn(Object *obj)
 {
-    struct XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
+    XilinxAXIEnet *s = FROM_SYSBUS(typeof(*s), SYS_BUS_DEVICE(obj));
     Error *errp = NULL;
 
     object_property_add_link(obj, "axistream-connected", TYPE_STREAM_SLAVE,
@@ -880,10 +882,10 @@ static void xilinx_enet_initfn(Object *obj)
 }
 
 static Property xilinx_enet_properties[] = {
-    DEFINE_PROP_UINT32("phyaddr", struct XilinxAXIEnet, c_phyaddr, 7),
-    DEFINE_PROP_UINT32("rxmem", struct XilinxAXIEnet, c_rxmem, 0x1000),
-    DEFINE_PROP_UINT32("txmem", struct XilinxAXIEnet, c_txmem, 0x1000),
-    DEFINE_NIC_PROPERTIES(struct XilinxAXIEnet, conf),
+    DEFINE_PROP_UINT32("phyaddr", XilinxAXIEnet, c_phyaddr, 7),
+    DEFINE_PROP_UINT32("rxmem", XilinxAXIEnet, c_rxmem, 0x1000),
+    DEFINE_PROP_UINT32("txmem", XilinxAXIEnet, c_txmem, 0x1000),
+    DEFINE_NIC_PROPERTIES(XilinxAXIEnet, conf),
     DEFINE_PROP_END_OF_LIST(),
 };
 
@@ -901,7 +903,7 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data)
 static const TypeInfo xilinx_enet_info = {
     .name          = "xlnx.axi-ethernet",
     .parent        = TYPE_SYS_BUS_DEVICE,
-    .instance_size = sizeof(struct XilinxAXIEnet),
+    .instance_size = sizeof(XilinxAXIEnet),
     .class_init    = xilinx_enet_class_init,
     .instance_init = xilinx_enet_initfn,
     .interfaces = (InterfaceInfo[]) {
commit bfe528b9b99d52693a55f2b803039d68a97bcfb2
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Mar 19 16:59:58 2013 +0100

    qxl: register QemuConsole for secondary cards
    
    Hook secondary qxl cards properly into the qemu console subsystem.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index bbc6f56..437f8d0 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1769,7 +1769,10 @@ static void qxl_hw_invalidate(void *opaque)
     PCIQXLDevice *qxl = opaque;
     VGACommonState *vga = &qxl->vga;
 
-    vga->hw_ops->invalidate(vga);
+    if (qxl->mode == QXL_MODE_VGA) {
+        vga->hw_ops->invalidate(vga);
+        return;
+    }
 }
 
 static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
@@ -2085,6 +2088,7 @@ static int qxl_init_secondary(PCIDevice *dev)
     memory_region_init_ram(&qxl->vga.vram, "qxl.vgavram", qxl->vga.vram_size);
     vmstate_register_ram(&qxl->vga.vram, &qxl->pci.qdev);
     qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram);
+    qxl->vga.con = graphic_console_init(&qxl_ops, qxl);
 
     return qxl_init_common(qxl);
 }
commit 9697f5d2d38e5dd1e64e8e0d64436e6d44e7b1fe
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 20 09:11:41 2013 +0100

    gtk: custom cursor support
    
    Makes gtk ui play nicely with qxl (and vmware_svga)
    as you can actually see your pointer now ;)
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/ui/gtk.c b/ui/gtk.c
index e9ebbd3..d48529a 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -330,6 +330,37 @@ static void gd_refresh(DisplayChangeListener *dcl)
     graphic_hw_update(dcl->con);
 }
 
+static void gd_mouse_set(DisplayChangeListener *dcl,
+                         int x, int y, int visible)
+{
+    GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
+    gint x_root, y_root;
+
+    gdk_window_get_root_coords(s->drawing_area->window,
+                               x, y, &x_root, &y_root);
+    gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area),
+                             gtk_widget_get_screen(s->drawing_area),
+                             x_root, y_root);
+}
+
+static void gd_cursor_define(DisplayChangeListener *dcl,
+                             QEMUCursor *c)
+{
+    GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
+    GdkPixbuf *pixbuf;
+    GdkCursor *cursor;
+
+    pixbuf = gdk_pixbuf_new_from_data((guchar *)(c->data),
+                                      GDK_COLORSPACE_RGB, true, 8,
+                                      c->width, c->height, c->width * 4,
+                                      NULL, NULL);
+    cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(s->drawing_area),
+                                        pixbuf, c->hot_x, c->hot_y);
+    gdk_window_set_cursor(s->drawing_area->window, cursor);
+    g_object_unref(pixbuf);
+    g_object_unref(cursor);
+}
+
 static void gd_switch(DisplayChangeListener *dcl,
                       DisplaySurface *surface)
 {
@@ -1358,6 +1389,8 @@ static const DisplayChangeListenerOps dcl_ops = {
     .dpy_gfx_update    = gd_update,
     .dpy_gfx_switch    = gd_switch,
     .dpy_refresh       = gd_refresh,
+    .dpy_mouse_set     = gd_mouse_set,
+    .dpy_cursor_define = gd_cursor_define,
 };
 
 void gtk_display_init(DisplayState *ds)
commit 284d1c6b3bf4ece6278f4b9831c7192e3777290c
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Mar 15 15:45:54 2013 +0100

    console: allow pinning displaychangelisteners to consoles
    
    DisplayChangeListener gets a new QemuConsole field, which can be set to
    non-NULL before registering.  This will pin the QemuConsole, so that
    particular DisplayChangeListener will not follow console switches.
    
    spice+gtk (which don't support text console input anyway) are switched
    over to be pinned to console 0, which usually is the graphical display.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 8721d44..bbc6f56 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -2061,7 +2061,6 @@ static int qxl_init_primary(PCIDevice *dev)
     portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
 
     vga->con = graphic_console_init(&qxl_ops, qxl);
-    qxl->ssd.con = vga->con,
     qemu_spice_display_init_common(&qxl->ssd);
 
     rc = qxl_init_common(qxl);
@@ -2070,6 +2069,7 @@ static int qxl_init_primary(PCIDevice *dev)
     }
 
     qxl->ssd.dcl.ops = &display_listener_ops;
+    qxl->ssd.dcl.con = vga->con;
     ds = qemu_console_displaystate(vga->con);
     register_displaychangelistener(ds, &qxl->ssd.dcl);
     return rc;
diff --git a/include/ui/console.h b/include/ui/console.h
index bcd0139..e591d74 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -178,6 +178,7 @@ struct DisplayChangeListener {
     uint64_t update_interval;
     const DisplayChangeListenerOps *ops;
     DisplayState *ds;
+    QemuConsole *con;
 
     QLIST_ENTRY(DisplayChangeListener) next;
 };
@@ -282,6 +283,7 @@ void graphic_hw_update(QemuConsole *con);
 void graphic_hw_invalidate(QemuConsole *con);
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 
+QemuConsole *qemu_console_lookup_by_index(unsigned int index);
 bool qemu_console_is_visible(QemuConsole *con);
 bool qemu_console_is_graphic(QemuConsole *con);
 bool qemu_console_is_fixedsize(QemuConsole *con);
diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h
index 7a20fc4..a46bc80 100644
--- a/include/ui/spice-display.h
+++ b/include/ui/spice-display.h
@@ -71,7 +71,6 @@ typedef struct SimpleSpiceDisplay SimpleSpiceDisplay;
 typedef struct SimpleSpiceUpdate SimpleSpiceUpdate;
 
 struct SimpleSpiceDisplay {
-    QemuConsole *con;
     DisplaySurface *ds;
     DisplayChangeListener dcl;
     void *buf;
diff --git a/ui/console.c b/ui/console.c
index 214cdba..4f9219e 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -117,6 +117,7 @@ struct QemuConsole {
     console_type_t console_type;
     DisplayState *ds;
     DisplaySurface *surface;
+    int dcls;
 
     /* Graphic console state.  */
     const GraphicHwOps *hw_ops;
@@ -172,8 +173,6 @@ static QemuConsole *consoles[MAX_CONSOLES];
 static int nb_consoles = 0;
 
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
-static void dpy_gfx_switch_surface(DisplayState *ds,
-                                   DisplaySurface *surface);
 static void dpy_refresh(DisplayState *s);
 
 static void gui_update(void *opaque)
@@ -309,7 +308,7 @@ write_err:
 
 void qmp_screendump(const char *filename, Error **errp)
 {
-    QemuConsole *con = consoles[0];
+    QemuConsole *con = qemu_console_lookup_by_index(0);
     DisplaySurface *surface;
 
     if (con == NULL) {
@@ -1022,13 +1021,14 @@ static void console_putchar(QemuConsole *s, int ch)
 
 void console_select(unsigned int index)
 {
+    DisplayChangeListener *dcl;
     QemuConsole *s;
 
     if (index >= MAX_CONSOLES)
         return;
 
     trace_console_select(index);
-    s = consoles[index];
+    s = qemu_console_lookup_by_index(index);
     if (s) {
         DisplayState *ds = s->ds;
 
@@ -1037,7 +1037,14 @@ void console_select(unsigned int index)
         }
         active_console = s;
         if (ds->have_gfx) {
-            dpy_gfx_switch_surface(ds, s->surface);
+            QLIST_FOREACH(dcl, &ds->listeners, next) {
+                if (dcl->con != NULL) {
+                    continue;
+                }
+                if (dcl->ops->dpy_gfx_switch) {
+                    dcl->ops->dpy_gfx_switch(dcl, s->surface);
+                }
+            }
             dpy_gfx_update(s, 0, 0, surface_width(s->surface),
                            surface_height(s->surface));
         }
@@ -1292,12 +1299,20 @@ void qemu_free_displaysurface(DisplaySurface *surface)
 void register_displaychangelistener(DisplayState *ds,
                                     DisplayChangeListener *dcl)
 {
+    QemuConsole *con;
+
     trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
     dcl->ds = ds;
     QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
     gui_setup_refresh(ds);
-    if (dcl->ops->dpy_gfx_switch && active_console) {
-        dcl->ops->dpy_gfx_switch(dcl, active_console->surface);
+    if (dcl->con) {
+        dcl->con->dcls++;
+        con = dcl->con;
+    } else {
+        con = active_console;
+    }
+    if (dcl->ops->dpy_gfx_switch && con) {
+        dcl->ops->dpy_gfx_switch(dcl, con->surface);
     }
 }
 
@@ -1316,6 +1331,9 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl)
 {
     DisplayState *ds = dcl->ds;
     trace_displaychangelistener_unregister(dcl, dcl->ops->dpy_name);
+    if (dcl->con) {
+        dcl->con->dcls--;
+    }
     QLIST_REMOVE(dcl, next);
     gui_setup_refresh(ds);
 }
@@ -1323,7 +1341,7 @@ void unregister_displaychangelistener(DisplayChangeListener *dcl)
 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
     int width = surface_width(con->surface);
     int height = surface_height(con->surface);
 
@@ -1338,40 +1356,38 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
         if (dcl->ops->dpy_gfx_update) {
             dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
         }
     }
 }
 
-static void dpy_gfx_switch_surface(DisplayState *ds,
-                                   DisplaySurface *surface)
-{
-    struct DisplayChangeListener *dcl;
-
-    QLIST_FOREACH(dcl, &ds->listeners, next) {
-        if (dcl->ops->dpy_gfx_switch) {
-            dcl->ops->dpy_gfx_switch(dcl, surface);
-        }
-    }
-}
-
 void dpy_gfx_replace_surface(QemuConsole *con,
                              DisplaySurface *surface)
 {
     DisplayState *s = con->ds;
     DisplaySurface *old_surface = con->surface;
+    DisplayChangeListener *dcl;
 
     con->surface = surface;
-    if (qemu_console_is_visible(con)) {
-        dpy_gfx_switch_surface(s, surface);
+    QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
+        if (dcl->ops->dpy_gfx_switch) {
+            dcl->ops->dpy_gfx_switch(dcl, surface);
+        }
     }
     qemu_free_displaysurface(old_surface);
 }
 
 void dpy_refresh(DisplayState *s)
 {
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
+
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_refresh) {
             dcl->ops->dpy_refresh(dcl);
@@ -1383,12 +1399,15 @@ void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
                   int dst_x, int dst_y, int w, int h)
 {
     DisplayState *s = con->ds;
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
 
     if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
         if (dcl->ops->dpy_gfx_copy) {
             dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
         } else { /* TODO */
@@ -1400,12 +1419,15 @@ void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
 void dpy_text_cursor(QemuConsole *con, int x, int y)
 {
     DisplayState *s = con->ds;
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
 
     if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
         if (dcl->ops->dpy_text_cursor) {
             dcl->ops->dpy_text_cursor(dcl, x, y);
         }
@@ -1415,12 +1437,15 @@ void dpy_text_cursor(QemuConsole *con, int x, int y)
 void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
 
     if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
         if (dcl->ops->dpy_text_update) {
             dcl->ops->dpy_text_update(dcl, x, y, w, h);
         }
@@ -1436,6 +1461,9 @@ void dpy_text_resize(QemuConsole *con, int w, int h)
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
         if (dcl->ops->dpy_text_resize) {
             dcl->ops->dpy_text_resize(dcl, w, h);
         }
@@ -1445,12 +1473,15 @@ void dpy_text_resize(QemuConsole *con, int w, int h)
 void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
 {
     DisplayState *s = con->ds;
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
 
     if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
         if (dcl->ops->dpy_mouse_set) {
             dcl->ops->dpy_mouse_set(dcl, x, y, on);
         }
@@ -1460,12 +1491,15 @@ void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
 void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
 {
     DisplayState *s = con->ds;
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
 
     if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
+        if (con != (dcl->con ? dcl->con : active_console)) {
+            continue;
+        }
         if (dcl->ops->dpy_cursor_define) {
             dcl->ops->dpy_cursor_define(dcl, cursor);
         }
@@ -1475,7 +1509,8 @@ void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
 bool dpy_cursor_define_supported(QemuConsole *con)
 {
     DisplayState *s = con->ds;
-    struct DisplayChangeListener *dcl;
+    DisplayChangeListener *dcl;
+
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_cursor_define) {
             return true;
@@ -1536,9 +1571,17 @@ QemuConsole *graphic_console_init(const GraphicHwOps *hw_ops,
     return s;
 }
 
+QemuConsole *qemu_console_lookup_by_index(unsigned int index)
+{
+    if (index >= MAX_CONSOLES) {
+        return NULL;
+    }
+    return consoles[index];
+}
+
 bool qemu_console_is_visible(QemuConsole *con)
 {
-    return con == active_console;
+    return (con == active_console) || (con->dcls > 0);
 }
 
 bool qemu_console_is_graphic(QemuConsole *con)
diff --git a/ui/gtk.c b/ui/gtk.c
index d0e444c..e9ebbd3 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -327,7 +327,7 @@ static void gd_update(DisplayChangeListener *dcl,
 
 static void gd_refresh(DisplayChangeListener *dcl)
 {
-    graphic_hw_update(NULL);
+    graphic_hw_update(dcl->con);
 }
 
 static void gd_switch(DisplayChangeListener *dcl,
@@ -1368,6 +1368,7 @@ void gtk_display_init(DisplayState *ds)
     gtk_init(NULL, NULL);
 
     s->dcl.ops = &dcl_ops;
+    s->dcl.con = qemu_console_lookup_by_index(0);
 
     s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 #if GTK_CHECK_VERSION(3, 2, 0)
diff --git a/ui/spice-display.c b/ui/spice-display.c
index 2c01674..53c19be 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -398,14 +398,14 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd,
 void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
 {
     if (ssd->cursor) {
-        assert(ssd->con);
-        dpy_cursor_define(ssd->con, ssd->cursor);
+        assert(ssd->dcl.con);
+        dpy_cursor_define(ssd->dcl.con, ssd->cursor);
         cursor_put(ssd->cursor);
         ssd->cursor = NULL;
     }
     if (ssd->mouse_x != -1 && ssd->mouse_y != -1) {
-        assert(ssd->con);
-        dpy_mouse_set(ssd->con, ssd->mouse_x, ssd->mouse_y, 1);
+        assert(ssd->dcl.con);
+        dpy_mouse_set(ssd->dcl.con, ssd->mouse_x, ssd->mouse_y, 1);
         ssd->mouse_x = -1;
         ssd->mouse_y = -1;
     }
@@ -414,7 +414,7 @@ void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
 {
     dprint(3, "%s:\n", __func__);
-    graphic_hw_update(ssd->con);
+    graphic_hw_update(ssd->dcl.con);
 
     qemu_mutex_lock(&ssd->lock);
     if (QTAILQ_EMPTY(&ssd->updates) && ssd->ds) {
@@ -624,6 +624,7 @@ void qemu_spice_display_init(DisplayState *ds)
     qemu_spice_create_host_memslot(ssd);
 
     ssd->dcl.ops = &display_listener_ops;
+    ssd->dcl.con = qemu_console_lookup_by_index(0);
     register_displaychangelistener(ds, &ssd->dcl);
 
     qemu_spice_create_host_primary(ssd);
commit 81c0d5a66295024d0a42e3d28efcd102a32f93c3
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Mar 14 14:27:08 2013 +0100

    console: add qemu_console_is_*
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/console.h b/include/ui/console.h
index 800f458..bcd0139 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -282,8 +282,10 @@ void graphic_hw_update(QemuConsole *con);
 void graphic_hw_invalidate(QemuConsole *con);
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 
-int is_graphic_console(void);
-int is_fixedsize_console(void);
+bool qemu_console_is_visible(QemuConsole *con);
+bool qemu_console_is_graphic(QemuConsole *con);
+bool qemu_console_is_fixedsize(QemuConsole *con);
+
 void text_consoles_set_display(DisplayState *ds);
 void console_select(unsigned int index);
 void console_color_init(DisplayState *ds);
diff --git a/ui/console.c b/ui/console.c
index 43ff80b..214cdba 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -515,7 +515,7 @@ static void update_xy(QemuConsole *s, int x, int y)
     TextCell *c;
     int y1, y2;
 
-    if (s != active_console) {
+    if (!qemu_console_is_visible(s)) {
         return;
     }
 
@@ -543,7 +543,7 @@ static void console_show_cursor(QemuConsole *s, int show)
     int y, y1;
     int x = s->x;
 
-    if (s != active_console) {
+    if (!qemu_console_is_visible(s)) {
         return;
     }
 
@@ -579,8 +579,9 @@ static void console_refresh(QemuConsole *s)
     TextCell *c;
     int x, y, y1;
 
-    if (s != active_console)
+    if (!qemu_console_is_visible(s)) {
         return;
+    }
 
     if (s->ds->have_text) {
         s->text_x[0] = 0;
@@ -611,15 +612,10 @@ static void console_refresh(QemuConsole *s)
     }
 }
 
-static void console_scroll(int ydelta)
+static void console_scroll(QemuConsole *s, int ydelta)
 {
-    QemuConsole *s;
     int i, y1;
 
-    s = active_console;
-    if (!s || (s->console_type == GRAPHIC_CONSOLE))
-        return;
-
     if (ydelta > 0) {
         for(i = 0; i < ydelta; i++) {
             if (s->y_displayed == s->y_base)
@@ -669,7 +665,7 @@ static void console_put_lf(QemuConsole *s)
             c->t_attrib = s->t_attrib_default;
             c++;
         }
-        if (s == active_console && s->y_displayed == s->y_base) {
+        if (qemu_console_is_visible(s) && s->y_displayed == s->y_base) {
             if (s->ds->have_text) {
                 s->text_x[0] = 0;
                 s->text_y[0] = 0;
@@ -1112,16 +1108,16 @@ void kbd_put_keysym(int keysym)
 
     switch(keysym) {
     case QEMU_KEY_CTRL_UP:
-        console_scroll(-1);
+        console_scroll(s, -1);
         break;
     case QEMU_KEY_CTRL_DOWN:
-        console_scroll(1);
+        console_scroll(s, 1);
         break;
     case QEMU_KEY_CTRL_PAGEUP:
-        console_scroll(-10);
+        console_scroll(s, -10);
         break;
     case QEMU_KEY_CTRL_PAGEDOWN:
-        console_scroll(10);
+        console_scroll(s, 10);
         break;
     default:
         /* convert the QEMU keysym to VT100 key string */
@@ -1338,7 +1334,7 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
     w = MIN(w, width - x);
     h = MIN(h, height - y);
 
-    if (con != active_console) {
+    if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -1367,7 +1363,7 @@ void dpy_gfx_replace_surface(QemuConsole *con,
     DisplaySurface *old_surface = con->surface;
 
     con->surface = surface;
-    if (con == active_console) {
+    if (qemu_console_is_visible(con)) {
         dpy_gfx_switch_surface(s, surface);
     }
     qemu_free_displaysurface(old_surface);
@@ -1389,7 +1385,7 @@ void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
 
-    if (con != active_console) {
+    if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -1406,7 +1402,7 @@ void dpy_text_cursor(QemuConsole *con, int x, int y)
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
 
-    if (con != active_console) {
+    if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -1421,7 +1417,7 @@ void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
 
-    if (con != active_console) {
+    if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -1436,7 +1432,7 @@ void dpy_text_resize(QemuConsole *con, int w, int h)
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
 
-    if (con != active_console) {
+    if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -1451,7 +1447,7 @@ void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
 
-    if (con != active_console) {
+    if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -1466,7 +1462,7 @@ void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
 
-    if (con != active_console) {
+    if (!qemu_console_is_visible(con)) {
         return;
     }
     QLIST_FOREACH(dcl, &s->listeners, next) {
@@ -1540,14 +1536,25 @@ QemuConsole *graphic_console_init(const GraphicHwOps *hw_ops,
     return s;
 }
 
-int is_graphic_console(void)
+bool qemu_console_is_visible(QemuConsole *con)
 {
-    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
+    return con == active_console;
 }
 
-int is_fixedsize_console(void)
+bool qemu_console_is_graphic(QemuConsole *con)
 {
-    return active_console && active_console->console_type != TEXT_CONSOLE;
+    if (con == NULL) {
+        con = active_console;
+    }
+    return con && (con->console_type == GRAPHIC_CONSOLE);
+}
+
+bool qemu_console_is_fixedsize(QemuConsole *con)
+{
+    if (con == NULL) {
+        con = active_console;
+    }
+    return con && (con->console_type != TEXT_CONSOLE);
 }
 
 static void text_console_set_echo(CharDriverState *chr, bool echo)
diff --git a/ui/curses.c b/ui/curses.c
index ed9e65c..a85a7da 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -56,7 +56,7 @@ static void curses_update(DisplayChangeListener *dcl,
 
 static void curses_calc_pad(void)
 {
-    if (is_fixedsize_console()) {
+    if (qemu_console_is_fixedsize(NULL)) {
         width = gwidth;
         height = gheight;
     } else {
@@ -143,8 +143,9 @@ static void curses_cursor_position(DisplayChangeListener *dcl,
             curs_set(1);
             /* it seems that curs_set(1) must always be called before
              * curs_set(2) for the latter to have effect */
-            if (!is_graphic_console())
+            if (!qemu_console_is_graphic(NULL)) {
                 curs_set(2);
+            }
             return;
         }
     }
@@ -252,7 +253,7 @@ static void curses_refresh(DisplayChangeListener *dcl)
         if (keycode == -1)
             continue;
 
-        if (is_graphic_console()) {
+        if (qemu_console_is_graphic(NULL)) {
             /* since terminals don't know about key press and release
              * events, we need to emit both for each key received */
             if (keycode & SHIFT)
diff --git a/ui/sdl.c b/ui/sdl.c
index 97764a6..c9f2928 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -358,7 +358,7 @@ static void sdl_show_cursor(void)
     if (!cursor_hide)
         return;
 
-    if (!kbd_mouse_is_absolute() || !is_graphic_console()) {
+    if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) {
         SDL_ShowCursor(1);
         if (guest_cursor &&
                 (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
@@ -413,7 +413,7 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data)
     if (kbd_mouse_is_absolute()) {
         if (!absolute_enabled) {
             absolute_enabled = 1;
-            if (is_graphic_console()) {
+            if (qemu_console_is_graphic(NULL)) {
                 absolute_mouse_grab();
             }
         }
@@ -488,7 +488,7 @@ static void toggle_full_screen(void)
         } else {
             do_sdl_resize(width, height, 0);
         }
-        if (!gui_saved_grab || !is_graphic_console()) {
+        if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) {
             sdl_grab_end();
         }
     }
@@ -535,7 +535,7 @@ static void handle_keydown(SDL_Event *ev)
             if (gui_fullscreen) {
                 break;
             }
-            if (!is_graphic_console()) {
+            if (!qemu_console_is_graphic(NULL)) {
                 /* release grab if going to a text console */
                 if (gui_grab) {
                     sdl_grab_end();
@@ -563,7 +563,7 @@ static void handle_keydown(SDL_Event *ev)
         default:
             break;
         }
-    } else if (!is_graphic_console()) {
+    } else if (!qemu_console_is_graphic(NULL)) {
         int keysym = 0;
 
         if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
@@ -637,7 +637,7 @@ static void handle_keydown(SDL_Event *ev)
             kbd_put_keysym(ev->key.keysym.unicode);
         }
     }
-    if (is_graphic_console() && !gui_keysym) {
+    if (qemu_console_is_graphic(NULL) && !gui_keysym) {
         sdl_process_key(&ev->key);
     }
 }
@@ -656,7 +656,7 @@ static void handle_keyup(SDL_Event *ev)
         if (gui_keysym == 0) {
             /* exit/enter grab if pressing Ctrl-Alt */
             if (!gui_grab) {
-                if (is_graphic_console()) {
+                if (qemu_console_is_graphic(NULL)) {
                     sdl_grab_start();
                 }
             } else if (!gui_fullscreen) {
@@ -669,7 +669,7 @@ static void handle_keyup(SDL_Event *ev)
         }
         gui_keysym = 0;
     }
-    if (is_graphic_console() && !gui_keysym) {
+    if (qemu_console_is_graphic(NULL) && !gui_keysym) {
         sdl_process_key(&ev->key);
     }
 }
@@ -678,7 +678,7 @@ static void handle_mousemotion(SDL_Event *ev)
 {
     int max_x, max_y;
 
-    if (is_graphic_console() &&
+    if (qemu_console_is_graphic(NULL) &&
         (kbd_mouse_is_absolute() || absolute_enabled)) {
         max_x = real_screen->w - 1;
         max_y = real_screen->h - 1;
@@ -704,7 +704,7 @@ static void handle_mousebutton(SDL_Event *ev)
     SDL_MouseButtonEvent *bev;
     int dz;
 
-    if (!is_graphic_console()) {
+    if (!qemu_console_is_graphic(NULL)) {
         return;
     }
 
@@ -744,7 +744,7 @@ static void handle_activation(SDL_Event *ev)
         sdl_grab_end();
     }
 #endif
-    if (!gui_grab && ev->active.gain && is_graphic_console() &&
+    if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) &&
         (kbd_mouse_is_absolute() || absolute_enabled)) {
         absolute_mouse_grab();
     }
@@ -771,7 +771,7 @@ static void sdl_refresh(DisplayChangeListener *dcl)
     }
 
     graphic_hw_update(NULL);
-    SDL_EnableUNICODE(!is_graphic_console());
+    SDL_EnableUNICODE(!qemu_console_is_graphic(NULL));
 
     while (SDL_PollEvent(ev)) {
         switch (ev->type) {
diff --git a/ui/vnc.c b/ui/vnc.c
index 75ad67f..8ee66b7 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1609,7 +1609,7 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
         }
     }
 
-    if (is_graphic_console()) {
+    if (qemu_console_is_graphic(NULL)) {
         if (keycode & SCANCODE_GREY)
             kbd_put_keycode(SCANCODE_EMUL0);
         if (down)
@@ -1728,7 +1728,7 @@ static void vnc_release_modifiers(VncState *vs)
     };
     int i, keycode;
 
-    if (!is_graphic_console()) {
+    if (!qemu_console_is_graphic(NULL)) {
         return;
     }
     for (i = 0; i < ARRAY_SIZE(keycodes); i++) {
@@ -1748,7 +1748,7 @@ static void key_event(VncState *vs, int down, uint32_t sym)
     int keycode;
     int lsym = sym;
 
-    if (lsym >= 'A' && lsym <= 'Z' && is_graphic_console()) {
+    if (lsym >= 'A' && lsym <= 'Z' && qemu_console_is_graphic(NULL)) {
         lsym = lsym - 'A' + 'a';
     }
 
commit dea1b0bdd8b0bb9d76a012fd0f234ba1768a4a93
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Mar 19 15:01:02 2013 +0100

    xen: re-enable refresh interval reporting for xenfb
    
    xenfb informs the guest about the gui refresh interval so it can avoid
    pointless work.  That logic was temporarely disabled for the
    DisplayState reorganization.  Restore it now, with a proper interface
    for it.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index 8d327f1..f2eb89f 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -78,7 +78,6 @@ struct XenFB {
     void              *pixels;
     int               fbpages;
     int               feature_update;
-    int               refresh_period;
     int               bug_trigger;
     int               have_console;
     int               do_resize;
@@ -646,7 +645,7 @@ static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
     dpy_gfx_update(xenfb->c.con, x, y, w, h);
 }
 
-#if 0 /* def XENFB_TYPE_REFRESH_PERIOD */
+#ifdef XENFB_TYPE_REFRESH_PERIOD
 static int xenfb_queue_full(struct XenFB *xenfb)
 {
     struct xenfb_page *page = xenfb->c.page;
@@ -704,39 +703,7 @@ static void xenfb_update(void *opaque)
     if (xenfb->c.xendev.be_state != XenbusStateConnected)
         return;
 
-    if (xenfb->feature_update) {
-#if 0 /* XENFB_TYPE_REFRESH_PERIOD */
-        struct DisplayChangeListener *l;
-        int period = 99999999;
-        int idle = 1;
-
-	if (xenfb_queue_full(xenfb))
-	    return;
-
-        QLIST_FOREACH(l, &xenfb->c.ds->listeners, next) {
-            if (l->idle)
-                continue;
-            idle = 0;
-            if (!l->gui_timer_interval) {
-                if (period > GUI_REFRESH_INTERVAL)
-                    period = GUI_REFRESH_INTERVAL;
-            } else {
-                if (period > l->gui_timer_interval)
-                    period = l->gui_timer_interval;
-            }
-        }
-        if (idle)
-	    period = XENFB_NO_REFRESH;
-
-	if (xenfb->refresh_period != period) {
-	    xenfb_send_refresh_period(xenfb, period);
-	    xenfb->refresh_period = period;
-            xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period);
-	}
-#else
-	; /* nothing */
-#endif
-    } else {
+    if (!xenfb->feature_update) {
 	/* we don't get update notifications, thus use the
 	 * sledge hammer approach ... */
 	xenfb->up_fullscreen = 1;
@@ -785,6 +752,20 @@ static void xenfb_update(void *opaque)
     xenfb->up_fullscreen = 0;
 }
 
+static void xenfb_update_interval(void *opaque, uint64_t interval)
+{
+    struct XenFB *xenfb = opaque;
+
+    if (xenfb->feature_update) {
+#ifdef XENFB_TYPE_REFRESH_PERIOD
+        if (xenfb_queue_full(xenfb)) {
+            return;
+        }
+        xenfb_send_refresh_period(xenfb, interval);
+#endif
+    }
+}
+
 /* QEMU display state changed, so refresh the framebuffer copy */
 static void xenfb_invalidate(void *opaque)
 {
@@ -858,10 +839,6 @@ static void xenfb_handle_events(struct XenFB *xenfb)
 
 static int fb_init(struct XenDevice *xendev)
 {
-    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
-
-    fb->refresh_period = -1;
-
 #ifdef XENFB_TYPE_RESIZE
     xenstore_write_be_int(xendev, "feature-resize", 1);
 #endif
@@ -980,6 +957,7 @@ struct XenDevOps xen_framebuffer_ops = {
 static const GraphicHwOps xenfb_ops = {
     .invalidate  = xenfb_invalidate,
     .gfx_update  = xenfb_update,
+    .update_interval = xenfb_update_interval,
 };
 
 /*
diff --git a/include/ui/console.h b/include/ui/console.h
index 3cb0018..800f458 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -272,6 +272,7 @@ typedef struct GraphicHwOps {
     void (*invalidate)(void *opaque);
     void (*gfx_update)(void *opaque);
     void (*text_update)(void *opaque, console_ch_t *text);
+    void (*update_interval)(void *opaque, uint64_t interval);
 } GraphicHwOps;
 
 QemuConsole *graphic_console_init(const GraphicHwOps *ops,
diff --git a/ui/console.c b/ui/console.c
index 5bbc891..43ff80b 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -182,6 +182,7 @@ static void gui_update(void *opaque)
     uint64_t dcl_interval;
     DisplayState *ds = opaque;
     DisplayChangeListener *dcl;
+    int i;
 
     ds->refreshing = true;
     dpy_refresh(ds);
@@ -196,6 +197,11 @@ static void gui_update(void *opaque)
     }
     if (ds->update_interval != interval) {
         ds->update_interval = interval;
+        for (i = 0; i < nb_consoles; i++) {
+            if (consoles[i]->hw_ops->update_interval) {
+                consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval);
+            }
+        }
         trace_console_refresh(interval);
     }
     ds->last_update = qemu_get_clock_ms(rt_clock);
commit d4bcb199fb15f9f079ef280e7e6f9ccdfaa49ced
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Mar 15 11:53:47 2013 +0100

    qxl: add 4k + 8k resolutions
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 930b7cf..9d8ab58 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -116,6 +116,10 @@ static QXLMode qxl_modes[] = {
     QXL_MODE_EX(2560, 2048),
     QXL_MODE_EX(2800, 2100),
     QXL_MODE_EX(3200, 2400),
+    QXL_MODE_EX(3840, 2160), /* 4k mainstream */
+    QXL_MODE_EX(4096, 2160), /* 4k            */
+    QXL_MODE_EX(7680, 4320), /* 8k mainstream */
+    QXL_MODE_EX(8192, 4320), /* 8k            */
 };
 
 static void qxl_send_events(PCIQXLDevice *d, uint32_t events);
commit 0f7b2864d0d0c3ef2801f9214d8c510c80a220d1
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Mar 14 11:56:16 2013 +0100

    console: gui timer fixes
    
    Make gui update rate adaption code in gui_update() actually work.
    Sprinkle in a tracepoint so you can see the code at work.  Remove
    the update rate adaption code in vnc and make vnc simply use the
    generic bits instead.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/console.h b/include/ui/console.h
index f3e7791..3cb0018 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -21,7 +21,8 @@
 #define QEMU_CAPS_LOCK_LED   (1 << 2)
 
 /* in ms */
-#define GUI_REFRESH_INTERVAL 30
+#define GUI_REFRESH_INTERVAL_DEFAULT    30
+#define GUI_REFRESH_INTERVAL_IDLE     3000
 
 typedef void QEMUPutKBDEvent(void *opaque, int keycode);
 typedef void QEMUPutLEDEvent(void *opaque, int ledstate);
@@ -174,8 +175,7 @@ typedef struct DisplayChangeListenerOps {
 } DisplayChangeListenerOps;
 
 struct DisplayChangeListener {
-    int idle;
-    uint64_t gui_timer_interval;
+    uint64_t update_interval;
     const DisplayChangeListenerOps *ops;
     DisplayState *ds;
 
@@ -207,12 +207,13 @@ static inline int is_buffer_shared(DisplaySurface *surface)
 
 void register_displaychangelistener(DisplayState *ds,
                                     DisplayChangeListener *dcl);
+void update_displaychangelistener(DisplayChangeListener *dcl,
+                                  uint64_t interval);
 void unregister_displaychangelistener(DisplayChangeListener *dcl);
 
 void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h);
 void dpy_gfx_replace_surface(QemuConsole *con,
                              DisplaySurface *surface);
-void dpy_refresh(DisplayState *s);
 void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
                   int dst_x, int dst_y, int w, int h);
 void dpy_text_cursor(QemuConsole *con, int x, int y);
diff --git a/trace-events b/trace-events
index b08627b..968edb6 100644
--- a/trace-events
+++ b/trace-events
@@ -965,6 +965,7 @@ dma_map_wait(void *dbs) "dbs=%p"
 console_gfx_new(void) ""
 console_txt_new(int w, int h) "%dx%d"
 console_select(int nr) "%d"
+console_refresh(int interval) "interval %d ms"
 displaysurface_create(void *display_surface, int w, int h) "surface=%p, %dx%d"
 displaysurface_create_from(void *display_surface, int w, int h, int bpp, int swap) "surface=%p, %dx%d, bpp %d, bswap %d"
 displaysurface_free(void *display_surface) "surface=%p"
diff --git a/ui/console.c b/ui/console.c
index 79a306b..5bbc891 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -157,6 +157,9 @@ struct QemuConsole {
 
 struct DisplayState {
     struct QEMUTimer *gui_timer;
+    uint64_t last_update;
+    uint64_t update_interval;
+    bool refreshing;
     bool have_gfx;
     bool have_text;
 
@@ -171,22 +174,32 @@ static int nb_consoles = 0;
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
 static void dpy_gfx_switch_surface(DisplayState *ds,
                                    DisplaySurface *surface);
+static void dpy_refresh(DisplayState *s);
 
 static void gui_update(void *opaque)
 {
-    uint64_t interval = GUI_REFRESH_INTERVAL;
+    uint64_t interval = GUI_REFRESH_INTERVAL_IDLE;
+    uint64_t dcl_interval;
     DisplayState *ds = opaque;
     DisplayChangeListener *dcl;
 
+    ds->refreshing = true;
     dpy_refresh(ds);
+    ds->refreshing = false;
 
     QLIST_FOREACH(dcl, &ds->listeners, next) {
-        if (dcl->gui_timer_interval &&
-            dcl->gui_timer_interval < interval) {
-            interval = dcl->gui_timer_interval;
+        dcl_interval = dcl->update_interval ?
+            dcl->update_interval : GUI_REFRESH_INTERVAL_DEFAULT;
+        if (interval > dcl_interval) {
+            interval = dcl_interval;
         }
     }
-    qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
+    if (ds->update_interval != interval) {
+        ds->update_interval = interval;
+        trace_console_refresh(interval);
+    }
+    ds->last_update = qemu_get_clock_ms(rt_clock);
+    qemu_mod_timer(ds->gui_timer, ds->last_update + interval);
 }
 
 static void gui_setup_refresh(DisplayState *ds)
@@ -1286,6 +1299,17 @@ void register_displaychangelistener(DisplayState *ds,
     }
 }
 
+void update_displaychangelistener(DisplayChangeListener *dcl,
+                                  uint64_t interval)
+{
+    DisplayState *ds = dcl->ds;
+
+    dcl->update_interval = interval;
+    if (!ds->refreshing && ds->update_interval > interval) {
+        qemu_mod_timer(ds->gui_timer, ds->last_update + interval);
+    }
+}
+
 void unregister_displaychangelistener(DisplayChangeListener *dcl)
 {
     DisplayState *ds = dcl->ds;
diff --git a/ui/sdl.c b/ui/sdl.c
index ede31dc..97764a6 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -751,12 +751,12 @@ static void handle_activation(SDL_Event *ev)
     if (ev->active.state & SDL_APPACTIVE) {
         if (ev->active.gain) {
             /* Back to default interval */
-            dcl->gui_timer_interval = 0;
-            dcl->idle = 0;
+            update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
         } else {
-            /* Sleeping interval */
-            dcl->gui_timer_interval = 500;
-            dcl->idle = 1;
+            /* Sleeping interval.  Not using the long default here as
+             * sdl_refresh does not only update the guest screen, but
+             * also checks for gui events. */
+            update_displaychangelistener(dcl, 500);
         }
     }
 }
diff --git a/ui/vnc.c b/ui/vnc.c
index bc787cc..75ad67f 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -34,9 +34,9 @@
 #include "qmp-commands.h"
 #include "qemu/osdep.h"
 
-#define VNC_REFRESH_INTERVAL_BASE 30
+#define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT
 #define VNC_REFRESH_INTERVAL_INC  50
-#define VNC_REFRESH_INTERVAL_MAX  2000
+#define VNC_REFRESH_INTERVAL_MAX  GUI_REFRESH_INTERVAL_IDLE
 static const struct timeval VNC_REFRESH_STATS = { 0, 500000 };
 static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 };
 
@@ -419,14 +419,12 @@ out_error:
 static int vnc_update_client(VncState *vs, int has_dirty);
 static int vnc_update_client_sync(VncState *vs, int has_dirty);
 static void vnc_disconnect_start(VncState *vs);
-static void vnc_init_timer(VncDisplay *vd);
-static void vnc_remove_timer(VncDisplay *vd);
 
 static void vnc_colordepth(VncState *vs);
 static void framebuffer_update_request(VncState *vs, int incremental,
                                        int x_position, int y_position,
                                        int w, int h);
-static void vnc_refresh(void *opaque);
+static void vnc_refresh(DisplayChangeListener *dcl);
 static int vnc_refresh_server_surface(VncDisplay *vd);
 
 static void vnc_dpy_update(DisplayChangeListener *dcl,
@@ -1064,11 +1062,6 @@ void vnc_disconnect_finish(VncState *vs)
         qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
     }
 
-    if (QTAILQ_EMPTY(&vs->vd->clients)) {
-        vs->vd->dcl.idle = 1;
-    }
-
-    vnc_remove_timer(vs->vd);
     if (vs->vd->lock_key_sync)
         qemu_remove_led_event_handler(vs->led);
     vnc_unlock_output(vs);
@@ -2013,9 +2006,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
     VncDisplay *vd = vs->vd;
 
     if (data[0] > 3) {
-        vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
-        if (!qemu_timer_expired(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval))
-            qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval);
+        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
     }
 
     switch (data[0]) {
@@ -2647,18 +2638,16 @@ static int vnc_refresh_server_surface(VncDisplay *vd)
     return has_dirty;
 }
 
-static void vnc_refresh(void *opaque)
+static void vnc_refresh(DisplayChangeListener *dcl)
 {
-    VncDisplay *vd = opaque;
+    VncDisplay *vd = container_of(dcl, VncDisplay, dcl);
     VncState *vs, *vn;
     int has_dirty, rects = 0;
 
     graphic_hw_update(NULL);
 
     if (vnc_trylock_display(vd)) {
-        vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
-        qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) +
-                       vd->timer_interval);
+        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
         return;
     }
 
@@ -2670,39 +2659,21 @@ static void vnc_refresh(void *opaque)
         /* vs might be free()ed here */
     }
 
-    /* vd->timer could be NULL now if the last client disconnected,
-     * in this case don't update the timer */
-    if (vd->timer == NULL)
+    if (QTAILQ_EMPTY(&vd->clients)) {
+        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_MAX);
         return;
+    }
 
     if (has_dirty && rects) {
-        vd->timer_interval /= 2;
-        if (vd->timer_interval < VNC_REFRESH_INTERVAL_BASE)
-            vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
+        vd->dcl.update_interval /= 2;
+        if (vd->dcl.update_interval < VNC_REFRESH_INTERVAL_BASE) {
+            vd->dcl.update_interval = VNC_REFRESH_INTERVAL_BASE;
+        }
     } else {
-        vd->timer_interval += VNC_REFRESH_INTERVAL_INC;
-        if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX)
-            vd->timer_interval = VNC_REFRESH_INTERVAL_MAX;
-    }
-    qemu_mod_timer(vd->timer, qemu_get_clock_ms(rt_clock) + vd->timer_interval);
-}
-
-static void vnc_init_timer(VncDisplay *vd)
-{
-    vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
-    if (vd->timer == NULL && !QTAILQ_EMPTY(&vd->clients)) {
-        vd->timer = qemu_new_timer_ms(rt_clock, vnc_refresh, vd);
-        graphic_hw_update(NULL);
-        vnc_refresh(vd);
-    }
-}
-
-static void vnc_remove_timer(VncDisplay *vd)
-{
-    if (vd->timer != NULL && QTAILQ_EMPTY(&vd->clients)) {
-        qemu_del_timer(vd->timer);
-        qemu_free_timer(vd->timer);
-        vd->timer = NULL;
+        vd->dcl.update_interval += VNC_REFRESH_INTERVAL_INC;
+        if (vd->dcl.update_interval > VNC_REFRESH_INTERVAL_MAX) {
+            vd->dcl.update_interval = VNC_REFRESH_INTERVAL_MAX;
+        }
     }
 }
 
@@ -2731,7 +2702,7 @@ static void vnc_connect(VncDisplay *vd, int csock, int skipauth, bool websocket)
     }
 
     VNC_DEBUG("New client on socket %d\n", csock);
-    vd->dcl.idle = 0;
+    update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
     qemu_set_nonblock(vs->csock);
 #ifdef CONFIG_VNC_WS
     if (websocket) {
@@ -2787,8 +2758,6 @@ void vnc_init_state(VncState *vs)
     vs->mouse_mode_notifier.notify = check_pointer_type_change;
     qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier);
 
-    vnc_init_timer(vd);
-
     /* vs might be free()ed here */
 }
 
@@ -2829,6 +2798,7 @@ static void vnc_listen_websocket_read(void *opaque)
 
 static const DisplayChangeListenerOps dcl_ops = {
     .dpy_name          = "vnc",
+    .dpy_refresh       = vnc_refresh,
     .dpy_gfx_copy      = vnc_dpy_copy,
     .dpy_gfx_update    = vnc_dpy_update,
     .dpy_gfx_switch    = vnc_dpy_switch,
@@ -2840,7 +2810,6 @@ void vnc_display_init(DisplayState *ds)
 {
     VncDisplay *vs = g_malloc0(sizeof(*vs));
 
-    vs->dcl.idle = 1;
     vnc_display = vs;
 
     vs->lsock = -1;
diff --git a/ui/vnc.h b/ui/vnc.h
index 58e002e..ad1dec2 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -142,8 +142,6 @@ struct VncDisplay
     QTAILQ_HEAD(, VncState) clients;
     int num_exclusive;
     VncSharePolicy share_policy;
-    QEMUTimer *timer;
-    int timer_interval;
     int lsock;
 #ifdef CONFIG_VNC_WS
     int lwebsock;
commit 380cd056ec0e7fc8bbd553cdcb061d3ca612bb82
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 13 14:04:18 2013 +0100

    console: add GraphicHwOps
    
    Pass a single GraphicHwOps struct pointer to graphic_console_init,
    instead of a bunch of function pointers.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index 6e77447..31586c6 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -601,6 +601,11 @@ static const MemoryRegionOps musicpal_lcd_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
+static const GraphicHwOps musicpal_gfx_ops = {
+    .invalidate  = lcd_invalidate,
+    .gfx_update  = lcd_refresh,
+};
+
 static int musicpal_lcd_init(SysBusDevice *dev)
 {
     musicpal_lcd_state *s = FROM_SYSBUS(musicpal_lcd_state, dev);
@@ -611,8 +616,7 @@ static int musicpal_lcd_init(SysBusDevice *dev)
                           "musicpal-lcd", MP_LCD_SIZE);
     sysbus_init_mmio(dev, &s->iomem);
 
-    s->con = graphic_console_init(lcd_refresh, lcd_invalidate,
-                                  NULL, s);
+    s->con = graphic_console_init(&musicpal_gfx_ops, s);
     qemu_console_resize(s->con, 128*3, 64*3);
 
     qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3);
diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c
index 70b6822..1ca3355 100644
--- a/hw/display/blizzard.c
+++ b/hw/display/blizzard.c
@@ -944,6 +944,11 @@ static void blizzard_update_display(void *opaque)
 #define DEPTH 32
 #include "blizzard_template.h"
 
+static const GraphicHwOps blizzard_ops = {
+    .invalidate  = blizzard_invalidate_display,
+    .gfx_update  = blizzard_update_display,
+};
+
 void *s1d13745_init(qemu_irq gpio_int)
 {
     BlizzardState *s = (BlizzardState *) g_malloc0(sizeof(*s));
@@ -951,9 +956,7 @@ void *s1d13745_init(qemu_irq gpio_int)
 
     s->fb = g_malloc(0x180000);
 
-    s->con = graphic_console_init(blizzard_update_display,
-                                  blizzard_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&blizzard_ops, s);
     surface = qemu_console_surface(s->con);
 
     switch (surface_bits_per_pixel(surface)) {
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index c31b021..db232af 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -2910,9 +2910,7 @@ static int vga_initfn(ISADevice *dev)
     vga_common_init(s);
     cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0,
                        isa_address_space(dev), isa_address_space_io(dev));
-    s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->text_update,
-                                  s);
+    s->con = graphic_console_init(s->hw_ops, s);
     rom_add_vga(VGABIOS_CIRRUS_FILENAME);
     /* XXX ISA-LFB support */
     /* FIXME not qdev yet */
@@ -2959,9 +2957,7 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev)
      vga_common_init(&s->vga);
      cirrus_init_common(s, device_id, 1, pci_address_space(dev),
                         pci_address_space_io(dev));
-     s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
-                                       s->vga.text_update,
-                                       &s->vga);
+     s->vga.con = graphic_console_init(s->vga.hw_ops, &s->vga);
 
      /* setup PCI */
 
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index d651ddb..e6e7b27 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1887,6 +1887,11 @@ static const VMStateDescription exynos4210_fimd_vmstate = {
     }
 };
 
+static const GraphicHwOps exynos4210_fimd_ops = {
+    .invalidate  = exynos4210_fimd_invalidate,
+    .gfx_update  = exynos4210_fimd_update,
+};
+
 static int exynos4210_fimd_init(SysBusDevice *dev)
 {
     Exynos4210fimdState *s = FROM_SYSBUS(Exynos4210fimdState, dev);
@@ -1900,8 +1905,7 @@ static int exynos4210_fimd_init(SysBusDevice *dev)
     memory_region_init_io(&s->iomem, &exynos4210_fimd_mmio_ops, s,
             "exynos4210.fimd", FIMD_REGS_SIZE);
     sysbus_init_mmio(dev, &s->iomem);
-    s->console = graphic_console_init(exynos4210_fimd_update,
-                                      exynos4210_fimd_invalidate, NULL, s);
+    s->console = graphic_console_init(&exynos4210_fimd_ops, s);
 
     return 0;
 }
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index b70fe8a..03810e9 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -475,13 +475,16 @@ static const VMStateDescription vmstate_g364fb = {
     }
 };
 
+static const GraphicHwOps g364fb_ops = {
+    .invalidate  = g364fb_invalidate_display,
+    .gfx_update  = g364fb_update_display,
+};
+
 static void g364fb_init(DeviceState *dev, G364State *s)
 {
     s->vram = g_malloc0(s->vram_size);
 
-    s->con = graphic_console_init(g364fb_update_display,
-                                  g364fb_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&g364fb_ops, s);
 
     memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
     memory_region_init_ram_ptr(&s->mem_vram, "vram",
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
index c027f76..6306d8c 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -254,6 +254,12 @@ static const VMStateDescription vmstate_jazz_led = {
     }
 };
 
+static const GraphicHwOps jazz_led_ops = {
+    .invalidate  = jazz_led_invalidate_display,
+    .gfx_update  = jazz_led_update_display,
+    .text_update = jazz_led_text_update,
+};
+
 static int jazz_led_init(SysBusDevice *dev)
 {
     LedState *s = FROM_SYSBUS(LedState, dev);
@@ -261,9 +267,7 @@ static int jazz_led_init(SysBusDevice *dev)
     memory_region_init_io(&s->iomem, &led_ops, s, "led", 1);
     sysbus_init_mmio(dev, &s->iomem);
 
-    s->con = graphic_console_init(jazz_led_update_display,
-                                  jazz_led_invalidate_display,
-                                  jazz_led_text_update, s);
+    s->con = graphic_console_init(&jazz_led_ops, s);
 
     return 0;
 }
diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c
index 9bdb5c7..716997c 100644
--- a/hw/display/milkymist-vgafb.c
+++ b/hw/display/milkymist-vgafb.c
@@ -270,6 +270,11 @@ static void milkymist_vgafb_reset(DeviceState *d)
     s->regs[R_BASEADDRESS] = 0;
 }
 
+static const GraphicHwOps vgafb_ops = {
+    .invalidate  = vgafb_invalidate_display,
+    .gfx_update  = vgafb_update_display,
+};
+
 static int milkymist_vgafb_init(SysBusDevice *dev)
 {
     MilkymistVgafbState *s = FROM_SYSBUS(typeof(*s), dev);
@@ -278,9 +283,7 @@ static int milkymist_vgafb_init(SysBusDevice *dev)
             "milkymist-vgafb", R_MAX * 4);
     sysbus_init_mmio(dev, &s->regs_region);
 
-    s->con = graphic_console_init(vgafb_update_display,
-                                  vgafb_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&vgafb_ops, s);
 
     return 0;
 }
diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c
index f76f613..e4a5595 100644
--- a/hw/display/omap_lcdc.c
+++ b/hw/display/omap_lcdc.c
@@ -384,6 +384,11 @@ void omap_lcdc_reset(struct omap_lcd_panel_s *s)
     s->ctrl = 0;
 }
 
+static const GraphicHwOps omap_ops = {
+    .invalidate  = omap_invalidate_display,
+    .gfx_update  = omap_update_display,
+};
+
 struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
                                         hwaddr base,
                                         qemu_irq irq,
@@ -401,9 +406,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
     memory_region_init_io(&s->iomem, &omap_lcdc_ops, s, "omap.lcdc", 0x100);
     memory_region_add_subregion(sysmem, base, &s->iomem);
 
-    s->con = graphic_console_init(omap_update_display,
-                                  omap_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&omap_ops, s);
 
     return s;
 }
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index 5599755..d232431 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -444,6 +444,11 @@ static int vmstate_pl110_post_load(void *opaque, int version_id)
     return 0;
 }
 
+static const GraphicHwOps pl110_gfx_ops = {
+    .invalidate  = pl110_invalidate_display,
+    .gfx_update  = pl110_update_display,
+};
+
 static int pl110_init(SysBusDevice *dev)
 {
     pl110_state *s = FROM_SYSBUS(pl110_state, dev);
@@ -452,9 +457,7 @@ static int pl110_init(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->iomem);
     sysbus_init_irq(dev, &s->irq);
     qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
-    s->con = graphic_console_init(pl110_update_display,
-                                  pl110_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&pl110_gfx_ops, s);
     return 0;
 }
 
diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c
index d38479d..12d9cd2 100644
--- a/hw/display/pxa2xx_lcd.c
+++ b/hw/display/pxa2xx_lcd.c
@@ -991,6 +991,11 @@ static const VMStateDescription vmstate_pxa2xx_lcdc = {
 #define BITS 32
 #include "pxa2xx_template.h"
 
+static const GraphicHwOps pxa2xx_ops = {
+    .invalidate  = pxa2xx_invalidate_display,
+    .gfx_update  = pxa2xx_update_display,
+};
+
 PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
                                  hwaddr base, qemu_irq irq)
 {
@@ -1008,9 +1013,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
                           "pxa2xx-lcd-controller", 0x00100000);
     memory_region_add_subregion(sysmem, base, &s->iomem);
 
-    s->con = graphic_console_init(pxa2xx_update_display,
-                                  pxa2xx_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&pxa2xx_ops, s);
     surface = qemu_console_surface(s->con);
 
     switch (surface_bits_per_pixel(surface)) {
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 3b09d20..8721d44 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1753,7 +1753,7 @@ static void qxl_hw_update(void *opaque)
 
     switch (qxl->mode) {
     case QXL_MODE_VGA:
-        vga->update(vga);
+        vga->hw_ops->gfx_update(vga);
         break;
     case QXL_MODE_COMPAT:
     case QXL_MODE_NATIVE:
@@ -1769,7 +1769,7 @@ static void qxl_hw_invalidate(void *opaque)
     PCIQXLDevice *qxl = opaque;
     VGACommonState *vga = &qxl->vga;
 
-    vga->invalidate(vga);
+    vga->hw_ops->invalidate(vga);
 }
 
 static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
@@ -1778,7 +1778,7 @@ static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
     VGACommonState *vga = &qxl->vga;
 
     if (qxl->mode == QXL_MODE_VGA) {
-        vga->text_update(vga, chardata);
+        vga->hw_ops->text_update(vga, chardata);
         return;
     }
 }
@@ -2038,6 +2038,12 @@ static int qxl_init_common(PCIQXLDevice *qxl)
     return 0;
 }
 
+static const GraphicHwOps qxl_ops = {
+    .invalidate  = qxl_hw_invalidate,
+    .gfx_update  = qxl_hw_update,
+    .text_update = qxl_hw_text_update,
+};
+
 static int qxl_init_primary(PCIDevice *dev)
 {
     PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev);
@@ -2054,9 +2060,7 @@ static int qxl_init_primary(PCIDevice *dev)
     portio_list_init(qxl_vga_port_list, qxl_vga_portio_list, vga, "vga");
     portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
 
-    vga->con = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
-                                    qxl_hw_text_update,
-                                    qxl);
+    vga->con = graphic_console_init(&qxl_ops, qxl);
     qxl->ssd.con = vga->con,
     qemu_spice_display_init_common(&qxl->ssd);
 
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index e57ff22..f0e6d70 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1383,6 +1383,10 @@ static void sm501_update_display(void *opaque)
 	sm501_draw_crt(s);
 }
 
+static const GraphicHwOps sm501_ops = {
+    .gfx_update  = sm501_update_display,
+};
+
 void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
                 uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr)
 {
@@ -1445,6 +1449,5 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
     }
 
     /* create qemu graphic console */
-    s->con = graphic_console_init(sm501_update_display, NULL,
-                                  NULL, s);
+    s->con = graphic_console_init(&sm501_ops, s);
 }
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
index 9b6810f..3d7ebbe 100644
--- a/hw/display/ssd0303.c
+++ b/hw/display/ssd0303.c
@@ -284,13 +284,16 @@ static const VMStateDescription vmstate_ssd0303 = {
     }
 };
 
+static const GraphicHwOps ssd0303_ops = {
+    .invalidate  = ssd0303_invalidate_display,
+    .gfx_update  = ssd0303_update_display,
+};
+
 static int ssd0303_init(I2CSlave *i2c)
 {
     ssd0303_state *s = FROM_I2C_SLAVE(ssd0303_state, i2c);
 
-    s->con = graphic_console_init(ssd0303_update_display,
-                                  ssd0303_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&ssd0303_ops, s);
     qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
     return 0;
 }
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
index 301cb20..45e8dc1 100644
--- a/hw/display/ssd0323.c
+++ b/hw/display/ssd0323.c
@@ -331,15 +331,18 @@ static int ssd0323_load(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
+static const GraphicHwOps ssd0323_ops = {
+    .invalidate  = ssd0323_invalidate_display,
+    .gfx_update  = ssd0323_update_display,
+};
+
 static int ssd0323_init(SSISlave *dev)
 {
     ssd0323_state *s = FROM_SSI_SLAVE(ssd0323_state, dev);
 
     s->col_end = 63;
     s->row_end = 79;
-    s->con = graphic_console_init(ssd0323_update_display,
-                                  ssd0323_invalidate_display,
-                                  NULL, s);
+    s->con = graphic_console_init(&ssd0323_ops, s);
     qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
 
     qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
index 01beba4..b5b255c 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -548,6 +548,10 @@ static void tc6393xb_writeb(void *opaque, hwaddr addr,
                 (uint32_t) addr, (int)value & 0xff);
 }
 
+static const GraphicHwOps tc6393xb_gfx_ops = {
+    .gfx_update  = tc6393xb_update_display,
+};
+
 TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
 {
     TC6393xbState *s;
@@ -583,10 +587,7 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
     memory_region_add_subregion(sysmem, base + 0x100000, &s->vram);
     s->scr_width = 480;
     s->scr_height = 640;
-    s->con = graphic_console_init(tc6393xb_update_display,
-            NULL, /* invalidate */
-            NULL, /* text_update */
-            s);
+    s->con = graphic_console_init(&tc6393xb_gfx_ops, s);
 
     return s;
 }
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index ba3857a..77c7191 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -510,6 +510,16 @@ static const MemoryRegionOps dummy_ops = {
     },
 };
 
+static const GraphicHwOps tcx_ops = {
+    .invalidate = tcx_invalidate_display,
+    .gfx_update = tcx_update_display,
+};
+
+static const GraphicHwOps tcx24_ops = {
+    .invalidate = tcx24_invalidate_display,
+    .gfx_update = tcx24_update_display,
+};
+
 static int tcx_init1(SysBusDevice *dev)
 {
     TCXState *s = FROM_SYSBUS(TCXState, dev);
@@ -562,18 +572,14 @@ static int tcx_init1(SysBusDevice *dev)
                                  &s->vram_mem, vram_offset, size);
         sysbus_init_mmio(dev, &s->vram_cplane);
 
-        s->con = graphic_console_init(tcx24_update_display,
-                                      tcx24_invalidate_display,
-                                      NULL, s);
+        s->con = graphic_console_init(&tcx24_ops, s);
     } else {
         /* THC 8 bit (dummy) */
         memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8",
                               TCX_THC_NREGS_8);
         sysbus_init_mmio(dev, &s->thc8);
 
-        s->con = graphic_console_init(tcx_update_display,
-                                      tcx_invalidate_display,
-                                      NULL, s);
+        s->con = graphic_console_init(&tcx_ops, s);
     }
 
     qemu_console_resize(s->con, s->width, s->height);
diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c
index e177197..2da08a1 100644
--- a/hw/display/vga-isa-mm.c
+++ b/hw/display/vga-isa-mm.c
@@ -135,9 +135,7 @@ int isa_vga_mm_init(hwaddr vram_base,
     vga_common_init(&s->vga);
     vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
 
-    s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
-                                      s->vga.text_update,
-                                      s);
+    s->vga.con = graphic_console_init(s->vga.hw_ops, s);
 
     vga_init_vbe(&s->vga, address_space);
     return 0;
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
index 228657e..d2c548e 100644
--- a/hw/display/vga-isa.c
+++ b/hw/display/vga-isa.c
@@ -62,8 +62,7 @@ static int vga_initfn(ISADevice *dev)
                                         isa_mem_base + 0x000a0000,
                                         vga_io_memory, 1);
     memory_region_set_coalescing(vga_io_memory);
-    s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->text_update, s);
+    s->con = graphic_console_init(s->hw_ops, s);
 
     vga_init_vbe(s, isa_address_space(dev));
     /* ROM BIOS */
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index a6245b4..dc73f28 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -150,8 +150,7 @@ static int pci_std_vga_initfn(PCIDevice *dev)
     vga_common_init(s);
     vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true);
 
-    s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->text_update, s);
+    s->con = graphic_console_init(s->hw_ops, s);
 
     /* XXX: VGA_RAM_SIZE must be a power of two */
     pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 5d7684a..21a108d 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -2250,6 +2250,12 @@ const VMStateDescription vmstate_vga_common = {
     }
 };
 
+static const GraphicHwOps vga_ops = {
+    .invalidate  = vga_invalidate_display,
+    .gfx_update  = vga_update_display,
+    .text_update = vga_update_text,
+};
+
 void vga_common_init(VGACommonState *s)
 {
     int i, j, v, b;
@@ -2293,9 +2299,7 @@ void vga_common_init(VGACommonState *s)
     s->get_bpp = vga_get_bpp;
     s->get_offsets = vga_get_offsets;
     s->get_resolution = vga_get_resolution;
-    s->update = vga_update_display;
-    s->invalidate = vga_invalidate_display;
-    s->text_update = vga_update_text;
+    s->hw_ops = &vga_ops;
     switch (vga_retrace_method) {
     case VGA_RETRACE_DUMB:
         s->retrace = vga_dumb_retrace;
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index e4bb4a0..66f9f3c 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -152,9 +152,7 @@ typedef struct VGACommonState {
     uint32_t cursor_offset;
     unsigned int (*rgb_to_pixel)(unsigned int r,
                                  unsigned int g, unsigned b);
-    graphic_hw_update_ptr update;
-    graphic_hw_invalidate_ptr invalidate;
-    graphic_hw_text_update_ptr text_update;
+    const GraphicHwOps *hw_ops;
     bool full_update_text;
     bool full_update_gfx;
     /* hardware mouse cursor support */
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 61d8c15..263bf09 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -913,7 +913,7 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
     case SVGA_REG_ENABLE:
         s->enable = !!value;
         s->invalidated = 1;
-        s->vga.invalidate(&s->vga);
+        s->vga.hw_ops->invalidate(&s->vga);
         if (s->enable && s->config) {
             vga_dirty_log_stop(&s->vga);
         } else {
@@ -1058,7 +1058,7 @@ static void vmsvga_update_display(void *opaque)
     bool dirty = false;
 
     if (!s->enable) {
-        s->vga.update(&s->vga);
+        s->vga.hw_ops->gfx_update(&s->vga);
         return;
     }
 
@@ -1112,7 +1112,7 @@ static void vmsvga_invalidate_display(void *opaque)
 {
     struct vmsvga_state_s *s = opaque;
     if (!s->enable) {
-        s->vga.invalidate(&s->vga);
+        s->vga.hw_ops->invalidate(&s->vga);
         return;
     }
 
@@ -1123,8 +1123,8 @@ static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
 {
     struct vmsvga_state_s *s = opaque;
 
-    if (s->vga.text_update) {
-        s->vga.text_update(&s->vga, chardata);
+    if (s->vga.hw_ops->text_update) {
+        s->vga.hw_ops->text_update(&s->vga, chardata);
     }
 }
 
@@ -1179,15 +1179,19 @@ static const VMStateDescription vmstate_vmware_vga = {
     }
 };
 
+static const GraphicHwOps vmsvga_ops = {
+    .invalidate  = vmsvga_invalidate_display,
+    .gfx_update  = vmsvga_update_display,
+    .text_update = vmsvga_text_update,
+};
+
 static void vmsvga_init(struct vmsvga_state_s *s,
                         MemoryRegion *address_space, MemoryRegion *io)
 {
     s->scratch_size = SVGA_SCRATCH_SIZE;
     s->scratch = g_malloc(s->scratch_size * 4);
 
-    s->vga.con = graphic_console_init(vmsvga_update_display,
-                                      vmsvga_invalidate_display,
-                                      vmsvga_text_update, s);
+    s->vga.con = graphic_console_init(&vmsvga_ops, s);
 
     s->fifo_size = SVGA_FIFO_SIZE;
     memory_region_init_ram(&s->fifo_ram, "vmsvga.fifo", s->fifo_size);
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index 80cc9e8..8d327f1 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -977,6 +977,11 @@ struct XenDevOps xen_framebuffer_ops = {
     .frontend_changed = fb_frontend_changed,
 };
 
+static const GraphicHwOps xenfb_ops = {
+    .invalidate  = xenfb_invalidate,
+    .gfx_update  = xenfb_update,
+};
+
 /*
  * FIXME/TODO: Kill this.
  * Temporary needed while DisplayState reorganization is in flight.
@@ -1004,10 +1009,7 @@ wait_more:
 
     /* vfb */
     fb = container_of(xfb, struct XenFB, c.xendev);
-    fb->c.con = graphic_console_init(xenfb_update,
-                                     xenfb_invalidate,
-                                     NULL,
-                                     fb);
+    fb->c.con = graphic_console_init(&xenfb_ops, fb);
     fb->have_console = 1;
 
     /* vkbd */
diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c
index 7488547..f8d32bc 100644
--- a/hw/unicore32/puv3.c
+++ b/hw/unicore32/puv3.c
@@ -78,6 +78,8 @@ static void puv3_board_init(CPUUniCore32State *env, ram_addr_t ram_size)
     memory_region_add_subregion(get_system_memory(), 0, ram_memory);
 }
 
+static const GraphicHwOps no_ops;
+
 static void puv3_load_kernel(const char *kernel_filename)
 {
     int size;
@@ -92,7 +94,7 @@ static void puv3_load_kernel(const char *kernel_filename)
     }
 
     /* cheat curses that we have a graphic console, only under ocd console */
-    graphic_console_init(NULL, NULL, NULL, NULL);
+    graphic_console_init(&no_ops, NULL);
 }
 
 static void puv3_init(QEMUMachineInitArgs *args)
diff --git a/include/ui/console.h b/include/ui/console.h
index 50cd7b0..f3e7791 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -267,13 +267,13 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
     *dest = ch;
 }
 
-typedef void (*graphic_hw_update_ptr)(void *);
-typedef void (*graphic_hw_invalidate_ptr)(void *);
-typedef void (*graphic_hw_text_update_ptr)(void *, console_ch_t *);
+typedef struct GraphicHwOps {
+    void (*invalidate)(void *opaque);
+    void (*gfx_update)(void *opaque);
+    void (*text_update)(void *opaque, console_ch_t *text);
+} GraphicHwOps;
 
-QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
-                                  graphic_hw_invalidate_ptr invalidate,
-                                  graphic_hw_text_update_ptr text_update,
+QemuConsole *graphic_console_init(const GraphicHwOps *ops,
                                   void *opaque);
 
 void graphic_hw_update(QemuConsole *con);
diff --git a/ui/console.c b/ui/console.c
index 07fba67..79a306b 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -119,9 +119,7 @@ struct QemuConsole {
     DisplaySurface *surface;
 
     /* Graphic console state.  */
-    graphic_hw_update_ptr hw_update;
-    graphic_hw_invalidate_ptr hw_invalidate;
-    graphic_hw_text_update_ptr hw_text_update;
+    const GraphicHwOps *hw_ops;
     void *hw;
 
     /* Text console state */
@@ -229,8 +227,8 @@ void graphic_hw_update(QemuConsole *con)
     if (!con) {
         con = active_console;
     }
-    if (con && con->hw_update) {
-        con->hw_update(con->hw);
+    if (con && con->hw_ops->gfx_update) {
+        con->hw_ops->gfx_update(con->hw);
     }
 }
 
@@ -239,8 +237,8 @@ void graphic_hw_invalidate(QemuConsole *con)
     if (!con) {
         con = active_console;
     }
-    if (con && con->hw_invalidate) {
-        con->hw_invalidate(con->hw);
+    if (con && con->hw_ops->invalidate) {
+        con->hw_ops->invalidate(con->hw);
     }
 }
 
@@ -310,8 +308,9 @@ void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
     if (!con) {
         con = active_console;
     }
-    if (con && con->hw_text_update)
-        con->hw_text_update(con->hw, chardata);
+    if (con && con->hw_ops->text_update) {
+        con->hw_ops->text_update(con->hw, chardata);
+    }
 }
 
 static void vga_fill_rect(QemuConsole *con,
@@ -1493,9 +1492,7 @@ DisplayState *init_displaystate(void)
     return display_state;
 }
 
-QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
-                                  graphic_hw_invalidate_ptr invalidate,
-                                  graphic_hw_text_update_ptr text_update,
+QemuConsole *graphic_console_init(const GraphicHwOps *hw_ops,
                                   void *opaque)
 {
     int width = 640;
@@ -1506,9 +1503,7 @@ QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
     ds = get_alloc_displaystate();
     trace_console_gfx_new();
     s = new_console(ds, GRAPHIC_CONSOLE);
-    s->hw_update = update;
-    s->hw_invalidate = invalidate;
-    s->hw_text_update = text_update;
+    s->hw_ops = hw_ops;
     s->hw = opaque;
 
     s->surface = qemu_create_displaysurface(width, height);
@@ -1542,6 +1537,11 @@ static void text_console_update_cursor(void *opaque)
                    qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
 }
 
+static const GraphicHwOps text_console_ops = {
+    .invalidate  = text_console_invalidate,
+    .text_update = text_console_update,
+};
+
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 {
     QemuConsole *s;
@@ -1573,8 +1573,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
     s->cursor_timer =
         qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
 
-    s->hw_invalidate = text_console_invalidate;
-    s->hw_text_update = text_console_update;
+    s->hw_ops = &text_console_ops;
     s->hw = s;
 
     /* Set text attribute defaults */
commit 27be55872dd747c733a42a3d90864d9f59272d26
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 13 12:25:25 2013 +0100

    console: make DisplayState private to console.c
    
    With gui_* being moved to console.c nobody outside console.c needs
    access to DisplayState fields any more.  Make the struct private.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/console.h b/include/ui/console.h
index d92626b..50cd7b0 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -182,14 +182,6 @@ struct DisplayChangeListener {
     QLIST_ENTRY(DisplayChangeListener) next;
 };
 
-struct DisplayState {
-    struct QEMUTimer *gui_timer;
-    bool have_gfx;
-    bool have_text;
-
-    QLIST_HEAD(, DisplayChangeListener) listeners;
-};
-
 DisplayState *init_displaystate(void);
 DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
                                                 int linesize, uint8_t *data,
diff --git a/ui/console.c b/ui/console.c
index b618221..07fba67 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -157,6 +157,14 @@ struct QemuConsole {
     QEMUTimer *kbd_timer;
 };
 
+struct DisplayState {
+    struct QEMUTimer *gui_timer;
+    bool have_gfx;
+    bool have_text;
+
+    QLIST_HEAD(, DisplayChangeListener) listeners;
+};
+
 static DisplayState *display_state;
 static QemuConsole *active_console;
 static QemuConsole *consoles[MAX_CONSOLES];
commit 98a9ad9082284df62fb5b9355dd1901639de8268
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 13 12:17:13 2013 +0100

    console: move gui_update+gui_setup_refresh from vl.c into console.c
    
    Pure code motion, no functional changes.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/console.h b/include/ui/console.h
index d6e3e92..d92626b 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -213,8 +213,6 @@ static inline int is_buffer_shared(DisplaySurface *surface)
     return !(surface->flags & QEMU_ALLOCATED_FLAG);
 }
 
-void gui_setup_refresh(DisplayState *ds);
-
 void register_displaychangelistener(DisplayState *ds,
                                     DisplayChangeListener *dcl);
 void unregister_displaychangelistener(DisplayChangeListener *dcl);
diff --git a/ui/console.c b/ui/console.c
index dd1a0fc..b618221 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -166,6 +166,56 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
 static void dpy_gfx_switch_surface(DisplayState *ds,
                                    DisplaySurface *surface);
 
+static void gui_update(void *opaque)
+{
+    uint64_t interval = GUI_REFRESH_INTERVAL;
+    DisplayState *ds = opaque;
+    DisplayChangeListener *dcl;
+
+    dpy_refresh(ds);
+
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
+        if (dcl->gui_timer_interval &&
+            dcl->gui_timer_interval < interval) {
+            interval = dcl->gui_timer_interval;
+        }
+    }
+    qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
+}
+
+static void gui_setup_refresh(DisplayState *ds)
+{
+    DisplayChangeListener *dcl;
+    bool need_timer = false;
+    bool have_gfx = false;
+    bool have_text = false;
+
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
+        if (dcl->ops->dpy_refresh != NULL) {
+            need_timer = true;
+        }
+        if (dcl->ops->dpy_gfx_update != NULL) {
+            have_gfx = true;
+        }
+        if (dcl->ops->dpy_text_update != NULL) {
+            have_text = true;
+        }
+    }
+
+    if (need_timer && ds->gui_timer == NULL) {
+        ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
+        qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
+    }
+    if (!need_timer && ds->gui_timer != NULL) {
+        qemu_del_timer(ds->gui_timer);
+        qemu_free_timer(ds->gui_timer);
+        ds->gui_timer = NULL;
+    }
+
+    ds->have_gfx = have_gfx;
+    ds->have_text = have_text;
+}
+
 void graphic_hw_update(QemuConsole *con)
 {
     if (!con) {
diff --git a/vl.c b/vl.c
index 63fe9a4..2ef00d8 100644
--- a/vl.c
+++ b/vl.c
@@ -1626,55 +1626,6 @@ MachineInfoList *qmp_query_machines(Error **errp)
 /***********************************************************/
 /* main execution loop */
 
-static void gui_update(void *opaque)
-{
-    uint64_t interval = GUI_REFRESH_INTERVAL;
-    DisplayState *ds = opaque;
-    DisplayChangeListener *dcl;
-
-    dpy_refresh(ds);
-
-    QLIST_FOREACH(dcl, &ds->listeners, next) {
-        if (dcl->gui_timer_interval &&
-            dcl->gui_timer_interval < interval)
-            interval = dcl->gui_timer_interval;
-    }
-    qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock_ms(rt_clock));
-}
-
-void gui_setup_refresh(DisplayState *ds)
-{
-    DisplayChangeListener *dcl;
-    bool need_timer = false;
-    bool have_gfx = false;
-    bool have_text = false;
-
-    QLIST_FOREACH(dcl, &ds->listeners, next) {
-        if (dcl->ops->dpy_refresh != NULL) {
-            need_timer = true;
-        }
-        if (dcl->ops->dpy_gfx_update != NULL) {
-            have_gfx = true;
-        }
-        if (dcl->ops->dpy_text_update != NULL) {
-            have_text = true;
-        }
-    }
-
-    if (need_timer && ds->gui_timer == NULL) {
-        ds->gui_timer = qemu_new_timer_ms(rt_clock, gui_update, ds);
-        qemu_mod_timer(ds->gui_timer, qemu_get_clock_ms(rt_clock));
-    }
-    if (!need_timer && ds->gui_timer != NULL) {
-        qemu_del_timer(ds->gui_timer);
-        qemu_free_timer(ds->gui_timer);
-        ds->gui_timer = NULL;
-    }
-
-    ds->have_gfx = have_gfx;
-    ds->have_text = have_text;
-}
-
 struct vm_change_state_entry {
     VMChangeStateHandler *cb;
     void *opaque;
commit 36671fbd06f31efc592c37acda3f8a75599e48e0
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 13 10:14:52 2013 +0100

    console: zap g_width + g_height
    
    We have a surface per QemuConsole now, so there is no need to keep
    track of the QemuConsole size any more as we can query the surface
    size directly at any time.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/ui/console.c b/ui/console.c
index 537b2fc..dd1a0fc 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -123,7 +123,6 @@ struct QemuConsole {
     graphic_hw_invalidate_ptr hw_invalidate;
     graphic_hw_text_update_ptr hw_text_update;
     void *hw;
-    int g_width, g_height;
 
     /* Text console state */
     int width;
@@ -389,8 +388,8 @@ static void text_console_resize(QemuConsole *s)
     int w1, x, y, last_width;
 
     last_width = s->width;
-    s->width = s->g_width / FONT_WIDTH;
-    s->height = s->g_height / FONT_HEIGHT;
+    s->width = surface_width(s->surface) / FONT_WIDTH;
+    s->height = surface_height(s->surface) / FONT_HEIGHT;
 
     w1 = last_width;
     if (s->width < w1)
@@ -951,18 +950,12 @@ static void console_putchar(QemuConsole *s, int ch)
 
 void console_select(unsigned int index)
 {
-    DisplaySurface *surface;
     QemuConsole *s;
 
     if (index >= MAX_CONSOLES)
         return;
 
     trace_console_select(index);
-    if (active_console) {
-        surface = qemu_console_surface(active_console);
-        active_console->g_width = surface_width(surface);
-        active_console->g_height = surface_height(surface);
-    }
     s = consoles[index];
     if (s) {
         DisplayState *ds = s->ds;
@@ -1089,11 +1082,8 @@ void kbd_put_keysym(int keysym)
 static void text_console_invalidate(void *opaque)
 {
     QemuConsole *s = (QemuConsole *) opaque;
-    DisplaySurface *surface = qemu_console_surface(s);
 
     if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
-        s->g_width = surface_width(surface);
-        s->g_height = surface_height(surface);
         text_console_resize(s);
     }
     console_refresh(s);
@@ -1497,6 +1487,8 @@ static void text_console_update_cursor(void *opaque)
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
 {
     QemuConsole *s;
+    int g_width = 80 * FONT_WIDTH;
+    int g_height = 24 * FONT_HEIGHT;
 
     s = chr->opaque;
 
@@ -1512,16 +1504,13 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
     s->total_height = DEFAULT_BACKSCROLL;
     s->x = 0;
     s->y = 0;
-    if (s->console_type == TEXT_CONSOLE) {
+    if (!s->surface) {
         if (active_console && active_console->surface) {
-            s->g_width = surface_width(active_console->surface);
-            s->g_height = surface_height(active_console->surface);
-        } else {
-            s->g_width = 80 * FONT_WIDTH;
-            s->g_height = 24 * FONT_HEIGHT;
+            g_width = surface_width(active_console->surface);
+            g_height = surface_height(active_console->surface);
         }
+        s->surface = qemu_create_displaysurface(g_width, g_height);
     }
-    s->surface = qemu_create_displaysurface(s->g_width, s->g_height);
 
     s->cursor_timer =
         qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
@@ -1583,6 +1572,7 @@ static CharDriverState *text_console_init(ChardevVC *vc)
         s = new_console(NULL, TEXT_CONSOLE);
     } else {
         s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE);
+        s->surface = qemu_create_displaysurface(width, height);
     }
 
     if (!s) {
@@ -1591,8 +1581,6 @@ static CharDriverState *text_console_init(ChardevVC *vc)
     }
 
     s->chr = chr;
-    s->g_width = width;
-    s->g_height = height;
     chr->opaque = s;
     chr->chr_set_echo = text_console_set_echo;
 
@@ -1619,8 +1607,6 @@ void qemu_console_resize(QemuConsole *s, int width, int height)
     DisplaySurface *surface;
 
     assert(s->console_type == GRAPHIC_CONSOLE);
-    s->g_width = width;
-    s->g_height = height;
     surface = qemu_create_displaysurface(width, height);
     dpy_gfx_replace_surface(s, surface);
 }
commit 2c62f08ddbf3fa80dc7202eb9a2ea60ae44e2cc5
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Mar 12 14:48:31 2013 +0100

    console: simplify screendump
    
    Screendumps are alot simpler as we can update non-active
    QemuConsoles now.  So we only need to update the QemuConsole
    we want write out, then dump the DisplaySurface content into
    a ppm file.  Done.
    
    No console switching needed.  No special support code in the
    gfx card emulation needed.  Zap it all.  Also move ppm_save
    out of the vga code and next to the qmp_screendump function.
    
    For now screen dumping is limited to console #0 (like it used
    to be), even though it is dead simple to extend it to other
    consoles.  I wanna finish the console cleanup before setting
    new qapi interfaces into stone.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
    Tested-by: Igor Mitsyanko <i.mitsyanko at gmail.com>

diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c
index d2247fa..6e77447 100644
--- a/hw/arm/musicpal.c
+++ b/hw/arm/musicpal.c
@@ -612,7 +612,7 @@ static int musicpal_lcd_init(SysBusDevice *dev)
     sysbus_init_mmio(dev, &s->iomem);
 
     s->con = graphic_console_init(lcd_refresh, lcd_invalidate,
-                                  NULL, NULL, s);
+                                  NULL, s);
     qemu_console_resize(s->con, 128*3, 64*3);
 
     qdev_init_gpio_in(&dev->qdev, musicpal_lcd_gpio_brigthness_in, 3);
diff --git a/hw/display/blizzard.c b/hw/display/blizzard.c
index de7ccf8..70b6822 100644
--- a/hw/display/blizzard.c
+++ b/hw/display/blizzard.c
@@ -933,18 +933,6 @@ static void blizzard_update_display(void *opaque)
     s->my[1] = 0;
 }
 
-static void blizzard_screen_dump(void *opaque, const char *filename,
-                                 bool cswitch, Error **errp)
-{
-    BlizzardState *s = (BlizzardState *) opaque;
-    DisplaySurface *surface = qemu_console_surface(s->con);
-
-    blizzard_update_display(opaque);
-    if (s && surface_data(surface)) {
-        ppm_save(filename, surface, errp);
-    }
-}
-
 #define DEPTH 8
 #include "blizzard_template.h"
 #define DEPTH 15
@@ -965,7 +953,7 @@ void *s1d13745_init(qemu_irq gpio_int)
 
     s->con = graphic_console_init(blizzard_update_display,
                                   blizzard_invalidate_display,
-                                  blizzard_screen_dump, NULL, s);
+                                  NULL, s);
     surface = qemu_console_surface(s->con);
 
     switch (surface_bits_per_pixel(surface)) {
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index 514bc33..c31b021 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -2911,7 +2911,7 @@ static int vga_initfn(ISADevice *dev)
     cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0,
                        isa_address_space(dev), isa_address_space_io(dev));
     s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->screen_dump, s->text_update,
+                                  s->text_update,
                                   s);
     rom_add_vga(VGABIOS_CIRRUS_FILENAME);
     /* XXX ISA-LFB support */
@@ -2960,7 +2960,7 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev)
      cirrus_init_common(s, device_id, 1, pci_address_space(dev),
                         pci_address_space_io(dev));
      s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
-                                       s->vga.screen_dump, s->vga.text_update,
+                                       s->vga.text_update,
                                        &s->vga);
 
      /* setup PCI */
diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 7e1cbb6..d651ddb 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1901,7 +1901,7 @@ static int exynos4210_fimd_init(SysBusDevice *dev)
             "exynos4210.fimd", FIMD_REGS_SIZE);
     sysbus_init_mmio(dev, &s->iomem);
     s->console = graphic_console_init(exynos4210_fimd_update,
-                                  exynos4210_fimd_invalidate, NULL, NULL, s);
+                                      exynos4210_fimd_invalidate, NULL, s);
 
     return 0;
 }
diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c
index f7014e9..b70fe8a 100644
--- a/hw/display/g364fb.c
+++ b/hw/display/g364fb.c
@@ -294,77 +294,6 @@ static void g364fb_reset(G364State *s)
     g364fb_invalidate_display(s);
 }
 
-static void g364fb_screen_dump(void *opaque, const char *filename, bool cswitch,
-                               Error **errp)
-{
-    G364State *s = opaque;
-    int ret, y, x;
-    uint8_t index;
-    uint8_t *data_buffer;
-    FILE *f;
-
-    qemu_flush_coalesced_mmio_buffer();
-
-    if (s->depth != 8) {
-        error_setg(errp, "g364: unknown guest depth %d", s->depth);
-        return;
-    }
-
-    f = fopen(filename, "wb");
-    if (!f) {
-        error_setg(errp, "failed to open file '%s': %s", filename,
-                   strerror(errno));
-        return;
-    }
-
-    if (s->ctla & CTLA_FORCE_BLANK) {
-        /* blank screen */
-        ret = fprintf(f, "P4\n%d %d\n", s->width, s->height);
-        if (ret < 0) {
-            goto write_err;
-        }
-        for (y = 0; y < s->height; y++)
-            for (x = 0; x < s->width; x++) {
-                ret = fputc(0, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-            }
-    } else {
-        data_buffer = s->vram + s->top_of_screen;
-        ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
-        if (ret < 0) {
-            goto write_err;
-        }
-        for (y = 0; y < s->height; y++)
-            for (x = 0; x < s->width; x++, data_buffer++) {
-                index = *data_buffer;
-                ret = fputc(s->color_palette[index][0], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc(s->color_palette[index][1], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc(s->color_palette[index][2], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-        }
-    }
-
-out:
-    fclose(f);
-    return;
-
-write_err:
-    error_setg(errp, "failed to write to file '%s': %s", filename,
-               strerror(errno));
-    unlink(filename);
-    goto out;
-}
-
 /* called for accesses to io ports */
 static uint64_t g364fb_ctrl_read(void *opaque,
                                  hwaddr addr,
@@ -552,7 +481,7 @@ static void g364fb_init(DeviceState *dev, G364State *s)
 
     s->con = graphic_console_init(g364fb_update_display,
                                   g364fb_invalidate_display,
-                                  g364fb_screen_dump, NULL, s);
+                                  NULL, s);
 
     memory_region_init_io(&s->mem_ctrl, &g364fb_ctrl_ops, s, "ctrl", 0x180000);
     memory_region_init_ram_ptr(&s->mem_vram, "vram",
diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c
index 05528c7..c027f76 100644
--- a/hw/display/jazz_led.c
+++ b/hw/display/jazz_led.c
@@ -263,7 +263,6 @@ static int jazz_led_init(SysBusDevice *dev)
 
     s->con = graphic_console_init(jazz_led_update_display,
                                   jazz_led_invalidate_display,
-                                  NULL,
                                   jazz_led_text_update, s);
 
     return 0;
diff --git a/hw/display/milkymist-vgafb.c b/hw/display/milkymist-vgafb.c
index 3219041..9bdb5c7 100644
--- a/hw/display/milkymist-vgafb.c
+++ b/hw/display/milkymist-vgafb.c
@@ -280,7 +280,7 @@ static int milkymist_vgafb_init(SysBusDevice *dev)
 
     s->con = graphic_console_init(vgafb_update_display,
                                   vgafb_invalidate_display,
-                                  NULL, NULL, s);
+                                  NULL, s);
 
     return 0;
 }
diff --git a/hw/display/omap_lcdc.c b/hw/display/omap_lcdc.c
index be7e9c0..f76f613 100644
--- a/hw/display/omap_lcdc.c
+++ b/hw/display/omap_lcdc.c
@@ -227,90 +227,6 @@ static void omap_update_display(void *opaque)
     omap_lcd->invalidate = 0;
 }
 
-static void omap_ppm_save(const char *filename, uint8_t *data,
-                    int w, int h, int linesize, Error **errp)
-{
-    FILE *f;
-    uint8_t *d, *d1;
-    unsigned int v;
-    int ret, y, x, bpp;
-
-    f = fopen(filename, "wb");
-    if (!f) {
-        error_setg(errp, "failed to open file '%s': %s", filename,
-                   strerror(errno));
-        return;
-    }
-    ret = fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
-    if (ret < 0) {
-        goto write_err;
-    }
-    d1 = data;
-    bpp = linesize / w;
-    for (y = 0; y < h; y ++) {
-        d = d1;
-        for (x = 0; x < w; x ++) {
-            v = *(uint32_t *) d;
-            switch (bpp) {
-            case 2:
-                ret = fputc((v >> 8) & 0xf8, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc((v >> 3) & 0xfc, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc((v << 3) & 0xf8, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                break;
-            case 3:
-            case 4:
-            default:
-                ret = fputc((v >> 16) & 0xff, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc((v >> 8) & 0xff, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc((v) & 0xff, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                break;
-            }
-            d += bpp;
-        }
-        d1 += linesize;
-    }
-out:
-    fclose(f);
-    return;
-
-write_err:
-    error_setg(errp, "failed to write to file '%s': %s", filename,
-               strerror(errno));
-    unlink(filename);
-    goto out;
-}
-
-static void omap_screen_dump(void *opaque, const char *filename, bool cswitch,
-                             Error **errp)
-{
-    struct omap_lcd_panel_s *omap_lcd = opaque;
-    DisplaySurface *surface = qemu_console_surface(omap_lcd->con);
-
-    omap_update_display(opaque);
-    if (omap_lcd && surface_data(surface))
-        omap_ppm_save(filename, surface_data(surface),
-                    omap_lcd->width, omap_lcd->height,
-                    surface_stride(surface), errp);
-}
-
 static void omap_invalidate_display(void *opaque) {
     struct omap_lcd_panel_s *omap_lcd = opaque;
     omap_lcd->invalidate = 1;
@@ -487,7 +403,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(MemoryRegion *sysmem,
 
     s->con = graphic_console_init(omap_update_display,
                                   omap_invalidate_display,
-                                  omap_screen_dump, NULL, s);
+                                  NULL, s);
 
     return s;
 }
diff --git a/hw/display/pl110.c b/hw/display/pl110.c
index 295434e..5599755 100644
--- a/hw/display/pl110.c
+++ b/hw/display/pl110.c
@@ -454,7 +454,7 @@ static int pl110_init(SysBusDevice *dev)
     qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
     s->con = graphic_console_init(pl110_update_display,
                                   pl110_invalidate_display,
-                                  NULL, NULL, s);
+                                  NULL, s);
     return 0;
 }
 
diff --git a/hw/display/pxa2xx_lcd.c b/hw/display/pxa2xx_lcd.c
index c9bd42e..d38479d 100644
--- a/hw/display/pxa2xx_lcd.c
+++ b/hw/display/pxa2xx_lcd.c
@@ -1010,7 +1010,7 @@ PXA2xxLCDState *pxa2xx_lcdc_init(MemoryRegion *sysmem,
 
     s->con = graphic_console_init(pxa2xx_update_display,
                                   pxa2xx_invalidate_display,
-                                  NULL, NULL, s);
+                                  NULL, s);
     surface = qemu_console_surface(s->con);
 
     switch (surface_bits_per_pixel(surface)) {
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 247209d..3b09d20 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1772,26 +1772,6 @@ static void qxl_hw_invalidate(void *opaque)
     vga->invalidate(vga);
 }
 
-static void qxl_hw_screen_dump(void *opaque, const char *filename, bool cswitch,
-                               Error **errp)
-{
-    PCIQXLDevice *qxl = opaque;
-    VGACommonState *vga = &qxl->vga;
-
-    switch (qxl->mode) {
-    case QXL_MODE_COMPAT:
-    case QXL_MODE_NATIVE:
-        qxl_render_update(qxl);
-        ppm_save(filename, qxl->ssd.ds, errp);
-        break;
-    case QXL_MODE_VGA:
-        vga->screen_dump(vga, filename, cswitch, errp);
-        break;
-    default:
-        break;
-    }
-}
-
 static void qxl_hw_text_update(void *opaque, console_ch_t *chardata)
 {
     PCIQXLDevice *qxl = opaque;
@@ -2075,7 +2055,7 @@ static int qxl_init_primary(PCIDevice *dev)
     portio_list_add(qxl_vga_port_list, pci_address_space_io(dev), 0x3b0);
 
     vga->con = graphic_console_init(qxl_hw_update, qxl_hw_invalidate,
-                                    qxl_hw_screen_dump, qxl_hw_text_update,
+                                    qxl_hw_text_update,
                                     qxl);
     qxl->ssd.con = vga->con,
     qemu_spice_display_init_common(&qxl->ssd);
diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index 9878df4..e57ff22 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -1446,5 +1446,5 @@ void sm501_init(MemoryRegion *address_space_mem, uint32_t base,
 
     /* create qemu graphic console */
     s->con = graphic_console_init(sm501_update_display, NULL,
-                                  NULL, NULL, s);
+                                  NULL, s);
 }
diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c
index 183a878..9b6810f 100644
--- a/hw/display/ssd0303.c
+++ b/hw/display/ssd0303.c
@@ -290,7 +290,7 @@ static int ssd0303_init(I2CSlave *i2c)
 
     s->con = graphic_console_init(ssd0303_update_display,
                                   ssd0303_invalidate_display,
-                                  NULL, NULL, s);
+                                  NULL, s);
     qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY);
     return 0;
 }
diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c
index 5cf2f70..301cb20 100644
--- a/hw/display/ssd0323.c
+++ b/hw/display/ssd0323.c
@@ -339,7 +339,7 @@ static int ssd0323_init(SSISlave *dev)
     s->row_end = 79;
     s->con = graphic_console_init(ssd0323_update_display,
                                   ssd0323_invalidate_display,
-                                  NULL, NULL, s);
+                                  NULL, s);
     qemu_console_resize(s->con, 128 * MAGNIFY, 64 * MAGNIFY);
 
     qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1);
diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c
index 178a21f..01beba4 100644
--- a/hw/display/tc6393xb.c
+++ b/hw/display/tc6393xb.c
@@ -585,7 +585,6 @@ TC6393xbState *tc6393xb_init(MemoryRegion *sysmem, uint32_t base, qemu_irq irq)
     s->scr_height = 640;
     s->con = graphic_console_init(tc6393xb_update_display,
             NULL, /* invalidate */
-            NULL, /* screen_dump */
             NULL, /* text_update */
             s);
 
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index c44068e..ba3857a 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -56,11 +56,6 @@ typedef struct TCXState {
     uint8_t dac_index, dac_state;
 } TCXState;
 
-static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
-                            Error **errp);
-static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
-                            Error **errp);
-
 static void tcx_set_dirty(TCXState *s)
 {
     memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
@@ -569,7 +564,7 @@ static int tcx_init1(SysBusDevice *dev)
 
         s->con = graphic_console_init(tcx24_update_display,
                                       tcx24_invalidate_display,
-                                      tcx24_screen_dump, NULL, s);
+                                      NULL, s);
     } else {
         /* THC 8 bit (dummy) */
         memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8",
@@ -578,133 +573,13 @@ static int tcx_init1(SysBusDevice *dev)
 
         s->con = graphic_console_init(tcx_update_display,
                                       tcx_invalidate_display,
-                                      tcx_screen_dump, NULL, s);
+                                      NULL, s);
     }
 
     qemu_console_resize(s->con, s->width, s->height);
     return 0;
 }
 
-static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
-                            Error **errp)
-{
-    TCXState *s = opaque;
-    FILE *f;
-    uint8_t *d, *d1, v;
-    int ret, y, x;
-
-    f = fopen(filename, "wb");
-    if (!f) {
-        error_setg(errp, "failed to open file '%s': %s", filename,
-                   strerror(errno));
-        return;
-    }
-    ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
-    if (ret < 0) {
-        goto write_err;
-    }
-    d1 = s->vram;
-    for(y = 0; y < s->height; y++) {
-        d = d1;
-        for(x = 0; x < s->width; x++) {
-            v = *d;
-            ret = fputc(s->r[v], f);
-            if (ret == EOF) {
-                goto write_err;
-            }
-            ret = fputc(s->g[v], f);
-            if (ret == EOF) {
-                goto write_err;
-            }
-            ret = fputc(s->b[v], f);
-            if (ret == EOF) {
-                goto write_err;
-            }
-            d++;
-        }
-        d1 += MAXX;
-    }
-
-out:
-    fclose(f);
-    return;
-
-write_err:
-    error_setg(errp, "failed to write to file '%s': %s", filename,
-               strerror(errno));
-    unlink(filename);
-    goto out;
-}
-
-static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
-                              Error **errp)
-{
-    TCXState *s = opaque;
-    FILE *f;
-    uint8_t *d, *d1, v;
-    uint32_t *s24, *cptr, dval;
-    int ret, y, x;
-
-    f = fopen(filename, "wb");
-    if (!f) {
-        error_setg(errp, "failed to open file '%s': %s", filename,
-                   strerror(errno));
-        return;
-    }
-    ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
-    if (ret < 0) {
-        goto write_err;
-    }
-    d1 = s->vram;
-    s24 = s->vram24;
-    cptr = s->cplane;
-    for(y = 0; y < s->height; y++) {
-        d = d1;
-        for(x = 0; x < s->width; x++, d++, s24++) {
-            if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
-                dval = *s24 & 0x00ffffff;
-                ret = fputc((dval >> 16) & 0xff, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc((dval >> 8) & 0xff, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc(dval & 0xff, f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-            } else {
-                v = *d;
-                ret = fputc(s->r[v], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc(s->g[v], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-                ret = fputc(s->b[v], f);
-                if (ret == EOF) {
-                    goto write_err;
-                }
-            }
-        }
-        d1 += MAXX;
-    }
-
-out:
-    fclose(f);
-    return;
-
-write_err:
-    error_setg(errp, "failed to write to file '%s': %s", filename,
-               strerror(errno));
-    unlink(filename);
-    goto out;
-}
-
 static Property tcx_properties[] = {
     DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
     DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c
index 1c50070..e177197 100644
--- a/hw/display/vga-isa-mm.c
+++ b/hw/display/vga-isa-mm.c
@@ -136,7 +136,7 @@ int isa_vga_mm_init(hwaddr vram_base,
     vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
 
     s->vga.con = graphic_console_init(s->vga.update, s->vga.invalidate,
-                                      s->vga.screen_dump, s->vga.text_update,
+                                      s->vga.text_update,
                                       s);
 
     vga_init_vbe(&s->vga, address_space);
diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c
index 90959eb..228657e 100644
--- a/hw/display/vga-isa.c
+++ b/hw/display/vga-isa.c
@@ -63,7 +63,7 @@ static int vga_initfn(ISADevice *dev)
                                         vga_io_memory, 1);
     memory_region_set_coalescing(vga_io_memory);
     s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->screen_dump, s->text_update, s);
+                                  s->text_update, s);
 
     vga_init_vbe(s, isa_address_space(dev));
     /* ROM BIOS */
diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c
index a9c69b6..a6245b4 100644
--- a/hw/display/vga-pci.c
+++ b/hw/display/vga-pci.c
@@ -151,7 +151,7 @@ static int pci_std_vga_initfn(PCIDevice *dev)
     vga_init(s, pci_address_space(dev), pci_address_space_io(dev), true);
 
     s->con = graphic_console_init(s->update, s->invalidate,
-                                  s->screen_dump, s->text_update, s);
+                                  s->text_update, s);
 
     /* XXX: VGA_RAM_SIZE must be a power of two */
     pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
diff --git a/hw/display/vga.c b/hw/display/vga.c
index e37e898..5d7684a 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -166,9 +166,6 @@ static uint32_t expand4[256];
 static uint16_t expand2[256];
 static uint8_t expand4to8[16];
 
-static void vga_screen_dump(void *opaque, const char *filename, bool cswitch,
-                            Error **errp);
-
 static void vga_update_memory_access(VGACommonState *s)
 {
     MemoryRegion *region, *old_region = s->chain4_alias;
@@ -2298,7 +2295,6 @@ void vga_common_init(VGACommonState *s)
     s->get_resolution = vga_get_resolution;
     s->update = vga_update_display;
     s->invalidate = vga_invalidate_display;
-    s->screen_dump = vga_screen_dump;
     s->text_update = vga_update_text;
     switch (vga_retrace_method) {
     case VGA_RETRACE_DUMB:
@@ -2393,65 +2389,3 @@ void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory)
                                 &s->vram_vbe);
     s->vbe_mapped = 1;
 }
-/********************************************************/
-/* vga screen dump */
-
-void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp)
-{
-    int width = pixman_image_get_width(ds->image);
-    int height = pixman_image_get_height(ds->image);
-    FILE *f;
-    int y;
-    int ret;
-    pixman_image_t *linebuf;
-
-    trace_ppm_save(filename, ds);
-    f = fopen(filename, "wb");
-    if (!f) {
-        error_setg(errp, "failed to open file '%s': %s", filename,
-                   strerror(errno));
-        return;
-    }
-    ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
-    if (ret < 0) {
-        linebuf = NULL;
-        goto write_err;
-    }
-    linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
-    for (y = 0; y < height; y++) {
-        qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
-        clearerr(f);
-        ret = fwrite(pixman_image_get_data(linebuf), 1,
-                     pixman_image_get_stride(linebuf), f);
-        (void)ret;
-        if (ferror(f)) {
-            goto write_err;
-        }
-    }
-
-out:
-    qemu_pixman_image_unref(linebuf);
-    fclose(f);
-    return;
-
-write_err:
-    error_setg(errp, "failed to write to file '%s': %s", filename,
-               strerror(errno));
-    unlink(filename);
-    goto out;
-}
-
-/* save the vga display in a PPM image even if no display is
-   available */
-static void vga_screen_dump(void *opaque, const char *filename, bool cswitch,
-                            Error **errp)
-{
-    VGACommonState *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->con);
-
-    if (cswitch) {
-        vga_invalidate_display(s);
-    }
-    graphic_hw_update(s->con);
-    ppm_save(filename, surface, errp);
-}
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index 1b8f670..e4bb4a0 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -154,7 +154,6 @@ typedef struct VGACommonState {
                                  unsigned int g, unsigned b);
     graphic_hw_update_ptr update;
     graphic_hw_invalidate_ptr invalidate;
-    graphic_hw_screen_dump_ptr screen_dump;
     graphic_hw_text_update_ptr text_update;
     bool full_update_text;
     bool full_update_gfx;
@@ -198,7 +197,6 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val);
 uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr);
 void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val);
 void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2);
-void ppm_save(const char *filename, struct DisplaySurface *ds, Error **errp);
 
 int vga_ioport_invalid(VGACommonState *s, uint32_t addr);
 
diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 05befe4..61d8c15 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -1119,31 +1119,6 @@ static void vmsvga_invalidate_display(void *opaque)
     s->invalidated = 1;
 }
 
-/* save the vga display in a PPM image even if no display is
-   available */
-static void vmsvga_screen_dump(void *opaque, const char *filename, bool cswitch,
-                               Error **errp)
-{
-    struct vmsvga_state_s *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
-
-    if (!s->enable) {
-        s->vga.screen_dump(&s->vga, filename, cswitch, errp);
-        return;
-    }
-
-    if (surface_bits_per_pixel(surface) == 32) {
-        DisplaySurface *ds = qemu_create_displaysurface_from(
-                                 surface_width(surface),
-                                 surface_height(surface),
-                                 32,
-                                 surface_stride(surface),
-                                 s->vga.vram_ptr, false);
-        ppm_save(filename, ds, errp);
-        g_free(ds);
-    }
-}
-
 static void vmsvga_text_update(void *opaque, console_ch_t *chardata)
 {
     struct vmsvga_state_s *s = opaque;
@@ -1212,7 +1187,6 @@ static void vmsvga_init(struct vmsvga_state_s *s,
 
     s->vga.con = graphic_console_init(vmsvga_update_display,
                                       vmsvga_invalidate_display,
-                                      vmsvga_screen_dump,
                                       vmsvga_text_update, s);
 
     s->fifo_size = SVGA_FIFO_SIZE;
diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c
index e371569..80cc9e8 100644
--- a/hw/display/xenfb.c
+++ b/hw/display/xenfb.c
@@ -1007,7 +1007,6 @@ wait_more:
     fb->c.con = graphic_console_init(xenfb_update,
                                      xenfb_invalidate,
                                      NULL,
-                                     NULL,
                                      fb);
     fb->have_console = 1;
 
diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c
index 7c8fc36..7488547 100644
--- a/hw/unicore32/puv3.c
+++ b/hw/unicore32/puv3.c
@@ -92,7 +92,7 @@ static void puv3_load_kernel(const char *kernel_filename)
     }
 
     /* cheat curses that we have a graphic console, only under ocd console */
-    graphic_console_init(NULL, NULL, NULL, NULL, NULL);
+    graphic_console_init(NULL, NULL, NULL, NULL);
 }
 
 static void puv3_init(QEMUMachineInitArgs *args)
diff --git a/include/ui/console.h b/include/ui/console.h
index 0dd66fd..d6e3e92 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -279,13 +279,10 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
 
 typedef void (*graphic_hw_update_ptr)(void *);
 typedef void (*graphic_hw_invalidate_ptr)(void *);
-typedef void (*graphic_hw_screen_dump_ptr)(void *, const char *, bool cswitch,
-                                       Error **errp);
 typedef void (*graphic_hw_text_update_ptr)(void *, console_ch_t *);
 
 QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
                                   graphic_hw_invalidate_ptr invalidate,
-                                  graphic_hw_screen_dump_ptr screen_dump,
                                   graphic_hw_text_update_ptr text_update,
                                   void *opaque);
 
diff --git a/ui/console.c b/ui/console.c
index e8e548e..537b2fc 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -121,7 +121,6 @@ struct QemuConsole {
     /* Graphic console state.  */
     graphic_hw_update_ptr hw_update;
     graphic_hw_invalidate_ptr hw_invalidate;
-    graphic_hw_screen_dump_ptr hw_screen_dump;
     graphic_hw_text_update_ptr hw_text_update;
     void *hw;
     int g_width, g_height;
@@ -188,28 +187,65 @@ void graphic_hw_invalidate(QemuConsole *con)
     }
 }
 
-void qmp_screendump(const char *filename, Error **errp)
+static void ppm_save(const char *filename, struct DisplaySurface *ds,
+                     Error **errp)
 {
-    QemuConsole *previous_active_console;
-    bool cswitch;
-
-    previous_active_console = active_console;
-    cswitch = previous_active_console && previous_active_console->index != 0;
+    int width = pixman_image_get_width(ds->image);
+    int height = pixman_image_get_height(ds->image);
+    FILE *f;
+    int y;
+    int ret;
+    pixman_image_t *linebuf;
 
-    /* There is currently no way of specifying which screen we want to dump,
-       so always dump the first one.  */
-    if (cswitch) {
-        console_select(0);
+    trace_ppm_save(filename, ds);
+    f = fopen(filename, "wb");
+    if (!f) {
+        error_setg(errp, "failed to open file '%s': %s", filename,
+                   strerror(errno));
+        return;
     }
-    if (consoles[0] && consoles[0]->hw_screen_dump) {
-        consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch, errp);
-    } else {
-        error_setg(errp, "device doesn't support screendump");
+    ret = fprintf(f, "P6\n%d %d\n%d\n", width, height, 255);
+    if (ret < 0) {
+        linebuf = NULL;
+        goto write_err;
+    }
+    linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
+    for (y = 0; y < height; y++) {
+        qemu_pixman_linebuf_fill(linebuf, ds->image, width, 0, y);
+        clearerr(f);
+        ret = fwrite(pixman_image_get_data(linebuf), 1,
+                     pixman_image_get_stride(linebuf), f);
+        (void)ret;
+        if (ferror(f)) {
+            goto write_err;
+        }
     }
 
-    if (cswitch) {
-        console_select(previous_active_console->index);
+out:
+    qemu_pixman_image_unref(linebuf);
+    fclose(f);
+    return;
+
+write_err:
+    error_setg(errp, "failed to write to file '%s': %s", filename,
+               strerror(errno));
+    unlink(filename);
+    goto out;
+}
+
+void qmp_screendump(const char *filename, Error **errp)
+{
+    QemuConsole *con = consoles[0];
+    DisplaySurface *surface;
+
+    if (con == NULL) {
+        error_setg(errp, "There is no QemuConsole I can screendump from.");
+        return;
     }
+
+    graphic_hw_update(con);
+    surface = qemu_console_surface(con);
+    ppm_save(filename, surface, errp);
 }
 
 void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
@@ -1411,7 +1447,6 @@ DisplayState *init_displaystate(void)
 
 QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
                                   graphic_hw_invalidate_ptr invalidate,
-                                  graphic_hw_screen_dump_ptr screen_dump,
                                   graphic_hw_text_update_ptr text_update,
                                   void *opaque)
 {
@@ -1425,7 +1460,6 @@ QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
     s = new_console(ds, GRAPHIC_CONSOLE);
     s->hw_update = update;
     s->hw_invalidate = invalidate;
-    s->hw_screen_dump = screen_dump;
     s->hw_text_update = text_update;
     s->hw = opaque;
 
commit 321f048d248472f1e90559976bb917d869981c68
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Mar 12 14:39:22 2013 +0100

    console: give each QemuConsole its own DisplaySurface
    
    Go away from the global DisplaySurface, give one to each QemuConsole
    instead.  With this patch applied it is possible to call
    graphics_hw_* functions with qemu consoles which are not the current
    foreground console.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/console.h b/include/ui/console.h
index 9c585c0..0dd66fd 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -183,7 +183,6 @@ struct DisplayChangeListener {
 };
 
 struct DisplayState {
-    struct DisplaySurface *surface;
     struct QEMUTimer *gui_timer;
     bool have_gfx;
     bool have_text;
diff --git a/ui/console.c b/ui/console.c
index dccc618..e8e548e 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -116,6 +116,7 @@ struct QemuConsole {
     int index;
     console_type_t console_type;
     DisplayState *ds;
+    DisplaySurface *surface;
 
     /* Graphic console state.  */
     graphic_hw_update_ptr hw_update;
@@ -164,6 +165,8 @@ static QemuConsole *consoles[MAX_CONSOLES];
 static int nb_consoles = 0;
 
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
+static void dpy_gfx_switch_surface(DisplayState *ds,
+                                   DisplaySurface *surface);
 
 void graphic_hw_update(QemuConsole *con)
 {
@@ -933,8 +936,9 @@ void console_select(unsigned int index)
         }
         active_console = s;
         if (ds->have_gfx) {
-            surface = qemu_create_displaysurface(s->g_width, s->g_height);
-            dpy_gfx_replace_surface(s, surface);
+            dpy_gfx_switch_surface(ds, s->surface);
+            dpy_gfx_update(s, 0, 0, surface_width(s->surface),
+                           surface_height(s->surface));
         }
         if (ds->have_text) {
             dpy_text_resize(s, s->width, s->height);
@@ -943,7 +947,6 @@ void console_select(unsigned int index)
             qemu_mod_timer(s->cursor_timer,
                    qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
         }
-        graphic_hw_invalidate(s);
     }
 }
 
@@ -1195,8 +1198,8 @@ void register_displaychangelistener(DisplayState *ds,
     dcl->ds = ds;
     QLIST_INSERT_HEAD(&ds->listeners, dcl, next);
     gui_setup_refresh(ds);
-    if (dcl->ops->dpy_gfx_switch) {
-        dcl->ops->dpy_gfx_switch(dcl, ds->surface);
+    if (dcl->ops->dpy_gfx_switch && active_console) {
+        dcl->ops->dpy_gfx_switch(dcl, active_console->surface);
     }
 }
 
@@ -1212,8 +1215,8 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
-    int width = pixman_image_get_width(s->surface->image);
-    int height = pixman_image_get_height(s->surface->image);
+    int width = surface_width(con->surface);
+    int height = surface_height(con->surface);
 
     x = MAX(x, 0);
     y = MAX(y, 0);
@@ -1222,6 +1225,9 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
     w = MIN(w, width - x);
     h = MIN(h, height - y);
 
+    if (con != active_console) {
+        return;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_gfx_update) {
             dcl->ops->dpy_gfx_update(dcl, x, y, w, h);
@@ -1229,19 +1235,28 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
     }
 }
 
-void dpy_gfx_replace_surface(QemuConsole *con,
-                             DisplaySurface *surface)
+static void dpy_gfx_switch_surface(DisplayState *ds,
+                                   DisplaySurface *surface)
 {
-    DisplayState *s = con->ds;
-    DisplaySurface *old_surface = s->surface;
     struct DisplayChangeListener *dcl;
 
-    s->surface = surface;
-    QLIST_FOREACH(dcl, &s->listeners, next) {
+    QLIST_FOREACH(dcl, &ds->listeners, next) {
         if (dcl->ops->dpy_gfx_switch) {
             dcl->ops->dpy_gfx_switch(dcl, surface);
         }
     }
+}
+
+void dpy_gfx_replace_surface(QemuConsole *con,
+                             DisplaySurface *surface)
+{
+    DisplayState *s = con->ds;
+    DisplaySurface *old_surface = con->surface;
+
+    con->surface = surface;
+    if (con == active_console) {
+        dpy_gfx_switch_surface(s, surface);
+    }
     qemu_free_displaysurface(old_surface);
 }
 
@@ -1260,6 +1275,10 @@ void dpy_gfx_copy(QemuConsole *con, int src_x, int src_y,
 {
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
+
+    if (con != active_console) {
+        return;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_gfx_copy) {
             dcl->ops->dpy_gfx_copy(dcl, src_x, src_y, dst_x, dst_y, w, h);
@@ -1273,6 +1292,10 @@ void dpy_text_cursor(QemuConsole *con, int x, int y)
 {
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
+
+    if (con != active_console) {
+        return;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_text_cursor) {
             dcl->ops->dpy_text_cursor(dcl, x, y);
@@ -1284,6 +1307,10 @@ void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
 {
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
+
+    if (con != active_console) {
+        return;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_text_update) {
             dcl->ops->dpy_text_update(dcl, x, y, w, h);
@@ -1295,6 +1322,10 @@ void dpy_text_resize(QemuConsole *con, int w, int h)
 {
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
+
+    if (con != active_console) {
+        return;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_text_resize) {
             dcl->ops->dpy_text_resize(dcl, w, h);
@@ -1306,6 +1337,10 @@ void dpy_mouse_set(QemuConsole *con, int x, int y, int on)
 {
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
+
+    if (con != active_console) {
+        return;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_mouse_set) {
             dcl->ops->dpy_mouse_set(dcl, x, y, on);
@@ -1317,6 +1352,10 @@ void dpy_cursor_define(QemuConsole *con, QEMUCursor *cursor)
 {
     DisplayState *s = con->ds;
     struct DisplayChangeListener *dcl;
+
+    if (con != active_console) {
+        return;
+    }
     QLIST_FOREACH(dcl, &s->listeners, next) {
         if (dcl->ops->dpy_cursor_define) {
             dcl->ops->dpy_cursor_define(dcl, cursor);
@@ -1390,9 +1429,7 @@ QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
     s->hw_text_update = text_update;
     s->hw = opaque;
 
-    if (!ds->surface) {
-        ds->surface = qemu_create_displaysurface(width, height);
-    }
+    s->surface = qemu_create_displaysurface(width, height);
     return s;
 }
 
@@ -1442,9 +1479,15 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
     s->x = 0;
     s->y = 0;
     if (s->console_type == TEXT_CONSOLE) {
-        s->g_width = surface_width(s->ds->surface);
-        s->g_height = surface_height(s->ds->surface);
+        if (active_console && active_console->surface) {
+            s->g_width = surface_width(active_console->surface);
+            s->g_height = surface_height(active_console->surface);
+        } else {
+            s->g_width = 80 * FONT_WIDTH;
+            s->g_height = 24 * FONT_HEIGHT;
+        }
     }
+    s->surface = qemu_create_displaysurface(s->g_width, s->g_height);
 
     s->cursor_timer =
         qemu_new_timer_ms(rt_clock, text_console_update_cursor, s);
@@ -1539,26 +1582,25 @@ void register_vc_handler(VcHandler *handler)
 
 void qemu_console_resize(QemuConsole *s, int width, int height)
 {
+    DisplaySurface *surface;
+
+    assert(s->console_type == GRAPHIC_CONSOLE);
     s->g_width = width;
     s->g_height = height;
-    if (is_graphic_console()) {
-        DisplaySurface *surface;
-        surface = qemu_create_displaysurface(width, height);
-        dpy_gfx_replace_surface(s, surface);
-    }
+    surface = qemu_create_displaysurface(width, height);
+    dpy_gfx_replace_surface(s, surface);
 }
 
 void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
                        int dst_x, int dst_y, int w, int h)
 {
-    if (is_graphic_console()) {
-        dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
-    }
+    assert(con->console_type == GRAPHIC_CONSOLE);
+    dpy_gfx_copy(con, src_x, src_y, dst_x, dst_y, w, h);
 }
 
 DisplaySurface *qemu_console_surface(QemuConsole *console)
 {
-    return console->ds->surface;
+    return console->surface;
 }
 
 DisplayState *qemu_console_displaystate(QemuConsole *console)
commit 1dbfa005032d4fa5d7a5242da856d3487c907431
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Tue Mar 12 13:44:38 2013 +0100

    console: rename vga_hw_*, add QemuConsole param
    
    Add QemuConsole parameter to vga_hw_*, so the interface allows to update
    non-active consoles (the actual code can't handle this yet, see next
    patch).  Passing NULL is allowed and updates the active console, like
    the functions do today.
    
    While touching all vga_hw_* calls anyway rename that to the functions to
    hardware-neutral graphics_hw_*
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index bf2181a..514bc33 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -720,7 +720,7 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
     /* we have to flush all pending changes so that the copy
        is generated at the appropriate moment in time */
     if (notify)
-	vga_hw_update();
+        graphic_hw_update(s->vga.con);
 
     (*s->cirrus_rop) (s, s->vga.vram_ptr +
 		      (s->cirrus_blt_dstaddr & s->cirrus_addr_mask),
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
index 930b7cf..247209d 100644
--- a/hw/display/qxl.c
+++ b/hw/display/qxl.c
@@ -1074,7 +1074,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d)
     qemu_spice_create_host_primary(&d->ssd);
     d->mode = QXL_MODE_VGA;
     vga_dirty_log_start(&d->vga);
-    vga_hw_update();
+    graphic_hw_update(d->vga.con);
 }
 
 static void qxl_exit_vga_mode(PCIQXLDevice *d)
diff --git a/hw/display/vga.c b/hw/display/vga.c
index c1b67bb..e37e898 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -2452,6 +2452,6 @@ static void vga_screen_dump(void *opaque, const char *filename, bool cswitch,
     if (cswitch) {
         vga_invalidate_display(s);
     }
-    vga_hw_update();
+    graphic_hw_update(s->con);
     ppm_save(filename, surface, errp);
 }
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index 260f7d6..1b8f670 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -152,10 +152,10 @@ typedef struct VGACommonState {
     uint32_t cursor_offset;
     unsigned int (*rgb_to_pixel)(unsigned int r,
                                  unsigned int g, unsigned b);
-    vga_hw_update_ptr update;
-    vga_hw_invalidate_ptr invalidate;
-    vga_hw_screen_dump_ptr screen_dump;
-    vga_hw_text_update_ptr text_update;
+    graphic_hw_update_ptr update;
+    graphic_hw_invalidate_ptr invalidate;
+    graphic_hw_screen_dump_ptr screen_dump;
+    graphic_hw_text_update_ptr text_update;
     bool full_update_text;
     bool full_update_gfx;
     /* hardware mouse cursor support */
diff --git a/include/ui/console.h b/include/ui/console.h
index 3725dae..9c585c0 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -278,21 +278,21 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
     *dest = ch;
 }
 
-typedef void (*vga_hw_update_ptr)(void *);
-typedef void (*vga_hw_invalidate_ptr)(void *);
-typedef void (*vga_hw_screen_dump_ptr)(void *, const char *, bool cswitch,
+typedef void (*graphic_hw_update_ptr)(void *);
+typedef void (*graphic_hw_invalidate_ptr)(void *);
+typedef void (*graphic_hw_screen_dump_ptr)(void *, const char *, bool cswitch,
                                        Error **errp);
-typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *);
+typedef void (*graphic_hw_text_update_ptr)(void *, console_ch_t *);
 
-QemuConsole *graphic_console_init(vga_hw_update_ptr update,
-                                  vga_hw_invalidate_ptr invalidate,
-                                  vga_hw_screen_dump_ptr screen_dump,
-                                  vga_hw_text_update_ptr text_update,
+QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
+                                  graphic_hw_invalidate_ptr invalidate,
+                                  graphic_hw_screen_dump_ptr screen_dump,
+                                  graphic_hw_text_update_ptr text_update,
                                   void *opaque);
 
-void vga_hw_update(void);
-void vga_hw_invalidate(void);
-void vga_hw_text_update(console_ch_t *chardata);
+void graphic_hw_update(QemuConsole *con);
+void graphic_hw_invalidate(QemuConsole *con);
+void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata);
 
 int is_graphic_console(void);
 int is_fixedsize_console(void);
diff --git a/ui/console.c b/ui/console.c
index e100593..dccc618 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -118,10 +118,10 @@ struct QemuConsole {
     DisplayState *ds;
 
     /* Graphic console state.  */
-    vga_hw_update_ptr hw_update;
-    vga_hw_invalidate_ptr hw_invalidate;
-    vga_hw_screen_dump_ptr hw_screen_dump;
-    vga_hw_text_update_ptr hw_text_update;
+    graphic_hw_update_ptr hw_update;
+    graphic_hw_invalidate_ptr hw_invalidate;
+    graphic_hw_screen_dump_ptr hw_screen_dump;
+    graphic_hw_text_update_ptr hw_text_update;
     void *hw;
     int g_width, g_height;
 
@@ -165,16 +165,24 @@ static int nb_consoles = 0;
 
 static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
 
-void vga_hw_update(void)
+void graphic_hw_update(QemuConsole *con)
 {
-    if (active_console && active_console->hw_update)
-        active_console->hw_update(active_console->hw);
+    if (!con) {
+        con = active_console;
+    }
+    if (con && con->hw_update) {
+        con->hw_update(con->hw);
+    }
 }
 
-void vga_hw_invalidate(void)
+void graphic_hw_invalidate(QemuConsole *con)
 {
-    if (active_console && active_console->hw_invalidate)
-        active_console->hw_invalidate(active_console->hw);
+    if (!con) {
+        con = active_console;
+    }
+    if (con && con->hw_invalidate) {
+        con->hw_invalidate(con->hw);
+    }
 }
 
 void qmp_screendump(const char *filename, Error **errp)
@@ -201,10 +209,13 @@ void qmp_screendump(const char *filename, Error **errp)
     }
 }
 
-void vga_hw_text_update(console_ch_t *chardata)
+void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata)
 {
-    if (active_console && active_console->hw_text_update)
-        active_console->hw_text_update(active_console->hw, chardata);
+    if (!con) {
+        con = active_console;
+    }
+    if (con && con->hw_text_update)
+        con->hw_text_update(con->hw, chardata);
 }
 
 static void vga_fill_rect(QemuConsole *con,
@@ -932,7 +943,7 @@ void console_select(unsigned int index)
             qemu_mod_timer(s->cursor_timer,
                    qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
         }
-        vga_hw_invalidate();
+        graphic_hw_invalidate(s);
     }
 }
 
@@ -1359,10 +1370,10 @@ DisplayState *init_displaystate(void)
     return display_state;
 }
 
-QemuConsole *graphic_console_init(vga_hw_update_ptr update,
-                                  vga_hw_invalidate_ptr invalidate,
-                                  vga_hw_screen_dump_ptr screen_dump,
-                                  vga_hw_text_update_ptr text_update,
+QemuConsole *graphic_console_init(graphic_hw_update_ptr update,
+                                  graphic_hw_invalidate_ptr invalidate,
+                                  graphic_hw_screen_dump_ptr screen_dump,
+                                  graphic_hw_text_update_ptr text_update,
                                   void *opaque)
 {
     int width = 640;
@@ -1407,7 +1418,7 @@ static void text_console_update_cursor(void *opaque)
     QemuConsole *s = opaque;
 
     s->cursor_visible_phase = !s->cursor_visible_phase;
-    vga_hw_invalidate();
+    graphic_hw_invalidate(s);
     qemu_mod_timer(s->cursor_timer,
                    qemu_get_clock_ms(rt_clock) + CONSOLE_CURSOR_PERIOD / 2);
 }
diff --git a/ui/curses.c b/ui/curses.c
index ff82307..ed9e65c 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -166,11 +166,11 @@ static void curses_refresh(DisplayChangeListener *dcl)
         clear();
         refresh();
         curses_calc_pad();
-        vga_hw_invalidate();
+        graphic_hw_invalidate(NULL);
         invalidate = 0;
     }
 
-    vga_hw_text_update(screen);
+    graphic_hw_text_update(NULL, screen);
 
     nextchr = ERR;
     while (1) {
diff --git a/ui/gtk.c b/ui/gtk.c
index 1e105e2..d0e444c 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -327,7 +327,7 @@ static void gd_update(DisplayChangeListener *dcl,
 
 static void gd_refresh(DisplayChangeListener *dcl)
 {
-    vga_hw_update();
+    graphic_hw_update(NULL);
 }
 
 static void gd_switch(DisplayChangeListener *dcl,
diff --git a/ui/sdl.c b/ui/sdl.c
index 8da0534..ede31dc 100644
--- a/ui/sdl.c
+++ b/ui/sdl.c
@@ -492,8 +492,8 @@ static void toggle_full_screen(void)
             sdl_grab_end();
         }
     }
-    vga_hw_invalidate();
-    vga_hw_update();
+    graphic_hw_invalidate(NULL);
+    graphic_hw_update(NULL);
 }
 
 static void handle_keydown(SDL_Event *ev)
@@ -522,8 +522,8 @@ static void handle_keydown(SDL_Event *ev)
             if (scaling_active) {
                 scaling_active = 0;
                 sdl_switch(dcl, NULL);
-                vga_hw_invalidate();
-                vga_hw_update();
+                graphic_hw_invalidate(NULL);
+                graphic_hw_update(NULL);
             }
             gui_keysym = 1;
             break;
@@ -556,8 +556,8 @@ static void handle_keydown(SDL_Event *ev)
                     surface_width(surface);
 
                 sdl_scale(width, height);
-                vga_hw_invalidate();
-                vga_hw_update();
+                graphic_hw_invalidate(NULL);
+                graphic_hw_update(NULL);
                 gui_keysym = 1;
             }
         default:
@@ -770,7 +770,7 @@ static void sdl_refresh(DisplayChangeListener *dcl)
         sdl_update_caption();
     }
 
-    vga_hw_update();
+    graphic_hw_update(NULL);
     SDL_EnableUNICODE(!is_graphic_console());
 
     while (SDL_PollEvent(ev)) {
@@ -802,8 +802,8 @@ static void sdl_refresh(DisplayChangeListener *dcl)
             break;
         case SDL_VIDEORESIZE:
             sdl_scale(ev->resize.w, ev->resize.h);
-            vga_hw_invalidate();
-            vga_hw_update();
+            graphic_hw_invalidate(NULL);
+            graphic_hw_update(NULL);
             break;
         default:
             break;
diff --git a/ui/spice-display.c b/ui/spice-display.c
index eaab19d..2c01674 100644
--- a/ui/spice-display.c
+++ b/ui/spice-display.c
@@ -414,7 +414,7 @@ void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd)
 void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd)
 {
     dprint(3, "%s:\n", __func__);
-    vga_hw_update();
+    graphic_hw_update(ssd->con);
 
     qemu_mutex_lock(&ssd->lock);
     if (QTAILQ_EMPTY(&ssd->updates) && ssd->ds) {
diff --git a/ui/vnc.c b/ui/vnc.c
index 5ddb696..bc787cc 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1956,8 +1956,8 @@ static void set_pixel_format(VncState *vs,
 
     set_pixel_conversion(vs);
 
-    vga_hw_invalidate();
-    vga_hw_update();
+    graphic_hw_invalidate(NULL);
+    graphic_hw_update(NULL);
 }
 
 static void pixel_format_message (VncState *vs) {
@@ -2653,7 +2653,7 @@ static void vnc_refresh(void *opaque)
     VncState *vs, *vn;
     int has_dirty, rects = 0;
 
-    vga_hw_update();
+    graphic_hw_update(NULL);
 
     if (vnc_trylock_display(vd)) {
         vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
@@ -2692,7 +2692,7 @@ static void vnc_init_timer(VncDisplay *vd)
     vd->timer_interval = VNC_REFRESH_INTERVAL_BASE;
     if (vd->timer == NULL && !QTAILQ_EMPTY(&vd->clients)) {
         vd->timer = qemu_new_timer_ms(rt_clock, vnc_refresh, vd);
-        vga_hw_update();
+        graphic_hw_update(NULL);
         vnc_refresh(vd);
     }
 }
@@ -2775,7 +2775,7 @@ void vnc_init_state(VncState *vs)
 
     QTAILQ_INSERT_HEAD(&vd->clients, vs, next);
 
-    vga_hw_update();
+    graphic_hw_update(NULL);
 
     vnc_write(vs, "RFB 003.008\n", 12);
     vnc_flush(vs);
@@ -2800,7 +2800,7 @@ static void vnc_listen_read(void *opaque, bool websocket)
     int csock;
 
     /* Catch-up */
-    vga_hw_update();
+    graphic_hw_update(NULL);
 #ifdef CONFIG_VNC_WS
     if (websocket) {
         csock = qemu_accept(vs->lwebsock, (struct sockaddr *)&addr, &addrlen);
commit 64840c66b702cc4c809c72d8ad5d26861dd7fd8d
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Mar 7 17:08:29 2013 +0100

    console: displaystate init revamp
    
    We have only one DisplayState, so there is no need for the "next"
    linking, rip it.  Also consolidate all displaystate initialization
    into init_displaystate().  This function is called by vl.c after
    creating the devices (and thus all QemuConsoles) and before
    initializing DisplayChangeListensers (aka gtk/sdl/vnc/spice ui).
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/console.h b/include/ui/console.h
index a234c72..3725dae 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -189,12 +189,9 @@ struct DisplayState {
     bool have_text;
 
     QLIST_HEAD(, DisplayChangeListener) listeners;
-
-    struct DisplayState *next;
 };
 
-void register_displaystate(DisplayState *ds);
-DisplayState *get_displaystate(void);
+DisplayState *init_displaystate(void);
 DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
                                                 int linesize, uint8_t *data,
                                                 bool byteswap);
diff --git a/ui/console.c b/ui/console.c
index 3834e39..e100593 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -163,6 +163,8 @@ static QemuConsole *active_console;
 static QemuConsole *consoles[MAX_CONSOLES];
 static int nb_consoles = 0;
 
+static void text_console_do_init(CharDriverState *chr, DisplayState *ds);
+
 void vga_hw_update(void)
 {
     if (active_console && active_console->hw_update)
@@ -1323,39 +1325,37 @@ bool dpy_cursor_define_supported(QemuConsole *con)
     return false;
 }
 
-static void dumb_display_init(void)
-{
-    DisplayState *ds = g_malloc0(sizeof(DisplayState));
-    int width = 640;
-    int height = 480;
-
-    if (is_fixedsize_console()) {
-        width = active_console->g_width;
-        height = active_console->g_height;
-    }
-    ds->surface = qemu_create_displaysurface(width, height);
-
-    register_displaystate(ds);
-}
-
 /***********************************************************/
 /* register display */
 
-void register_displaystate(DisplayState *ds)
+/* console.c internal use only */
+static DisplayState *get_alloc_displaystate(void)
 {
-    DisplayState **s;
-    s = &display_state;
-    while (*s != NULL)
-        s = &(*s)->next;
-    ds->next = NULL;
-    *s = ds;
+    if (!display_state) {
+        display_state = g_new0(DisplayState, 1);
+    }
+    return display_state;
 }
 
-DisplayState *get_displaystate(void)
+/*
+ * Called by main(), after creating QemuConsoles
+ * and before initializing ui (sdl/vnc/...).
+ */
+DisplayState *init_displaystate(void)
 {
+    int i;
+
     if (!display_state) {
-        dumb_display_init ();
+        display_state = g_new0(DisplayState, 1);
     }
+
+    for (i = 0; i < nb_consoles; i++) {
+        if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
+            consoles[i]->ds == NULL) {
+            text_console_do_init(consoles[i]->chr, display_state);
+        }
+    }
+
     return display_state;
 }
 
@@ -1365,10 +1365,12 @@ QemuConsole *graphic_console_init(vga_hw_update_ptr update,
                                   vga_hw_text_update_ptr text_update,
                                   void *opaque)
 {
+    int width = 640;
+    int height = 480;
     QemuConsole *s;
     DisplayState *ds;
 
-    ds = (DisplayState *) g_malloc0(sizeof(DisplayState));
+    ds = get_alloc_displaystate();
     trace_console_gfx_new();
     s = new_console(ds, GRAPHIC_CONSOLE);
     s->hw_update = update;
@@ -1377,9 +1379,9 @@ QemuConsole *graphic_console_init(vga_hw_update_ptr update,
     s->hw_text_update = text_update;
     s->hw = opaque;
 
-    ds->surface = qemu_create_displaysurface(640, 480);
-
-    register_displaystate(ds);
+    if (!ds->surface) {
+        ds->surface = qemu_create_displaysurface(width, height);
+    }
     return s;
 }
 
@@ -1505,6 +1507,10 @@ static CharDriverState *text_console_init(ChardevVC *vc)
     s->g_height = height;
     chr->opaque = s;
     chr->chr_set_echo = text_console_set_echo;
+
+    if (display_state) {
+        text_console_do_init(chr, display_state);
+    }
     return chr;
 }
 
@@ -1520,17 +1526,6 @@ void register_vc_handler(VcHandler *handler)
     vc_handler = handler;
 }
 
-void text_consoles_set_display(DisplayState *ds)
-{
-    int i;
-
-    for (i = 0; i < nb_consoles; i++) {
-        if (consoles[i]->console_type != GRAPHIC_CONSOLE) {
-            text_console_do_init(consoles[i]->chr, ds);
-        }
-    }
-}
-
 void qemu_console_resize(QemuConsole *s, int width, int height)
 {
     s->g_width = width;
diff --git a/vl.c b/vl.c
index 0598998..63fe9a4 100644
--- a/vl.c
+++ b/vl.c
@@ -4331,8 +4331,7 @@ int main(int argc, char **argv, char **envp)
 
     net_check_clients();
 
-    /* just use the first displaystate for the moment */
-    ds = get_displaystate();
+    ds = init_displaystate();
 
     /* init local displays */
     switch (display_type) {
@@ -4388,9 +4387,6 @@ int main(int argc, char **argv, char **envp)
     }
 #endif
 
-    /* display setup */
-    text_consoles_set_display(ds);
-
     if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
         exit(1);
     }
commit 437fe1061be3da49b0b05ed2f0c9c50e2255c3fe
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Mar 7 16:04:52 2013 +0100

    console: add trace events
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/trace-events b/trace-events
index b4d69d6..b08627b 100644
--- a/trace-events
+++ b/trace-events
@@ -962,6 +962,9 @@ dma_bdrv_cb(void *dbs, int ret) "dbs=%p ret=%d"
 dma_map_wait(void *dbs) "dbs=%p"
 
 # console.h
+console_gfx_new(void) ""
+console_txt_new(int w, int h) "%dx%d"
+console_select(int nr) "%d"
 displaysurface_create(void *display_surface, int w, int h) "surface=%p, %dx%d"
 displaysurface_create_from(void *display_surface, int w, int h, int bpp, int swap) "surface=%p, %dx%d, bpp %d, bswap %d"
 displaysurface_free(void *display_surface) "surface=%p"
diff --git a/ui/console.c b/ui/console.c
index 1935996..3834e39 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -904,6 +904,8 @@ void console_select(unsigned int index)
 
     if (index >= MAX_CONSOLES)
         return;
+
+    trace_console_select(index);
     if (active_console) {
         surface = qemu_console_surface(active_console);
         active_console->g_width = surface_width(surface);
@@ -1367,6 +1369,7 @@ QemuConsole *graphic_console_init(vga_hw_update_ptr update,
     DisplayState *ds;
 
     ds = (DisplayState *) g_malloc0(sizeof(DisplayState));
+    trace_console_gfx_new();
     s = new_console(ds, GRAPHIC_CONSOLE);
     s->hw_update = update;
     s->hw_invalidate = invalidate;
@@ -1485,6 +1488,7 @@ static CharDriverState *text_console_init(ChardevVC *vc)
         height = vc->rows * FONT_HEIGHT;
     }
 
+    trace_console_txt_new(width, height);
     if (width == 0 || height == 0) {
         s = new_console(NULL, TEXT_CONSOLE);
     } else {
commit e27bd65a72dece59e93914d3ffa8653ce0c70511
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Fri Mar 8 08:43:24 2013 +0100

    console: switch color_table_rgb to pixman_color_t
    
    Now that all text console rendering uses pixman we can easily
    switch the color tables to use pixman_color_t directly.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/ui/console.c b/ui/console.c
index 584f069..1935996 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -32,9 +32,6 @@
 #define MAX_CONSOLES 12
 #define CONSOLE_CURSOR_PERIOD 500
 
-#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
-#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
-
 typedef struct TextAttributes {
     uint8_t fgcol:4;
     uint8_t bgcol:4;
@@ -210,17 +207,15 @@ void vga_hw_text_update(console_ch_t *chardata)
 
 static void vga_fill_rect(QemuConsole *con,
                           int posx, int posy, int width, int height,
-                          uint32_t color)
+                          pixman_color_t color)
 {
     DisplaySurface *surface = qemu_console_surface(con);
     pixman_rectangle16_t rect = {
         .x = posx, .y = posy, .width = width, .height = height
     };
-    pixman_color_t pcolor;
 
-    pcolor = qemu_pixman_color(&surface->pf, color);
     pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
-                                 &pcolor, 1, &rect);
+                                 &color, 1, &rect);
 }
 
 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
@@ -255,7 +250,10 @@ enum color_names {
 };
 #endif
 
-static const uint32_t color_table_rgb[2][8] = {
+#define QEMU_RGB(r, g, b)                                               \
+    { .red = r << 8, .green = g << 8, .blue = b << 8, .alpha = 0xffff }
+
+static const pixman_color_t color_table_rgb[2][8] = {
     {   /* dark */
         QEMU_RGB(0x00, 0x00, 0x00),  /* black */
         QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
@@ -316,9 +314,7 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
 {
     static pixman_image_t *glyphs[256];
     DisplaySurface *surface = qemu_console_surface(s);
-    unsigned int fgcol, bgcol;
-    pixman_image_t *ifg, *ibg;
-    pixman_color_t cfg, cbg;
+    pixman_color_t fgcol, bgcol;
 
     if (t_attrib->invers) {
         bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
@@ -327,16 +323,12 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
         fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
         bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
     }
-    cfg = qemu_pixman_color(&surface->pf, fgcol);
-    cbg = qemu_pixman_color(&surface->pf, bgcol);
-    ifg = pixman_image_create_solid_fill(&cfg);
-    ibg = pixman_image_create_solid_fill(&cbg);
 
     if (!glyphs[ch]) {
         glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
     }
     qemu_pixman_glyph_render(glyphs[ch], surface->image,
-                             &cfg, &cbg, x, y, FONT_WIDTH, FONT_HEIGHT);
+                             &fgcol, &bgcol, x, y, FONT_WIDTH, FONT_HEIGHT);
 }
 
 static void text_console_resize(QemuConsole *s)
commit 7d6ba01c3741bc32ae252bf64a5fd3f930c2df4f
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 6 15:44:10 2013 +0100

    console: use pixman for font rendering
    
    Zap homegrown font rendering code, use pixman calls instead.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/ui/console.c b/ui/console.c
index be7f4f1..584f069 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -242,45 +242,6 @@ static void vga_bitblt(QemuConsole *con,
 
 #include "vgafont.h"
 
-#define cbswap_32(__x) \
-((uint32_t)( \
-		(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
-		(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
-		(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
-		(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
-
-#ifdef HOST_WORDS_BIGENDIAN
-#define PAT(x) x
-#else
-#define PAT(x) cbswap_32(x)
-#endif
-
-static const uint32_t dmask16[16] = {
-    PAT(0x00000000),
-    PAT(0x000000ff),
-    PAT(0x0000ff00),
-    PAT(0x0000ffff),
-    PAT(0x00ff0000),
-    PAT(0x00ff00ff),
-    PAT(0x00ffff00),
-    PAT(0x00ffffff),
-    PAT(0xff000000),
-    PAT(0xff0000ff),
-    PAT(0xff00ff00),
-    PAT(0xff00ffff),
-    PAT(0xffff0000),
-    PAT(0xffff00ff),
-    PAT(0xffffff00),
-    PAT(0xffffffff),
-};
-
-static const uint32_t dmask4[4] = {
-    PAT(0x00000000),
-    PAT(0x0000ffff),
-    PAT(0xffff0000),
-    PAT(0xffffffff),
-};
-
 #ifndef CONFIG_CURSES
 enum color_names {
     COLOR_BLACK   = 0,
@@ -353,17 +314,11 @@ static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
 static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
                           TextAttributes *t_attrib)
 {
+    static pixman_image_t *glyphs[256];
     DisplaySurface *surface = qemu_console_surface(s);
-    uint8_t *d;
-    const uint8_t *font_ptr;
-    unsigned int font_data, linesize, xorcol, bpp;
-    int i;
     unsigned int fgcol, bgcol;
-
-#ifdef DEBUG_CONSOLE
-    printf("x: %2i y: %2i", x, y);
-    console_print_text_attributes(t_attrib, ch);
-#endif
+    pixman_image_t *ifg, *ibg;
+    pixman_color_t cfg, cbg;
 
     if (t_attrib->invers) {
         bgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
@@ -372,59 +327,16 @@ static void vga_putcharxy(QemuConsole *s, int x, int y, int ch,
         fgcol = color_table_rgb[t_attrib->bold][t_attrib->fgcol];
         bgcol = color_table_rgb[t_attrib->bold][t_attrib->bgcol];
     }
+    cfg = qemu_pixman_color(&surface->pf, fgcol);
+    cbg = qemu_pixman_color(&surface->pf, bgcol);
+    ifg = pixman_image_create_solid_fill(&cfg);
+    ibg = pixman_image_create_solid_fill(&cbg);
 
-    bpp = surface_bytes_per_pixel(surface);
-    d = surface_data(surface) +
-        surface_stride(surface) * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
-    linesize = surface_stride(surface);
-    font_ptr = vgafont16 + FONT_HEIGHT * ch;
-    xorcol = bgcol ^ fgcol;
-    switch (surface_bits_per_pixel(surface)) {
-    case 8:
-        for(i = 0; i < FONT_HEIGHT; i++) {
-            font_data = *font_ptr++;
-            if (t_attrib->uline
-                && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
-                font_data = 0xFF;
-            }
-            ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
-            ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
-            d += linesize;
-        }
-        break;
-    case 16:
-    case 15:
-        for(i = 0; i < FONT_HEIGHT; i++) {
-            font_data = *font_ptr++;
-            if (t_attrib->uline
-                && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
-                font_data = 0xFF;
-            }
-            ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
-            ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
-            ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
-            ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
-            d += linesize;
-        }
-        break;
-    case 32:
-        for(i = 0; i < FONT_HEIGHT; i++) {
-            font_data = *font_ptr++;
-            if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
-                font_data = 0xFF;
-            }
-            ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
-            ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
-            ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
-            ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
-            ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
-            ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
-            ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
-            ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
-            d += linesize;
-        }
-        break;
+    if (!glyphs[ch]) {
+        glyphs[ch] = qemu_pixman_glyph_from_vgafont(FONT_HEIGHT, vgafont16, ch);
     }
+    qemu_pixman_glyph_render(glyphs[ch], surface->image,
+                             &cfg, &cbg, x, y, FONT_WIDTH, FONT_HEIGHT);
 }
 
 static void text_console_resize(QemuConsole *s)
commit 68db6dc5310df9bb0d8d4ed8f5138b0c5c6415be
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 6 15:43:23 2013 +0100

    console: use pixman for fill+blit
    
    Zap homegrown pixel shuffeling code, use pixman calls instead.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/ui/console.c b/ui/console.c
index 0ed4211..be7f4f1 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -213,36 +213,14 @@ static void vga_fill_rect(QemuConsole *con,
                           uint32_t color)
 {
     DisplaySurface *surface = qemu_console_surface(con);
-    uint8_t *d, *d1;
-    int x, y, bpp;
+    pixman_rectangle16_t rect = {
+        .x = posx, .y = posy, .width = width, .height = height
+    };
+    pixman_color_t pcolor;
 
-    bpp = surface_bytes_per_pixel(surface);
-    d1 = surface_data(surface) +
-        surface_stride(surface) * posy + bpp * posx;
-    for (y = 0; y < height; y++) {
-        d = d1;
-        switch(bpp) {
-        case 1:
-            for (x = 0; x < width; x++) {
-                *((uint8_t *)d) = color;
-                d++;
-            }
-            break;
-        case 2:
-            for (x = 0; x < width; x++) {
-                *((uint16_t *)d) = color;
-                d += 2;
-            }
-            break;
-        case 4:
-            for (x = 0; x < width; x++) {
-                *((uint32_t *)d) = color;
-                d += 4;
-            }
-            break;
-        }
-        d1 += surface_stride(surface);
-    }
+    pcolor = qemu_pixman_color(&surface->pf, color);
+    pixman_image_fill_rectangles(PIXMAN_OP_SRC, surface->image,
+                                 &pcolor, 1, &rect);
 }
 
 /* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
@@ -250,33 +228,10 @@ static void vga_bitblt(QemuConsole *con,
                        int xs, int ys, int xd, int yd, int w, int h)
 {
     DisplaySurface *surface = qemu_console_surface(con);
-    const uint8_t *s;
-    uint8_t *d;
-    int wb, y, bpp;
 
-    bpp = surface_bytes_per_pixel(surface);
-    wb = w * bpp;
-    if (yd <= ys) {
-        s = surface_data(surface) +
-            surface_stride(surface) * ys + bpp * xs;
-        d = surface_data(surface) +
-            surface_stride(surface) * yd + bpp * xd;
-        for (y = 0; y < h; y++) {
-            memmove(d, s, wb);
-            d += surface_stride(surface);
-            s += surface_stride(surface);
-        }
-    } else {
-        s = surface_data(surface) +
-            surface_stride(surface) * (ys + h - 1) + bpp * xs;
-        d = surface_data(surface) +
-            surface_stride(surface) * (yd + h - 1) + bpp * xd;
-       for (y = 0; y < h; y++) {
-            memmove(d, s, wb);
-            d -= surface_stride(surface);
-            s -= surface_stride(surface);
-        }
-    }
+    pixman_image_composite(PIXMAN_OP_SRC,
+                           surface->image, NULL, surface->image,
+                           xs, ys, 0, 0, xd, yd, w, h);
 }
 
 /***********************************************************/
commit b762795257353760e8c69e144188ef7ab2b84c37
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Thu Mar 7 15:23:48 2013 +0100

    pixman: render vgafont glyphs into pixman images
    
    Add helper functions to create pixman mask images for glyphs
    and to render these glyphs into a pixman image.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index b0f09b5..f012ec5 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -44,5 +44,12 @@ pixman_image_t *qemu_pixman_mirror_create(pixman_format_code_t format,
 void qemu_pixman_image_unref(pixman_image_t *image);
 
 pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color);
+pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
+                                               unsigned int ch);
+void qemu_pixman_glyph_render(pixman_image_t *glyph,
+                              pixman_image_t *surface,
+                              pixman_color_t *fgcol,
+                              pixman_color_t *bgcol,
+                              int x, int y, int cw, int ch);
 
 #endif /* QEMU_PIXMAN_H */
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index be551e0..254bd8c 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -90,3 +90,46 @@ pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color)
     c.alpha = ((color & pf->amask) >> pf->ashift) << (16 - pf->abits);
     return c;
 }
+
+pixman_image_t *qemu_pixman_glyph_from_vgafont(int height, const uint8_t *font,
+                                               unsigned int ch)
+{
+    pixman_image_t *glyph;
+    uint8_t *data;
+    bool bit;
+    int x, y;
+
+    glyph = pixman_image_create_bits(PIXMAN_a8, 8, height,
+                                     NULL, 0);
+    data = (uint8_t *)pixman_image_get_data(glyph);
+
+    font += height * ch;
+    for (y = 0; y < height; y++, font++) {
+        for (x = 0; x < 8; x++, data++) {
+            bit = (*font) & (1 << (7-x));
+            *data = bit ? 0xff : 0x00;
+        }
+    }
+    return glyph;
+}
+
+void qemu_pixman_glyph_render(pixman_image_t *glyph,
+                              pixman_image_t *surface,
+                              pixman_color_t *fgcol,
+                              pixman_color_t *bgcol,
+                              int x, int y, int cw, int ch)
+{
+    pixman_image_t *ifg = pixman_image_create_solid_fill(fgcol);
+    pixman_image_t *ibg = pixman_image_create_solid_fill(bgcol);
+
+    pixman_image_composite(PIXMAN_OP_SRC, ibg, NULL, surface,
+                           0, 0, 0, 0,
+                           cw * x, ch * y,
+                           cw, ch);
+    pixman_image_composite(PIXMAN_OP_OVER, ifg, glyph, surface,
+                           0, 0, 0, 0,
+                           cw * x, ch * y,
+                           cw, ch);
+    pixman_image_unref(ifg);
+    pixman_image_unref(ibg);
+}
commit 867c538f988d326f6b447acde867e5de5a5014a0
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Wed Mar 6 14:14:17 2013 +0100

    pixman: add qemu_pixman_color()
    
    Helper function to map qemu colors (32bit integer + matching PixelFormat)
    into pixman_color_t.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h
index b032f52..b0f09b5 100644
--- a/include/ui/qemu-pixman.h
+++ b/include/ui/qemu-pixman.h
@@ -43,4 +43,6 @@ 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);
 
+pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color);
+
 #endif /* QEMU_PIXMAN_H */
diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c
index 6dcbe90..be551e0 100644
--- a/ui/qemu-pixman.c
+++ b/ui/qemu-pixman.c
@@ -79,3 +79,14 @@ void qemu_pixman_image_unref(pixman_image_t *image)
     }
     pixman_image_unref(image);
 }
+
+pixman_color_t qemu_pixman_color(PixelFormat *pf, uint32_t color)
+{
+    pixman_color_t c;
+
+    c.red   = ((color & pf->rmask) >> pf->rshift) << (16 - pf->rbits);
+    c.green = ((color & pf->gmask) >> pf->gshift) << (16 - pf->gbits);
+    c.blue  = ((color & pf->bmask) >> pf->bshift) << (16 - pf->bbits);
+    c.alpha = ((color & pf->amask) >> pf->ashift) << (16 - pf->abits);
+    return c;
+}
commit eb2f9b024d68884a3b25e63e4dbf90b67f8da236
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Mon Mar 25 11:44:21 2013 +0100

    hw/vmware_vga.c: various vmware vga fixes.
    
    Hardcode depth to 32 bpp.  It effectively was that way before because
    that is the default surface depth, this just makes it explicit in the
    code.
    
    Rename depth to new_depth to make it consistent with the new_width +
    new_height names.  In theory we can make new_depth changeable (i.e.
    allow the guest to fill in -- say -- 16 there).  In practice the guests
    don't try, the X-Server refuses to start if you ask it to use 16bpp
    depth (via DefaultDepth in the Screen section).
    
    Always return the correct rmask+gmask+bmask values for the given
    new_depth.
    
    Fix mode setting to also verify at new_depth to make sure we have a
    correct DisplaySurface, even if the current video mode happes to be
    16bpp (set by vgabios via bochs vbe interface).  While being at it
    switch over to use qemu_create_displaysurface_from, so the surface is
    backed by guest-visible video memory and we save a memcpy.
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 20e3a28..05befe4 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -39,8 +39,6 @@ struct vmsvga_state_s {
     VGACommonState vga;
 
     int invalidated;
-    int depth;
-    int bypp;
     int enable;
     int config;
     struct {
@@ -55,6 +53,7 @@ struct vmsvga_state_s {
     uint32_t *scratch;
     int new_width;
     int new_height;
+    int new_depth;
     uint32_t guest;
     uint32_t svgaid;
     int syncing;
@@ -721,6 +720,7 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
     uint32_t caps;
     struct vmsvga_state_s *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    PixelFormat pf;
     uint32_t ret;
 
     switch (s->index) {
@@ -733,11 +733,11 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
         break;
 
     case SVGA_REG_WIDTH:
-        ret = surface_width(surface);
+        ret = s->new_width ? s->new_width : surface_width(surface);
         break;
 
     case SVGA_REG_HEIGHT:
-        ret = surface_height(surface);
+        ret = s->new_height ? s->new_height : surface_height(surface);
         break;
 
     case SVGA_REG_MAX_WIDTH:
@@ -749,11 +749,12 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
         break;
 
     case SVGA_REG_DEPTH:
-        ret = s->depth;
+        ret = (s->new_depth == 32) ? 24 : s->new_depth;
         break;
 
     case SVGA_REG_BITS_PER_PIXEL:
-        ret = (s->depth + 7) & ~7;
+    case SVGA_REG_HOST_BITS_PER_PIXEL:
+        ret = s->new_depth;
         break;
 
     case SVGA_REG_PSEUDOCOLOR:
@@ -761,19 +762,26 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
         break;
 
     case SVGA_REG_RED_MASK:
-        ret = surface->pf.rmask;
+        pf = qemu_default_pixelformat(s->new_depth);
+        ret = pf.rmask;
         break;
 
     case SVGA_REG_GREEN_MASK:
-        ret = surface->pf.gmask;
+        pf = qemu_default_pixelformat(s->new_depth);
+        ret = pf.gmask;
         break;
 
     case SVGA_REG_BLUE_MASK:
-        ret = surface->pf.bmask;
+        pf = qemu_default_pixelformat(s->new_depth);
+        ret = pf.bmask;
         break;
 
     case SVGA_REG_BYTES_PER_LINE:
-        ret = s->bypp * s->new_width;
+        if (s->new_width) {
+            ret = (s->new_depth * s->new_width) / 8;
+        } else {
+            ret = surface_stride(surface);
+        }
         break;
 
     case SVGA_REG_FB_START: {
@@ -852,10 +860,6 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
         ret = s->cursor.on;
         break;
 
-    case SVGA_REG_HOST_BITS_PER_PIXEL:
-        ret = (s->depth + 7) & ~7;
-        break;
-
     case SVGA_REG_SCRATCH_SIZE:
         ret = s->scratch_size;
         break;
@@ -936,9 +940,10 @@ static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
         break;
 
     case SVGA_REG_BITS_PER_PIXEL:
-        if (value != s->depth) {
+        if (value != 32) {
             printf("%s: Bad bits per pixel: %i bits\n", __func__, value);
             s->config = 0;
+            s->invalidated = 1;
         }
         break;
 
@@ -1034,8 +1039,14 @@ static inline void vmsvga_check_size(struct vmsvga_state_s *s)
     DisplaySurface *surface = qemu_console_surface(s->vga.con);
 
     if (s->new_width != surface_width(surface) ||
-        s->new_height != surface_height(surface)) {
-        qemu_console_resize(s->vga.con, s->new_width, s->new_height);
+        s->new_height != surface_height(surface) ||
+        s->new_depth != surface_bits_per_pixel(surface)) {
+        int stride = (s->new_depth * s->new_width) / 8;
+        trace_vmware_setmode(s->new_width, s->new_height, s->new_depth);
+        surface = qemu_create_displaysurface_from(s->new_width, s->new_height,
+                                                  s->new_depth, stride,
+                                                  s->vga.vram_ptr, false);
+        dpy_gfx_replace_surface(s->vga.con, surface);
         s->invalidated = 1;
     }
 }
@@ -1069,8 +1080,6 @@ static void vmsvga_update_display(void *opaque)
     }
     if (s->invalidated || dirty) {
         s->invalidated = 0;
-        memcpy(surface_data(surface), s->vga.vram_ptr,
-               surface_stride(surface) * surface_height(surface));
         dpy_gfx_update(s->vga.con, 0, 0,
                    surface_width(surface), surface_height(surface));
     }
@@ -1162,7 +1171,7 @@ static const VMStateDescription vmstate_vmware_vga_internal = {
     .minimum_version_id_old = 0,
     .post_load = vmsvga_post_load,
     .fields      = (VMStateField[]) {
-        VMSTATE_INT32_EQUAL(depth, struct vmsvga_state_s),
+        VMSTATE_INT32_EQUAL(new_depth, struct vmsvga_state_s),
         VMSTATE_INT32(enable, struct vmsvga_state_s),
         VMSTATE_INT32(config, struct vmsvga_state_s),
         VMSTATE_INT32(cursor.id, struct vmsvga_state_s),
@@ -1198,8 +1207,6 @@ static const VMStateDescription vmstate_vmware_vga = {
 static void vmsvga_init(struct vmsvga_state_s *s,
                         MemoryRegion *address_space, MemoryRegion *io)
 {
-    DisplaySurface *surface;
-
     s->scratch_size = SVGA_SCRATCH_SIZE;
     s->scratch = g_malloc(s->scratch_size * 4);
 
@@ -1207,7 +1214,6 @@ static void vmsvga_init(struct vmsvga_state_s *s,
                                       vmsvga_invalidate_display,
                                       vmsvga_screen_dump,
                                       vmsvga_text_update, s);
-    surface = qemu_console_surface(s->vga.con);
 
     s->fifo_size = SVGA_FIFO_SIZE;
     memory_region_init_ram(&s->fifo_ram, "vmsvga.fifo", s->fifo_size);
@@ -1217,10 +1223,7 @@ static void vmsvga_init(struct vmsvga_state_s *s,
     vga_common_init(&s->vga);
     vga_init(&s->vga, address_space, io, true);
     vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga);
-    /* Save some values here in case they are changed later.
-     * This is suspicious and needs more though why it is needed. */
-    s->depth = surface_bits_per_pixel(surface);
-    s->bypp = surface_bytes_per_pixel(surface);
+    s->new_depth = 32;
 }
 
 static uint64_t vmsvga_io_read(void *opaque, hwaddr addr, unsigned size)
diff --git a/trace-events b/trace-events
index 6c795dc..b4d69d6 100644
--- a/trace-events
+++ b/trace-events
@@ -976,6 +976,7 @@ vmware_palette_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
 vmware_palette_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
 vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
 vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
+vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
 
 # savevm.c
 
commit 7a6404cd8be97d73d1fc272dda82445c19f05aa1
Author: Gerd Hoffmann <kraxel at redhat.com>
Date:   Mon Mar 25 09:53:35 2013 +0100

    hw/vmware_vga.c: add tracepoints for mmio reads+writes
    
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index 2233a8b..20e3a28 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -721,61 +721,79 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
     uint32_t caps;
     struct vmsvga_state_s *s = opaque;
     DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    uint32_t ret;
 
     switch (s->index) {
     case SVGA_REG_ID:
-        return s->svgaid;
+        ret = s->svgaid;
+        break;
 
     case SVGA_REG_ENABLE:
-        return s->enable;
+        ret = s->enable;
+        break;
 
     case SVGA_REG_WIDTH:
-        return surface_width(surface);
+        ret = surface_width(surface);
+        break;
 
     case SVGA_REG_HEIGHT:
-        return surface_height(surface);
+        ret = surface_height(surface);
+        break;
 
     case SVGA_REG_MAX_WIDTH:
-        return SVGA_MAX_WIDTH;
+        ret = SVGA_MAX_WIDTH;
+        break;
 
     case SVGA_REG_MAX_HEIGHT:
-        return SVGA_MAX_HEIGHT;
+        ret = SVGA_MAX_HEIGHT;
+        break;
 
     case SVGA_REG_DEPTH:
-        return s->depth;
+        ret = s->depth;
+        break;
 
     case SVGA_REG_BITS_PER_PIXEL:
-        return (s->depth + 7) & ~7;
+        ret = (s->depth + 7) & ~7;
+        break;
 
     case SVGA_REG_PSEUDOCOLOR:
-        return 0x0;
+        ret = 0x0;
+        break;
 
     case SVGA_REG_RED_MASK:
-        return surface->pf.rmask;
+        ret = surface->pf.rmask;
+        break;
 
     case SVGA_REG_GREEN_MASK:
-        return surface->pf.gmask;
+        ret = surface->pf.gmask;
+        break;
 
     case SVGA_REG_BLUE_MASK:
-        return surface->pf.bmask;
+        ret = surface->pf.bmask;
+        break;
 
     case SVGA_REG_BYTES_PER_LINE:
-        return s->bypp * s->new_width;
+        ret = s->bypp * s->new_width;
+        break;
 
     case SVGA_REG_FB_START: {
         struct pci_vmsvga_state_s *pci_vmsvga
             = container_of(s, struct pci_vmsvga_state_s, chip);
-        return pci_get_bar_addr(&pci_vmsvga->card, 1);
+        ret = pci_get_bar_addr(&pci_vmsvga->card, 1);
+        break;
     }
 
     case SVGA_REG_FB_OFFSET:
-        return 0x0;
+        ret = 0x0;
+        break;
 
     case SVGA_REG_VRAM_SIZE:
-        return s->vga.vram_size; /* No physical VRAM besides the framebuffer */
+        ret = s->vga.vram_size; /* No physical VRAM besides the framebuffer */
+        break;
 
     case SVGA_REG_FB_SIZE:
-        return s->vga.vram_size;
+        ret = s->vga.vram_size;
+        break;
 
     case SVGA_REG_CAPABILITIES:
         caps = SVGA_CAP_NONE;
@@ -791,66 +809,96 @@ static uint32_t vmsvga_value_read(void *opaque, uint32_t address)
                     SVGA_CAP_CURSOR_BYPASS;
         }
 #endif
-        return caps;
+        ret = caps;
+        break;
 
     case SVGA_REG_MEM_START: {
         struct pci_vmsvga_state_s *pci_vmsvga
             = container_of(s, struct pci_vmsvga_state_s, chip);
-        return pci_get_bar_addr(&pci_vmsvga->card, 2);
+        ret = pci_get_bar_addr(&pci_vmsvga->card, 2);
+        break;
     }
 
     case SVGA_REG_MEM_SIZE:
-        return s->fifo_size;
+        ret = s->fifo_size;
+        break;
 
     case SVGA_REG_CONFIG_DONE:
-        return s->config;
+        ret = s->config;
+        break;
 
     case SVGA_REG_SYNC:
     case SVGA_REG_BUSY:
-        return s->syncing;
+        ret = s->syncing;
+        break;
 
     case SVGA_REG_GUEST_ID:
-        return s->guest;
+        ret = s->guest;
+        break;
 
     case SVGA_REG_CURSOR_ID:
-        return s->cursor.id;
+        ret = s->cursor.id;
+        break;
 
     case SVGA_REG_CURSOR_X:
-        return s->cursor.x;
+        ret = s->cursor.x;
+        break;
 
     case SVGA_REG_CURSOR_Y:
-        return s->cursor.x;
+        ret = s->cursor.x;
+        break;
 
     case SVGA_REG_CURSOR_ON:
-        return s->cursor.on;
+        ret = s->cursor.on;
+        break;
 
     case SVGA_REG_HOST_BITS_PER_PIXEL:
-        return (s->depth + 7) & ~7;
+        ret = (s->depth + 7) & ~7;
+        break;
 
     case SVGA_REG_SCRATCH_SIZE:
-        return s->scratch_size;
+        ret = s->scratch_size;
+        break;
 
     case SVGA_REG_MEM_REGS:
     case SVGA_REG_NUM_DISPLAYS:
     case SVGA_REG_PITCHLOCK:
     case SVGA_PALETTE_BASE ... SVGA_PALETTE_END:
-        return 0;
+        ret = 0;
+        break;
 
     default:
         if (s->index >= SVGA_SCRATCH_BASE &&
             s->index < SVGA_SCRATCH_BASE + s->scratch_size) {
-            return s->scratch[s->index - SVGA_SCRATCH_BASE];
+            ret = s->scratch[s->index - SVGA_SCRATCH_BASE];
+            break;
         }
         printf("%s: Bad register %02x\n", __func__, s->index);
+        ret = 0;
+        break;
     }
 
-    return 0;
+    if (s->index >= SVGA_SCRATCH_BASE) {
+        trace_vmware_scratch_read(s->index, ret);
+    } else if (s->index >= SVGA_PALETTE_BASE) {
+        trace_vmware_palette_read(s->index, ret);
+    } else {
+        trace_vmware_value_read(s->index, ret);
+    }
+    return ret;
 }
 
 static void vmsvga_value_write(void *opaque, uint32_t address, uint32_t value)
 {
     struct vmsvga_state_s *s = opaque;
 
+    if (s->index >= SVGA_SCRATCH_BASE) {
+        trace_vmware_scratch_write(s->index, value);
+    } else if (s->index >= SVGA_PALETTE_BASE) {
+        trace_vmware_palette_write(s->index, value);
+    } else {
+        trace_vmware_value_write(s->index, value);
+    }
     switch (s->index) {
     case SVGA_REG_ID:
         if (value == SVGA_ID_2 || value == SVGA_ID_1 || value == SVGA_ID_0) {
diff --git a/trace-events b/trace-events
index 412f7e4..6c795dc 100644
--- a/trace-events
+++ b/trace-events
@@ -970,6 +970,12 @@ displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]"
 
 # vga.c
 ppm_save(const char *filename, void *display_surface) "%s surface=%p"
+vmware_value_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
+vmware_value_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
+vmware_palette_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
+vmware_palette_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
+vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x"
+vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
 
 # savevm.c
 
commit 17866fc888445ec7d2568645df45bb47e6be01de
Author: Igor Mitsyanko <i.mitsyanko at gmail.com>
Date:   Tue Mar 19 23:44:56 2013 +0400

    hw/vmware_vga.c: fix screen resize bug introduced after console revamp
    
    In vmsvga display update function, a pointer to DisplaySurface must be acquired
    after a call to vmsvga_check_size since this function might replace current
    DisplaySurface with a new one.
    
    Signed-off-by: Igor Mitsyanko <i.mitsyanko at gmail.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c
index bcad47a..2233a8b 100644
--- a/hw/display/vmware_vga.c
+++ b/hw/display/vmware_vga.c
@@ -995,7 +995,7 @@ static inline void vmsvga_check_size(struct vmsvga_state_s *s)
 static void vmsvga_update_display(void *opaque)
 {
     struct vmsvga_state_s *s = opaque;
-    DisplaySurface *surface = qemu_console_surface(s->vga.con);
+    DisplaySurface *surface;
     bool dirty = false;
 
     if (!s->enable) {
@@ -1004,6 +1004,7 @@ static void vmsvga_update_display(void *opaque)
     }
 
     vmsvga_check_size(s);
+    surface = qemu_console_surface(s->vga.con);
 
     vmsvga_fifo_run(s);
     vmsvga_update_rect_flush(s);
commit 522fccbe71e35efc96f66cb475f778c2ce02e9fc
Author: Igor Mitsyanko <i.mitsyanko at gmail.com>
Date:   Tue Mar 19 23:44:55 2013 +0400

    exynos4210_fimd.c: fix display resize bug introduced after console revamp
    
    In exynos4210 display update function, we were acquiring DisplaySurface
    pointer before calling screen resize function, not paying attention that resize
    procedure can replace current DisplaySurface with newly allocated one.
    Right thing to do is to initialize DisplaySurface AFTER a call to resize function.
    
    Signed-off-by: Igor Mitsyanko <i.mitsyanko at gmail.com>
    Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>

diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c
index 49cca4b..7e1cbb6 100644
--- a/hw/display/exynos4210_fimd.c
+++ b/hw/display/exynos4210_fimd.c
@@ -1242,7 +1242,7 @@ static void exynos4210_update_resolution(Exynos4210fimdState *s)
 static void exynos4210_fimd_update(void *opaque)
 {
     Exynos4210fimdState *s = (Exynos4210fimdState *)opaque;
-    DisplaySurface *surface = qemu_console_surface(s->console);
+    DisplaySurface *surface;
     Exynos4210fimdWindow *w;
     int i, line;
     hwaddr fb_line_addr, inc_size;
@@ -1255,11 +1255,12 @@ static void exynos4210_fimd_update(void *opaque)
     const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) &
             FIMD_VIDTCON2_SIZE_MASK) + 1;
 
-    if (!s || !s->console || !surface_bits_per_pixel(surface) ||
-            !s->enabled) {
+    if (!s || !s->console || !s->enabled ||
+        surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) {
         return;
     }
     exynos4210_update_resolution(s);
+    surface = qemu_console_surface(s->console);
 
     for (i = 0; i < NUM_OF_WINDOWS; i++) {
         w = &s->window[i];
commit b21bfeead284cf212d88dfa25171fee122407bc2
Author: Andreas Färber <afaerber at suse.de>
Date:   Mon Feb 18 19:59:39 2013 +0100

    target-cris: Override do_interrupt for pre-v32 CPU cores
    
    Instead of forwarding from cris_cpu_do_interrupt() to do_interruptv10(),
    override CPUClass::do_interrupt with crisv10_cpu_do_interrupt() in the
    newly introduced class_init functions.
    
    Acked-by: Edgar E. Iglesias <edgar.iglesias at gmail.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/target-cris/cpu-qom.h b/target-cris/cpu-qom.h
index deea1d8..03829bd 100644
--- a/target-cris/cpu-qom.h
+++ b/target-cris/cpu-qom.h
@@ -74,5 +74,6 @@ static inline CRISCPU *cris_env_get_cpu(CPUCRISState *env)
 #define ENV_OFFSET offsetof(CRISCPU, env)
 
 void cris_cpu_do_interrupt(CPUState *cpu);
+void crisv10_cpu_do_interrupt(CPUState *cpu);
 
 #endif
diff --git a/target-cris/cpu.c b/target-cris/cpu.c
index 95cbf39..67181e5 100644
--- a/target-cris/cpu.c
+++ b/target-cris/cpu.c
@@ -169,30 +169,38 @@ static void cris_cpu_initfn(Object *obj)
 
 static void crisv8_cpu_class_init(ObjectClass *oc, void *data)
 {
+    CPUClass *cc = CPU_CLASS(oc);
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 8;
+    cc->do_interrupt = crisv10_cpu_do_interrupt;
 }
 
 static void crisv9_cpu_class_init(ObjectClass *oc, void *data)
 {
+    CPUClass *cc = CPU_CLASS(oc);
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 9;
+    cc->do_interrupt = crisv10_cpu_do_interrupt;
 }
 
 static void crisv10_cpu_class_init(ObjectClass *oc, void *data)
 {
+    CPUClass *cc = CPU_CLASS(oc);
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 10;
+    cc->do_interrupt = crisv10_cpu_do_interrupt;
 }
 
 static void crisv11_cpu_class_init(ObjectClass *oc, void *data)
 {
+    CPUClass *cc = CPU_CLASS(oc);
     CRISCPUClass *ccc = CRIS_CPU_CLASS(oc);
 
     ccc->vr = 11;
+    cc->do_interrupt = crisv10_cpu_do_interrupt;
 }
 
 static void crisv32_cpu_class_init(ObjectClass *oc, void *data)
diff --git a/target-cris/helper.c b/target-cris/helper.c
index e1ef7bc..466cc2f 100644
--- a/target-cris/helper.c
+++ b/target-cris/helper.c
@@ -45,6 +45,11 @@ void cris_cpu_do_interrupt(CPUState *cs)
     env->pregs[PR_ERP] = env->pc;
 }
 
+void crisv10_cpu_do_interrupt(CPUState *cs)
+{
+    cris_cpu_do_interrupt(cs);
+}
+
 int cpu_cris_handle_mmu_fault(CPUCRISState * env, target_ulong address, int rw,
                               int mmu_idx)
 {
@@ -109,9 +114,10 @@ int cpu_cris_handle_mmu_fault(CPUCRISState *env, target_ulong address, int rw,
     return r;
 }
 
-static void do_interruptv10(CPUCRISState *env)
+void crisv10_cpu_do_interrupt(CPUState *cs)
 {
-    D(CPUState *cs = CPU(cris_env_get_cpu(env)));
+    CRISCPU *cpu = CRIS_CPU(cs);
+    CPUCRISState *env = &cpu->env;
     int ex_vec = -1;
 
     D_LOG("exception index=%d interrupt_req=%d\n",
@@ -171,10 +177,6 @@ void cris_cpu_do_interrupt(CPUState *cs)
     CPUCRISState *env = &cpu->env;
     int ex_vec = -1;
 
-    if (env->pregs[PR_VR] < 32) {
-        return do_interruptv10(env);
-    }
-
     D_LOG("exception index=%d interrupt_req=%d\n",
           env->exception_index,
           cs->interrupt_request);
commit a7ddba527c0b9dd32dfa7e35fa41701f990a3db4
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Thu Apr 11 16:51:56 2013 +0200

    qdev: Set device's parent before calling realize() down inheritance chain
    
    Currently device_set_realized() sets parent only after device was realized,
    but qdev_device_add() sets it before device is realized.
    Make behavior consistent and alter device_set_realized() to behave like
    qdev_device_add().
    
    It will allow to set link<> properties in realize() method in classes
    inherited from DEVICE.
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    Reviewed-by: Paolo Bonzini <pbonzini at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index bab4ed7..4eb0134 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -684,10 +684,6 @@ static void device_set_realized(Object *obj, bool value, Error **err)
     Error *local_err = NULL;
 
     if (value && !dev->realized) {
-        if (dc->realize) {
-            dc->realize(dev, &local_err);
-        }
-
         if (!obj->parent && local_err == NULL) {
             static int unattached_count;
             gchar *name = g_strdup_printf("device[%d]", unattached_count++);
@@ -698,6 +694,10 @@ static void device_set_realized(Object *obj, bool value, Error **err)
             g_free(name);
         }
 
+        if (dc->realize) {
+            dc->realize(dev, &local_err);
+        }
+
         if (qdev_get_vmsd(dev) && local_err == NULL) {
             vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev,
                                            dev->instance_id_alias,
commit 3f24a58fa7dacd82cb72393367be207b0dab16b4
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Thu Apr 11 16:51:41 2013 +0200

    cpu: Pass CPUState to *cpu_synchronize_post*()
    
    ... so it could be called without requiring CPUArchState.
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    Reviewed-by: Eduardo Habkost <ehabkost at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/cpus.c b/cpus.c
index 97e9ab4..c15ff6c 100644
--- a/cpus.c
+++ b/cpus.c
@@ -419,7 +419,7 @@ void cpu_synchronize_all_post_reset(void)
     CPUArchState *cpu;
 
     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
-        cpu_synchronize_post_reset(cpu);
+        cpu_synchronize_post_reset(ENV_GET_CPU(cpu));
     }
 }
 
@@ -428,7 +428,7 @@ void cpu_synchronize_all_post_init(void)
     CPUArchState *cpu;
 
     for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
-        cpu_synchronize_post_init(cpu);
+        cpu_synchronize_post_init(ENV_GET_CPU(cpu));
     }
 }
 
diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h
index f2d97b5..495e6f8 100644
--- a/include/sysemu/kvm.h
+++ b/include/sysemu/kvm.h
@@ -250,8 +250,8 @@ int kvm_check_extension(KVMState *s, unsigned int extension);
 uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function,
                                       uint32_t index, int reg);
 void kvm_cpu_synchronize_state(CPUArchState *env);
-void kvm_cpu_synchronize_post_reset(CPUArchState *env);
-void kvm_cpu_synchronize_post_init(CPUArchState *env);
+void kvm_cpu_synchronize_post_reset(CPUState *cpu);
+void kvm_cpu_synchronize_post_init(CPUState *cpu);
 
 /* generic hooks - to be moved/refactored once there are more users */
 
@@ -262,17 +262,17 @@ static inline void cpu_synchronize_state(CPUArchState *env)
     }
 }
 
-static inline void cpu_synchronize_post_reset(CPUArchState *env)
+static inline void cpu_synchronize_post_reset(CPUState *cpu)
 {
     if (kvm_enabled()) {
-        kvm_cpu_synchronize_post_reset(env);
+        kvm_cpu_synchronize_post_reset(cpu);
     }
 }
 
-static inline void cpu_synchronize_post_init(CPUArchState *env)
+static inline void cpu_synchronize_post_init(CPUState *cpu)
 {
     if (kvm_enabled()) {
-        kvm_cpu_synchronize_post_init(env);
+        kvm_cpu_synchronize_post_init(cpu);
     }
 }
 
diff --git a/kvm-all.c b/kvm-all.c
index 9b433d3..fc4e17c 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -1510,18 +1510,14 @@ void kvm_cpu_synchronize_state(CPUArchState *env)
     }
 }
 
-void kvm_cpu_synchronize_post_reset(CPUArchState *env)
+void kvm_cpu_synchronize_post_reset(CPUState *cpu)
 {
-    CPUState *cpu = ENV_GET_CPU(env);
-
     kvm_arch_put_registers(cpu, KVM_PUT_RESET_STATE);
     cpu->kvm_vcpu_dirty = false;
 }
 
-void kvm_cpu_synchronize_post_init(CPUArchState *env)
+void kvm_cpu_synchronize_post_init(CPUState *cpu)
 {
-    CPUState *cpu = ENV_GET_CPU(env);
-
     kvm_arch_put_registers(cpu, KVM_PUT_FULL_STATE);
     cpu->kvm_vcpu_dirty = false;
 }
diff --git a/kvm-stub.c b/kvm-stub.c
index f6137d3..723a813 100644
--- a/kvm-stub.c
+++ b/kvm-stub.c
@@ -41,11 +41,11 @@ void kvm_cpu_synchronize_state(CPUArchState *env)
 {
 }
 
-void kvm_cpu_synchronize_post_reset(CPUArchState *env)
+void kvm_cpu_synchronize_post_reset(CPUState *cpu)
 {
 }
 
-void kvm_cpu_synchronize_post_init(CPUArchState *env)
+void kvm_cpu_synchronize_post_init(CPUState *cpu)
 {
 }
 
commit 7f833247df4b68719413b5dccc5f84944f442cb3
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Thu Apr 11 16:51:40 2013 +0200

    target-i386: Split out CPU creation and features parsing
    
    Move CPU creation and features parsing into a separate cpu_x86_create()
    function, so that board would be able to set board-specific CPU
    properties before CPU is realized.
    
    Keep cpu_x86_init() for compatibility with the code that uses cpu_init()
    and doesn't need to modify CPU properties.
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    Reviewed-by: Eduardo Habkost <ehabkost at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 0a84000..e2302d8 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1563,7 +1563,7 @@ static void cpu_x86_register(X86CPU *cpu, const char *name, Error **errp)
     object_property_set_str(OBJECT(cpu), def->model_id, "model-id", errp);
 }
 
-X86CPU *cpu_x86_init(const char *cpu_model)
+X86CPU *cpu_x86_create(const char *cpu_model, Error **errp)
 {
     X86CPU *cpu = NULL;
     CPUX86State *env;
@@ -1593,13 +1593,25 @@ X86CPU *cpu_x86_init(const char *cpu_model)
         goto out;
     }
 
-    object_property_set_bool(OBJECT(cpu), true, "realized", &error);
+out:
+    error_propagate(errp, error);
+    g_strfreev(model_pieces);
+    return cpu;
+}
+
+X86CPU *cpu_x86_init(const char *cpu_model)
+{
+    Error *error = NULL;
+    X86CPU *cpu;
+
+    cpu = cpu_x86_create(cpu_model, &error);
     if (error) {
         goto out;
     }
 
+    object_property_set_bool(OBJECT(cpu), true, "realized", &error);
+
 out:
-    g_strfreev(model_pieces);
     if (error) {
         fprintf(stderr, "%s\n", error_get_pretty(error));
         error_free(error);
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 2b4e319..cf1b05c 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -896,6 +896,7 @@ typedef struct CPUX86State {
 #include "cpu-qom.h"
 
 X86CPU *cpu_x86_init(const char *cpu_model);
+X86CPU *cpu_x86_create(const char *cpu_model, Error **errp);
 int cpu_x86_exec(CPUX86State *s);
 void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf);
 void x86_cpudef_setup(void);
commit dd13e08804c8c33c6021c0e27787422534d3b321
Author: Eduardo Habkost <ehabkost at redhat.com>
Date:   Thu Apr 11 17:07:23 2013 -0300

    target-i386/cpu.c: Coding style fixes
    
     * Add braces to 'if' statements;
     * Remove last TAB character from the source.
    
    Signed-off-by: Eduardo Habkost <ehabkost at redhat.com>
    [AF: Changed whitespace]
    Reviewed-by: Igor Mammedov <imammedo at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 5d05803..0a84000 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1869,12 +1869,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
         if (env->cpuid_ext2_features & CPUID_EXT2_LM) {
             /* 64 bit processor */
 /* XXX: The physical address space is limited to 42 bits in exec.c. */
-            *eax = 0x00003028;	/* 48 bits virtual, 40 bits physical */
+            *eax = 0x00003028; /* 48 bits virtual, 40 bits physical */
         } else {
-            if (env->cpuid_features & CPUID_PSE36)
+            if (env->cpuid_features & CPUID_PSE36) {
                 *eax = 0x00000024; /* 36 bits physical */
-            else
+            } else {
                 *eax = 0x00000020; /* 32 bits physical */
+            }
         }
         *ebx = 0;
         *ecx = 0;
commit f16a69f7fce97186fd352c79201b1d0145c02d66
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Fri Apr 5 16:37:00 2013 +0200

    ioapic: Replace FROM_SYSBUS() with QOM type cast
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c
index 42c7adc..5c5bb3c 100644
--- a/hw/intc/ioapic_common.c
+++ b/hw/intc/ioapic_common.c
@@ -59,7 +59,7 @@ static int ioapic_dispatch_post_load(void *opaque, int version_id)
 
 static int ioapic_init_common(SysBusDevice *dev)
 {
-    IOAPICCommonState *s = FROM_SYSBUS(IOAPICCommonState, dev);
+    IOAPICCommonState *s = IOAPIC_COMMON(dev);
     IOAPICCommonClass *info;
     static int ioapic_no;
 
commit f1fc3e66581f638ba72d93c1a4912cfa573dd187
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Fri Apr 5 16:36:59 2013 +0200

    kvmvapic: Replace FROM_SYSBUS() with QOM type cast
    
    ... and define type name and type cast macro for kvmvapic according
    to accepted convention.
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c
index ed9b448..3a10c07 100644
--- a/hw/i386/kvmvapic.c
+++ b/hw/i386/kvmvapic.c
@@ -60,6 +60,9 @@ typedef struct VAPICROMState {
     bool rom_mapped_writable;
 } VAPICROMState;
 
+#define TYPE_VAPIC "kvmvapic"
+#define VAPIC(obj) OBJECT_CHECK(VAPICROMState, (obj), TYPE_VAPIC)
+
 #define TPR_INSTR_ABS_MODRM             0x1
 #define TPR_INSTR_MATCH_MODRM_REG       0x2
 
@@ -690,7 +693,7 @@ static const MemoryRegionOps vapic_ops = {
 
 static int vapic_init(SysBusDevice *dev)
 {
-    VAPICROMState *s = FROM_SYSBUS(VAPICROMState, dev);
+    VAPICROMState *s = VAPIC(dev);
 
     memory_region_init_io(&s->io, &vapic_ops, s, "kvmvapic", 2);
     sysbus_add_io(dev, VAPIC_IO_PORT, &s->io);
@@ -806,7 +809,7 @@ static void vapic_class_init(ObjectClass *klass, void *data)
 }
 
 static const TypeInfo vapic_type = {
-    .name          = "kvmvapic",
+    .name          = TYPE_VAPIC,
     .parent        = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(VAPICROMState),
     .class_init    = vapic_class_init,
commit d3c64d6a1874f94246af91963927fb4d924332f1
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Fri Apr 5 16:36:54 2013 +0200

    target-i386: Split APIC creation from initialization in x86_cpu_realizefn()
    
    When APIC is hotplugged during CPU hotplug, device_set_realized()
    calls device_reset() on it. And if QEMU runs in KVM mode, following
    call chain will fail:
        apic_reset_common()
            -> kvm_apic_vapic_base_update()
                -> kvm_vcpu_ioctl(cpu->kvm_fd,...)
    due to cpu->kvm_fd not being initialized yet.
    
    cpu->kvm_fd is initialized during qemu_init_vcpu() but x86_cpu_apic_init()
    can't be moved after it because kvm_init_vcpu() -> kvm_arch_reset_vcpu()
    relies on APIC to determine if CPU is BSP for setting initial env->mp_state.
    
    So split APIC device creation from its initialization and realize APIC
    after CPU is created, when it's safe to call APIC's reset method.
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    Reviewed-by: liguang <lig.fnst at cn.fujitsu.com>
    Reviewed-by: Eduardo Habkost <ehabkost at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 9d45f09..5d05803 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -2050,9 +2050,8 @@ static void mce_init(X86CPU *cpu)
 }
 
 #ifndef CONFIG_USER_ONLY
-static void x86_cpu_apic_init(X86CPU *cpu, Error **errp)
+static void x86_cpu_apic_create(X86CPU *cpu, Error **errp)
 {
-    static int apic_mapped;
     CPUX86State *env = &cpu->env;
     APICCommonState *apic;
     const char *apic_type = "apic";
@@ -2075,6 +2074,16 @@ static void x86_cpu_apic_init(X86CPU *cpu, Error **errp)
     /* TODO: convert to link<> */
     apic = APIC_COMMON(env->apic_state);
     apic->cpu = cpu;
+}
+
+static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
+{
+    CPUX86State *env = &cpu->env;
+    static int apic_mapped;
+
+    if (env->apic_state == NULL) {
+        return;
+    }
 
     if (qdev_init(env->apic_state)) {
         error_setg(errp, "APIC device '%s' could not be initialized",
@@ -2092,6 +2101,10 @@ static void x86_cpu_apic_init(X86CPU *cpu, Error **errp)
         apic_mapped = 1;
     }
 }
+#else
+static void x86_cpu_apic_realize(X86CPU *cpu, Error **errp)
+{
+}
 #endif
 
 static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
@@ -2142,7 +2155,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
     qemu_register_reset(x86_cpu_machine_reset_cb, cpu);
 
     if (cpu->env.cpuid_features & CPUID_APIC || smp_cpus > 1) {
-        x86_cpu_apic_init(cpu, &local_err);
+        x86_cpu_apic_create(cpu, &local_err);
         if (local_err != NULL) {
             goto out;
         }
@@ -2151,6 +2164,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
 
     mce_init(cpu);
     qemu_init_vcpu(&cpu->env);
+
+    x86_cpu_apic_realize(cpu, &local_err);
+    if (local_err != NULL) {
+        goto out;
+    }
     cpu_reset(CPU(cpu));
 
     xcc->parent_realize(dev, &local_err);
commit 4dc1f449ab22bb79ea3894bd90b154c30d73573e
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Fri Apr 5 16:36:53 2013 +0200

    target-i386: Consolidate error propagation in x86_cpu_realizefn()
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    Reviewed-by: Paolo Bonzini <pbonzini at redhat.com>
    Reviewed-by: Eduardo Habkost <ehabkost at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 0a4e5ae..9d45f09 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -2099,9 +2099,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
     X86CPU *cpu = X86_CPU(dev);
     X86CPUClass *xcc = X86_CPU_GET_CLASS(dev);
     CPUX86State *env = &cpu->env;
-#ifndef CONFIG_USER_ONLY
     Error *local_err = NULL;
-#endif
 
     if (env->cpuid_7_0_ebx_features && env->cpuid_level < 7) {
         env->cpuid_level = 7;
@@ -2131,8 +2129,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
     } else {
         if (check_cpuid && kvm_check_features_against_host(cpu)
             && enforce_cpuid) {
-            error_setg(errp, "Host's CPU doesn't support requested features");
-            return;
+            error_setg(&local_err,
+                       "Host's CPU doesn't support requested features");
+            goto out;
         }
 #ifdef CONFIG_KVM
         filter_features_for_kvm(cpu);
@@ -2145,8 +2144,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
     if (cpu->env.cpuid_features & CPUID_APIC || smp_cpus > 1) {
         x86_cpu_apic_init(cpu, &local_err);
         if (local_err != NULL) {
-            error_propagate(errp, local_err);
-            return;
+            goto out;
         }
     }
 #endif
@@ -2155,7 +2153,12 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
     qemu_init_vcpu(&cpu->env);
     cpu_reset(CPU(cpu));
 
-    xcc->parent_realize(dev, errp);
+    xcc->parent_realize(dev, &local_err);
+out:
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
 }
 
 /* Enables contiguous-apic-ID mode, for compatibility */
commit 72cc5137759ce1393c9eeac81b677947d618351d
Author: Igor Mammedov <imammedo at redhat.com>
Date:   Thu Mar 7 17:16:18 2013 +0100

    qdev: Add qdev property for bool type
    
    Signed-off-by: Igor Mammedov <imammedo at redhat.com>
    [AF: Use new qdev_prop_set_after_realize()]
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index ddde18e..ca1739e 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -120,6 +120,39 @@ PropertyInfo qdev_prop_bit = {
     .set   = set_bit,
 };
 
+/* --- bool --- */
+
+static void get_bool(Object *obj, Visitor *v, void *opaque,
+                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    bool *ptr = qdev_get_prop_ptr(dev, prop);
+
+    visit_type_bool(v, ptr, name, errp);
+}
+
+static void set_bool(Object *obj, Visitor *v, void *opaque,
+                     const char *name, Error **errp)
+{
+    DeviceState *dev = DEVICE(obj);
+    Property *prop = opaque;
+    bool *ptr = qdev_get_prop_ptr(dev, prop);
+
+    if (dev->realized) {
+        qdev_prop_set_after_realize(dev, name, errp);
+        return;
+    }
+
+    visit_type_bool(v, ptr, name, errp);
+}
+
+PropertyInfo qdev_prop_bool = {
+    .name  = "boolean",
+    .get   = get_bool,
+    .set   = set_bool,
+};
+
 /* --- 8bit integer --- */
 
 static void get_uint8(Object *obj, Visitor *v, void *opaque,
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index a379339..25dd1bb 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -6,6 +6,7 @@
 /*** qdev-properties.c ***/
 
 extern PropertyInfo qdev_prop_bit;
+extern PropertyInfo qdev_prop_bool;
 extern PropertyInfo qdev_prop_uint8;
 extern PropertyInfo qdev_prop_uint16;
 extern PropertyInfo qdev_prop_uint32;
@@ -52,6 +53,15 @@ extern PropertyInfo qdev_prop_arraylen;
         .defval    = (bool)_defval,                              \
         }
 
+#define DEFINE_PROP_BOOL(_name, _state, _field, _defval) {       \
+        .name      = (_name),                                    \
+        .info      = &(qdev_prop_bool),                          \
+        .offset    = offsetof(_state, _field)                    \
+            + type_check(bool, typeof_field(_state, _field)),    \
+        .qtype     = QTYPE_QBOOL,                                \
+        .defval    = (bool)_defval,                              \
+        }
+
 #define PROP_ARRAY_LEN_PREFIX "len-"
 
 /**
commit 3af60be28c35257e3ad1fc6ef1c415b09bdc8545
Author: Jan Kiszka <jan.kiszka at siemens.com>
Date:   Wed Feb 27 10:15:51 2013 +0100

    target-i386: Improve -cpu ? features output
    
    We were missing a bunch of feature lists. Fix this by simply dumping
    the meta list feature_word_info.
    
    Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
    Reviewed-by: Igor Mammedov <imammedo at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index b8690d2..0a4e5ae 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1470,14 +1470,12 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf)
 #endif
 
     (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n");
-    listflags(buf, sizeof(buf), (uint32_t)~0, feature_name, 1);
-    (*cpu_fprintf)(f, "  %s\n", buf);
-    listflags(buf, sizeof(buf), (uint32_t)~0, ext_feature_name, 1);
-    (*cpu_fprintf)(f, "  %s\n", buf);
-    listflags(buf, sizeof(buf), (uint32_t)~0, ext2_feature_name, 1);
-    (*cpu_fprintf)(f, "  %s\n", buf);
-    listflags(buf, sizeof(buf), (uint32_t)~0, ext3_feature_name, 1);
-    (*cpu_fprintf)(f, "  %s\n", buf);
+    for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) {
+        FeatureWordInfo *fw = &feature_word_info[i];
+
+        listflags(buf, sizeof(buf), (uint32_t)~0, fw->feat_names, 1);
+        (*cpu_fprintf)(f, "  %s\n", buf);
+    }
 }
 
 CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp)
commit 21ad77892d321f15325d77f6fab700864af61f49
Author: Jan Kiszka <jan.kiszka at siemens.com>
Date:   Sun Mar 24 17:01:02 2013 +0100

    target-i386: Fix including "host" in -cpu ? output
    
    kvm_enabled() cannot be true at this point because accelerators are
    initialized much later during init. Also, hiding this makes it very hard
    to discover for users. Simply dump unconditionally if CONFIG_KVM is set.
    
    Add explanation for "host" CPU type.
    
    Signed-off-by: Jan Kiszka <jan.kiszka at siemens.com>
    Reviewed-by: Igor Mammedov <imammedo at redhat.com>
    Signed-off-by: Andreas Färber <afaerber at suse.de>

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 6dd993f..b8690d2 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1463,9 +1463,12 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf)
         snprintf(buf, sizeof(buf), "%s", def->name);
         (*cpu_fprintf)(f, "x86 %16s  %-48s\n", buf, def->model_id);
     }
-    if (kvm_enabled()) {
-        (*cpu_fprintf)(f, "x86 %16s\n", "[host]");
-    }
+#ifdef CONFIG_KVM
+    (*cpu_fprintf)(f, "x86 %16s  %-48s\n", "host",
+                   "KVM processor with all supported host features "
+                   "(only available in KVM mode)");
+#endif
+
     (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n");
     listflags(buf, sizeof(buf), (uint32_t)~0, feature_name, 1);
     (*cpu_fprintf)(f, "  %s\n", buf);


More information about the Spice-commits mailing list