Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block patches

# gpg: Signature made Mon 19 May 2014 15:21:14 BST using RSA key ID C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"

* remotes/kevin/tags/for-upstream: (22 commits)
  block: optimize zero writes with bdrv_write_zeroes
  blockdev: add a function to parse enum ids from strings
  util: add qemu_iovec_is_zero
  qcow1: Stricter backing file length check
  qcow1: Validate image size (CVE-2014-0223)
  qcow1: Validate L2 table size (CVE-2014-0222)
  qcow1: Check maximum cluster size
  qcow1: Make padding in the header explicit
  curl: Add usage documentation
  curl: Add sslverify option
  curl: Remove broken parsing of options from url
  curl: Fix build when curl_multi_socket_action isn't available
  qemu-iotests: Fix blkdebug in VM drive in 030
  qemu-iotests: Fix core dump suppression in test 039
  iotests: Add test for the JSON protocol
  block: Allow JSON filenames
  check-qdict: Add test for qdict_join()
  qdict: Add qdict_join()
  block: add test for vhdx image created by Disk2VHD
  block: vhdx - account for identical header sections
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 97c9fa1..1de05f0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -659,6 +659,12 @@
 F: hw/block/nvme*
 F: tests/nvme-test.c
 
+megasas
+M: Hannes Reinecke <hare@suse.de>
+S: Supported
+F: hw/scsi/megasas.c
+F: hw/scsi/mfi.h
+
 Xilinx EDK
 M: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
 M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
diff --git a/block/iscsi.c b/block/iscsi.c
index 52355b8..d649424 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -30,6 +30,8 @@
 #include "qemu-common.h"
 #include "qemu/config-file.h"
 #include "qemu/error-report.h"
+#include "qemu/bitops.h"
+#include "qemu/bitmap.h"
 #include "block/block_int.h"
 #include "trace.h"
 #include "block/scsi.h"
@@ -59,6 +61,8 @@
     struct scsi_inquiry_logical_block_provisioning lbp;
     struct scsi_inquiry_block_limits bl;
     unsigned char *zeroblock;
+    unsigned long *allocationmap;
+    int cluster_sectors;
 } IscsiLun;
 
 typedef struct IscsiTask {
@@ -92,6 +96,15 @@
 #define MAX_NOP_FAILURES 3
 #define ISCSI_CMD_RETRIES 5
 
+/* this threshhold is a trade-off knob to choose between
+ * the potential additional overhead of an extra GET_LBA_STATUS request
+ * vs. unnecessarily reading a lot of zero sectors over the wire.
+ * If a read request is greater or equal than ISCSI_CHECKALLOC_THRES
+ * sectors we check the allocation status of the area covered by the
+ * request first if the allocationmap indicates that the area might be
+ * unallocated. */
+#define ISCSI_CHECKALLOC_THRES 64
+
 static void
 iscsi_bh_cb(void *p)
 {
@@ -273,6 +286,32 @@
     return 1;
 }
 
+static void iscsi_allocationmap_set(IscsiLun *iscsilun, int64_t sector_num,
+                                    int nb_sectors)
+{
+    if (iscsilun->allocationmap == NULL) {
+        return;
+    }
+    bitmap_set(iscsilun->allocationmap,
+               sector_num / iscsilun->cluster_sectors,
+               DIV_ROUND_UP(nb_sectors, iscsilun->cluster_sectors));
+}
+
+static void iscsi_allocationmap_clear(IscsiLun *iscsilun, int64_t sector_num,
+                                      int nb_sectors)
+{
+    int64_t cluster_num, nb_clusters;
+    if (iscsilun->allocationmap == NULL) {
+        return;
+    }
+    cluster_num = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors);
+    nb_clusters = (sector_num + nb_sectors) / iscsilun->cluster_sectors
+                  - cluster_num;
+    if (nb_clusters > 0) {
+        bitmap_clear(iscsilun->allocationmap, cluster_num, nb_clusters);
+    }
+}
+
 static int coroutine_fn iscsi_co_writev(BlockDriverState *bs,
                                         int64_t sector_num, int nb_sectors,
                                         QEMUIOVector *iov)
@@ -336,9 +375,127 @@
         return -EIO;
     }
 
+    iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors);
+
     return 0;
 }
 
+
+static bool iscsi_allocationmap_is_allocated(IscsiLun *iscsilun,
+                                             int64_t sector_num, int nb_sectors)
+{
+    unsigned long size;
+    if (iscsilun->allocationmap == NULL) {
+        return true;
+    }
+    size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors);
+    return !(find_next_bit(iscsilun->allocationmap, size,
+                           sector_num / iscsilun->cluster_sectors) == size);
+}
+
+
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
+
+static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
+                                                  int64_t sector_num,
+                                                  int nb_sectors, int *pnum)
+{
+    IscsiLun *iscsilun = bs->opaque;
+    struct scsi_get_lba_status *lbas = NULL;
+    struct scsi_lba_status_descriptor *lbasd = NULL;
+    struct IscsiTask iTask;
+    int64_t ret;
+
+    iscsi_co_init_iscsitask(iscsilun, &iTask);
+
+    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+        ret = -EINVAL;
+        goto out;
+    }
+
+    /* default to all sectors allocated */
+    ret = BDRV_BLOCK_DATA;
+    ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
+    *pnum = nb_sectors;
+
+    /* LUN does not support logical block provisioning */
+    if (iscsilun->lbpme == 0) {
+        goto out;
+    }
+
+retry:
+    if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
+                                  sector_qemu2lun(sector_num, iscsilun),
+                                  8 + 16, iscsi_co_generic_cb,
+                                  &iTask) == NULL) {
+        ret = -ENOMEM;
+        goto out;
+    }
+
+    while (!iTask.complete) {
+        iscsi_set_events(iscsilun);
+        qemu_coroutine_yield();
+    }
+
+    if (iTask.do_retry) {
+        if (iTask.task != NULL) {
+            scsi_free_scsi_task(iTask.task);
+            iTask.task = NULL;
+        }
+        iTask.complete = 0;
+        goto retry;
+    }
+
+    if (iTask.status != SCSI_STATUS_GOOD) {
+        /* in case the get_lba_status_callout fails (i.e.
+         * because the device is busy or the cmd is not
+         * supported) we pretend all blocks are allocated
+         * for backwards compatibility */
+        goto out;
+    }
+
+    lbas = scsi_datain_unmarshall(iTask.task);
+    if (lbas == NULL) {
+        ret = -EIO;
+        goto out;
+    }
+
+    lbasd = &lbas->descriptors[0];
+
+    if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
+        ret = -EIO;
+        goto out;
+    }
+
+    *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
+
+    if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
+        lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
+        ret &= ~BDRV_BLOCK_DATA;
+        if (iscsilun->lbprz) {
+            ret |= BDRV_BLOCK_ZERO;
+        }
+    }
+
+    if (ret & BDRV_BLOCK_ZERO) {
+        iscsi_allocationmap_clear(iscsilun, sector_num, *pnum);
+    } else {
+        iscsi_allocationmap_set(iscsilun, sector_num, *pnum);
+    }
+
+    if (*pnum > nb_sectors) {
+        *pnum = nb_sectors;
+    }
+out:
+    if (iTask.task != NULL) {
+        scsi_free_scsi_task(iTask.task);
+    }
+    return ret;
+}
+
+#endif /* LIBISCSI_FEATURE_IOVECTOR */
+
+
 static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
                                        int64_t sector_num, int nb_sectors,
                                        QEMUIOVector *iov)
@@ -355,6 +512,22 @@
         return -EINVAL;
     }
 
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
+    if (iscsilun->lbprz && nb_sectors >= ISCSI_CHECKALLOC_THRES &&
+        !iscsi_allocationmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
+        int64_t ret;
+        int pnum;
+        ret = iscsi_co_get_block_status(bs, sector_num, INT_MAX, &pnum);
+        if (ret < 0) {
+            return ret;
+        }
+        if (ret & BDRV_BLOCK_ZERO && pnum >= nb_sectors) {
+            qemu_iovec_memset(iov, 0, 0x00, iov->size);
+            return 0;
+        }
+    }
+#endif
+
     lba = sector_qemu2lun(sector_num, iscsilun);
     num_sectors = sector_qemu2lun(nb_sectors, iscsilun);
 
@@ -643,101 +816,6 @@
     return len;
 }
 
-#if defined(LIBISCSI_FEATURE_IOVECTOR)
-
-static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
-                                                  int64_t sector_num,
-                                                  int nb_sectors, int *pnum)
-{
-    IscsiLun *iscsilun = bs->opaque;
-    struct scsi_get_lba_status *lbas = NULL;
-    struct scsi_lba_status_descriptor *lbasd = NULL;
-    struct IscsiTask iTask;
-    int64_t ret;
-
-    iscsi_co_init_iscsitask(iscsilun, &iTask);
-
-    if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
-        ret = -EINVAL;
-        goto out;
-    }
-
-    /* default to all sectors allocated */
-    ret = BDRV_BLOCK_DATA;
-    ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
-    *pnum = nb_sectors;
-
-    /* LUN does not support logical block provisioning */
-    if (iscsilun->lbpme == 0) {
-        goto out;
-    }
-
-retry:
-    if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
-                                  sector_qemu2lun(sector_num, iscsilun),
-                                  8 + 16, iscsi_co_generic_cb,
-                                  &iTask) == NULL) {
-        ret = -ENOMEM;
-        goto out;
-    }
-
-    while (!iTask.complete) {
-        iscsi_set_events(iscsilun);
-        qemu_coroutine_yield();
-    }
-
-    if (iTask.do_retry) {
-        if (iTask.task != NULL) {
-            scsi_free_scsi_task(iTask.task);
-            iTask.task = NULL;
-        }
-        iTask.complete = 0;
-        goto retry;
-    }
-
-    if (iTask.status != SCSI_STATUS_GOOD) {
-        /* in case the get_lba_status_callout fails (i.e.
-         * because the device is busy or the cmd is not
-         * supported) we pretend all blocks are allocated
-         * for backwards compatibility */
-        goto out;
-    }
-
-    lbas = scsi_datain_unmarshall(iTask.task);
-    if (lbas == NULL) {
-        ret = -EIO;
-        goto out;
-    }
-
-    lbasd = &lbas->descriptors[0];
-
-    if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
-        ret = -EIO;
-        goto out;
-    }
-
-    *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
-    if (*pnum > nb_sectors) {
-        *pnum = nb_sectors;
-    }
-
-    if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
-        lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
-        ret &= ~BDRV_BLOCK_DATA;
-        if (iscsilun->lbprz) {
-            ret |= BDRV_BLOCK_ZERO;
-        }
-    }
-
-out:
-    if (iTask.task != NULL) {
-        scsi_free_scsi_task(iTask.task);
-    }
-    return ret;
-}
-
-#endif /* LIBISCSI_FEATURE_IOVECTOR */
-
 static int
 coroutine_fn iscsi_co_discard(BlockDriverState *bs, int64_t sector_num,
                                    int nb_sectors)
@@ -791,6 +869,8 @@
         return -EIO;
     }
 
+    iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors);
+
     return 0;
 }
 
@@ -809,13 +889,14 @@
         return -EINVAL;
     }
 
-    if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
-        /* WRITE SAME without UNMAP is not supported by the target */
-        return -ENOTSUP;
+    if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) {
+        /* WRITE SAME with UNMAP is not supported by the target,
+         * fall back and try WRITE SAME without UNMAP */
+        flags &= ~BDRV_REQ_MAY_UNMAP;
     }
 
-    if ((flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->lbp.lbpws) {
-        /* WRITE SAME with UNMAP is not supported by the target */
+    if (!(flags & BDRV_REQ_MAY_UNMAP) && !iscsilun->has_write_same) {
+        /* WRITE SAME without UNMAP is not supported by the target */
         return -ENOTSUP;
     }
 
@@ -864,6 +945,12 @@
         return -EIO;
     }
 
+    if (flags & BDRV_REQ_MAY_UNMAP) {
+        iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors);
+    } else {
+        iscsi_allocationmap_set(iscsilun, sector_num, nb_sectors);
+    }
+
     return 0;
 }
 
@@ -1295,6 +1382,22 @@
     timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
 #endif
 
+    /* Guess the internal cluster (page) size of the iscsi target by the means
+     * of opt_unmap_gran. Transfer the unmap granularity only if it has a
+     * reasonable size */
+    if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 4 * 1024 &&
+        iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
+        iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
+                                     iscsilun->block_size) >> BDRV_SECTOR_BITS;
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
+        if (iscsilun->lbprz && !(bs->open_flags & BDRV_O_NOCACHE)) {
+            iscsilun->allocationmap =
+                bitmap_new(DIV_ROUND_UP(bs->total_sectors,
+                                        iscsilun->cluster_sectors));
+        }
+#endif
+    }
+
 out:
     qemu_opts_del(opts);
     if (initiator_name != NULL) {
@@ -1328,6 +1431,7 @@
     qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL);
     iscsi_destroy_context(iscsi);
     g_free(iscsilun->zeroblock);
+    g_free(iscsilun->allocationmap);
     memset(iscsilun, 0, sizeof(IscsiLun));
 }
 
@@ -1388,6 +1492,13 @@
         return -EINVAL;
     }
 
+    if (iscsilun->allocationmap != NULL) {
+        g_free(iscsilun->allocationmap);
+        iscsilun->allocationmap =
+            bitmap_new(DIV_ROUND_UP(bs->total_sectors,
+                                    iscsilun->cluster_sectors));
+    }
+
     return 0;
 }
 
@@ -1450,13 +1561,7 @@
     IscsiLun *iscsilun = bs->opaque;
     bdi->unallocated_blocks_are_zero = !!iscsilun->lbprz;
     bdi->can_write_zeroes_with_unmap = iscsilun->lbprz && iscsilun->lbp.lbpws;
-    /* Guess the internal cluster (page) size of the iscsi target by the means
-     * of opt_unmap_gran. Transfer the unmap granularity only if it has a
-     * reasonable size for bdi->cluster_size */
-    if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 64 * 1024 &&
-        iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
-        bdi->cluster_size = iscsilun->bl.opt_unmap_gran * iscsilun->block_size;
-    }
+    bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE;
     return 0;
 }
 
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 26312d8..dea0d50 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -48,7 +48,7 @@
  { 'include': 'path/to/file.json'}
 
 The directive is evaluated recursively, and include paths are relative to the
-file using the directive.
+file using the directive. Multiple includes of the same file are safe.
 
 
 === Complex types ===
@@ -230,14 +230,13 @@
 case we want to accept/return a list of this type with a command), and a
 command which takes that type as a parameter and returns the same type:
 
-    mdroth@illuin:~/w/qemu2.git$ cat example-schema.json
+    $ cat example-schema.json
     { 'type': 'UserDefOne',
       'data': { 'integer': 'int', 'string': 'str' } }
 
     { 'command': 'my-command',
       'data':    {'arg1': 'UserDefOne'},
       'returns': 'UserDefOne' }
-    mdroth@illuin:~/w/qemu2.git$
 
 === scripts/qapi-types.py ===
 
@@ -255,14 +254,25 @@
 
 Example:
 
-    mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-types.py \
-      --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json
-    mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.c
-    /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
+    $ python scripts/qapi-types.py --output-dir="qapi-generated" \
+    --prefix="example-" --input-file=example-schema.json
+    $ cat qapi-generated/example-qapi-types.c
+[Uninteresting stuff omitted...]
 
-    #include "qapi/qapi-dealloc-visitor.h"
-    #include "example-qapi-types.h"
-    #include "example-qapi-visit.h"
+    void qapi_free_UserDefOneList(UserDefOneList * obj)
+    {
+        QapiDeallocVisitor *md;
+        Visitor *v;
+
+        if (!obj) {
+            return;
+        }
+
+        md = qapi_dealloc_visitor_new();
+        v = qapi_dealloc_get_visitor(md);
+        visit_type_UserDefOneList(v, &obj, NULL, NULL);
+        qapi_dealloc_visitor_cleanup(md);
+    }
 
     void qapi_free_UserDefOne(UserDefOne * obj)
     {
@@ -279,32 +289,38 @@
         qapi_dealloc_visitor_cleanup(md);
     }
 
-    mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-types.h
-    /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
-    #ifndef QAPI_GENERATED_EXAMPLE_QAPI_TYPES
-    #define QAPI_GENERATED_EXAMPLE_QAPI_TYPES
+    $ cat qapi-generated/example-qapi-types.h
+[Uninteresting stuff omitted...]
 
-    #include "qapi/qapi-types-core.h"
+    #ifndef EXAMPLE_QAPI_TYPES_H
+    #define EXAMPLE_QAPI_TYPES_H
+
+[Builtin types omitted...]
 
     typedef struct UserDefOne UserDefOne;
 
     typedef struct UserDefOneList
     {
-        UserDefOne *value;
+        union {
+            UserDefOne *value;
+            uint64_t padding;
+        };
         struct UserDefOneList *next;
     } UserDefOneList;
 
+[Functions on builtin types omitted...]
+
     struct UserDefOne
     {
         int64_t integer;
         char * string;
     };
 
+    void qapi_free_UserDefOneList(UserDefOneList * obj);
     void qapi_free_UserDefOne(UserDefOne * obj);
 
     #endif
 
-
 === scripts/qapi-visit.py ===
 
 Used to generate the visitor functions used to walk through and convert
@@ -325,51 +341,78 @@
 
 Example:
 
-    mdroth@illuin:~/w/qemu2.git$ python scripts/qapi-visit.py \
-        --output-dir="qapi-generated" --prefix="example-" --input-file=example-schema.json
-    mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.c
-    /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+    $ python scripts/qapi-visit.py --output-dir="qapi-generated"
+    --prefix="example-" --input-file=example-schema.json
+    $ cat qapi-generated/example-qapi-visit.c
+[Uninteresting stuff omitted...]
 
-    #include "example-qapi-visit.h"
+    static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne ** obj, Error **errp)
+    {
+        Error *err = NULL;
+        visit_type_int(m, &(*obj)->integer, "integer", &err);
+        if (err) {
+            goto out;
+        }
+        visit_type_str(m, &(*obj)->string, "string", &err);
+        if (err) {
+            goto out;
+        }
+
+    out:
+        error_propagate(errp, err);
+    }
 
     void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp)
     {
-        visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), errp);
-        visit_type_int(m, (obj && *obj) ? &(*obj)->integer : NULL, "integer", errp);
-        visit_type_str(m, (obj && *obj) ? &(*obj)->string : NULL, "string", errp);
-        visit_end_struct(m, errp);
+        Error *err = NULL;
+
+        visit_start_struct(m, (void **)obj, "UserDefOne", name, sizeof(UserDefOne), &err);
+        if (!err) {
+            if (*obj) {
+                visit_type_UserDefOne_fields(m, obj, errp);
+            }
+            visit_end_struct(m, &err);
+        }
+        error_propagate(errp, err);
     }
 
     void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp)
     {
-        GenericList *i, **prev = (GenericList **)obj;
+        Error *err = NULL;
+        GenericList *i, **prev;
 
-        visit_start_list(m, name, errp);
-
-        for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) {
-            UserDefOneList *native_i = (UserDefOneList *)i;
-            visit_type_UserDefOne(m, &native_i->value, NULL, errp);
+        visit_start_list(m, name, &err);
+        if (err) {
+            goto out;
         }
 
-        visit_end_list(m, errp);
+        for (prev = (GenericList **)obj;
+             !err && (i = visit_next_list(m, prev, &err)) != NULL;
+             prev = &i) {
+            UserDefOneList *native_i = (UserDefOneList *)i;
+            visit_type_UserDefOne(m, &native_i->value, NULL, &err);
+        }
+
+        error_propagate(errp, err);
+        err = NULL;
+        visit_end_list(m, &err);
+    out:
+        error_propagate(errp, err);
     }
-    mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qapi-visit.h
-    /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+    $ python scripts/qapi-commands.py --output-dir="qapi-generated" \
+    --prefix="example-" --input-file=example-schema.json
+    $ cat qapi-generated/example-qapi-visit.h
+[Uninteresting stuff omitted...]
 
-    #ifndef QAPI_GENERATED_EXAMPLE_QAPI_VISIT
-    #define QAPI_GENERATED_EXAMPLE_QAPI_VISIT
+    #ifndef EXAMPLE_QAPI_VISIT_H
+    #define EXAMPLE_QAPI_VISIT_H
 
-    #include "qapi/qapi-visit-core.h"
-    #include "example-qapi-types.h"
+[Visitors for builtin types omitted...]
 
     void visit_type_UserDefOne(Visitor *m, UserDefOne ** obj, const char *name, Error **errp);
     void visit_type_UserDefOneList(Visitor *m, UserDefOneList ** obj, const char *name, Error **errp);
 
     #endif
-    mdroth@illuin:~/w/qemu2.git$
-
-(The actual structure of the visit_type_* functions is a bit more complex
-in order to propagate errors correctly and avoid leaking memory).
 
 === scripts/qapi-commands.py ===
 
@@ -390,77 +433,80 @@
 
 Example:
 
-    mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c
-    /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+    $ cat qapi-generated/example-qmp-marshal.c
+[Uninteresting stuff omitted...]
 
-    #include "qemu-objects.h"
-    #include "qapi/qmp-core.h"
-    #include "qapi/qapi-visit-core.h"
-    #include "qapi/qmp-output-visitor.h"
-    #include "qapi/qmp-input-visitor.h"
-    #include "qapi/qapi-dealloc-visitor.h"
-    #include "example-qapi-types.h"
-    #include "example-qapi-visit.h"
-
-    #include "example-qmp-commands.h"
     static void qmp_marshal_output_my_command(UserDefOne * ret_in, QObject **ret_out, Error **errp)
     {
-        QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
+        Error *local_err = NULL;
         QmpOutputVisitor *mo = qmp_output_visitor_new();
+        QapiDeallocVisitor *md;
         Visitor *v;
 
         v = qmp_output_get_visitor(mo);
-        visit_type_UserDefOne(v, &ret_in, "unused", errp);
-        v = qapi_dealloc_get_visitor(md);
-        visit_type_UserDefOne(v, &ret_in, "unused", errp);
-        qapi_dealloc_visitor_cleanup(md);
-
-
+        visit_type_UserDefOne(v, &ret_in, "unused", &local_err);
+        if (local_err) {
+            goto out;
+        }
         *ret_out = qmp_output_get_qobject(mo);
+
+    out:
+        error_propagate(errp, local_err);
+        qmp_output_visitor_cleanup(mo);
+        md = qapi_dealloc_visitor_new();
+        v = qapi_dealloc_get_visitor(md);
+        visit_type_UserDefOne(v, &ret_in, "unused", NULL);
+        qapi_dealloc_visitor_cleanup(md);
     }
 
-    static void qmp_marshal_input_my_command(QmpState *qmp__sess, QDict *args, QObject **ret, Error **errp)
+    static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp)
     {
+        Error *local_err = NULL;
         UserDefOne * retval = NULL;
-        QmpInputVisitor *mi;
+        QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
         QapiDeallocVisitor *md;
         Visitor *v;
         UserDefOne * arg1 = NULL;
 
-        mi = qmp_input_visitor_new(QOBJECT(args));
         v = qmp_input_get_visitor(mi);
-        visit_type_UserDefOne(v, &arg1, "arg1", errp);
-
-        if (error_is_set(errp)) {
+        visit_type_UserDefOne(v, &arg1, "arg1", &local_err);
+        if (local_err) {
             goto out;
         }
-        retval = qmp_my_command(arg1, errp);
-        qmp_marshal_output_my_command(retval, ret, errp);
+
+        retval = qmp_my_command(arg1, &local_err);
+        if (local_err) {
+            goto out;
+        }
+
+        qmp_marshal_output_my_command(retval, ret, &local_err);
 
     out:
+        error_propagate(errp, local_err);
+        qmp_input_visitor_cleanup(mi);
         md = qapi_dealloc_visitor_new();
         v = qapi_dealloc_get_visitor(md);
-        visit_type_UserDefOne(v, &arg1, "arg1", errp);
+        visit_type_UserDefOne(v, &arg1, "arg1", NULL);
         qapi_dealloc_visitor_cleanup(md);
         return;
     }
 
     static void qmp_init_marshal(void)
     {
-        qmp_register_command("my-command", qmp_marshal_input_my_command);
+        qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS);
     }
 
     qapi_init(qmp_init_marshal);
-    mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-commands.h
-    /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
+    $ cat qapi-generated/example-qmp-commands.h
+[Uninteresting stuff omitted...]
 
-    #ifndef QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
-    #define QAPI_GENERATED_EXAMPLE_QMP_COMMANDS
+    #ifndef EXAMPLE_QMP_COMMANDS_H
+    #define EXAMPLE_QMP_COMMANDS_H
 
     #include "example-qapi-types.h"
-    #include "error.h"
+    #include "qapi/qmp/qdict.h"
+    #include "qapi/error.h"
 
     UserDefOne * qmp_my_command(UserDefOne * arg1, Error **errp);
 
     #endif
-    mdroth@illuin:~/w/qemu2.git$
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 8971f1b..2e462c0 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -556,6 +556,7 @@
         .params     = "keys [hold_ms]",
         .help       = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)",
         .mhandler.cmd = hmp_send_key,
+        .command_completion = sendkey_completion,
     },
 
 STEXI
@@ -1233,9 +1234,10 @@
     {
         .name       = "netdev_add",
         .args_type  = "netdev:O",
-        .params     = "[user|tap|socket|hubport|netmap],id=str[,prop=value][,...]",
+        .params     = "[user|tap|socket|vde|bridge|hubport|netmap],id=str[,prop=value][,...]",
         .help       = "add host network device",
         .mhandler.cmd = hmp_netdev_add,
+        .command_completion = netdev_add_completion,
     },
 
 STEXI
@@ -1250,6 +1252,7 @@
         .params     = "id",
         .help       = "remove host network device",
         .mhandler.cmd = hmp_netdev_del,
+        .command_completion = netdev_del_completion,
     },
 
 STEXI
@@ -1339,6 +1342,7 @@
         .params     = "name on|off",
         .help       = "change the link status of a network adapter",
         .mhandler.cmd = hmp_set_link,
+        .command_completion = set_link_completion,
     },
 
 STEXI
@@ -1622,6 +1626,7 @@
         .params     = "args",
         .help       = "add chardev",
         .mhandler.cmd = hmp_chardev_add,
+        .command_completion = chardev_add_completion,
     },
 
 STEXI
@@ -1638,6 +1643,7 @@
         .params     = "id",
         .help       = "remove chardev",
         .mhandler.cmd = hmp_chardev_remove,
+        .command_completion = chardev_remove_completion,
     },
 
 STEXI
diff --git a/hmp.c b/hmp.c
index ff506f3..ccc35d4 100644
--- a/hmp.c
+++ b/hmp.c
@@ -1393,6 +1393,7 @@
 void hmp_object_add(Monitor *mon, const QDict *qdict)
 {
     Error *err = NULL;
+    Error *err_end = NULL;
     QemuOpts *opts;
     char *type = NULL;
     char *id = NULL;
@@ -1416,24 +1417,23 @@
     qdict_del(pdict, "qom-type");
     visit_type_str(opts_get_visitor(ov), &type, "qom-type", &err);
     if (err) {
-        goto out_clean;
+        goto out_end;
     }
 
     qdict_del(pdict, "id");
     visit_type_str(opts_get_visitor(ov), &id, "id", &err);
     if (err) {
-        goto out_clean;
+        goto out_end;
     }
 
     object_add(type, id, pdict, opts_get_visitor(ov), &err);
-    if (err) {
-        goto out_clean;
-    }
-    visit_end_struct(opts_get_visitor(ov), &err);
-    if (err) {
+
+out_end:
+    visit_end_struct(opts_get_visitor(ov), &err_end);
+    if (!err && err_end) {
         qmp_object_del(id, NULL);
     }
-
+    error_propagate(&err, err_end);
 out_clean:
     opts_visitor_cleanup(ov);
 
diff --git a/hmp.h b/hmp.h
index 20ef454..aba59e9 100644
--- a/hmp.h
+++ b/hmp.h
@@ -97,5 +97,11 @@
 void object_del_completion(ReadLineState *rs, int nb_args, const char *str);
 void device_add_completion(ReadLineState *rs, int nb_args, const char *str);
 void device_del_completion(ReadLineState *rs, int nb_args, const char *str);
+void sendkey_completion(ReadLineState *rs, int nb_args, const char *str);
+void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str);
+void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str);
+void set_link_completion(ReadLineState *rs, int nb_args, const char *str);
+void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str);
+void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str);
 
 #endif
diff --git a/hw/char/escc.c b/hw/char/escc.c
index 6397f6f..d9a20aa 100644
--- a/hw/char/escc.c
+++ b/hw/char/escc.c
@@ -27,6 +27,7 @@
 #include "hw/char/escc.h"
 #include "sysemu/char.h"
 #include "ui/console.h"
+#include "ui/input.h"
 #include "trace.h"
 
 /*
@@ -94,6 +95,7 @@
     ChnID chn; // this channel, A (base+4) or B (base+0)
     ChnType type;
     uint8_t rx, tx;
+    QemuInputHandlerState *hs;
 } ChannelState;
 
 #define ESCC(obj) OBJECT_CHECK(ESCCState, (obj), TYPE_ESCC)
@@ -714,71 +716,181 @@
     return &d->mmio;
 }
 
-static const uint8_t keycodes[128] = {
-    127, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 53,
-    54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 89, 76, 77, 78,
-    79, 80, 81, 82, 83, 84, 85, 86, 87, 42, 99, 88, 100, 101, 102, 103,
-    104, 105, 106, 107, 108, 109, 110, 47, 19, 121, 119, 5, 6, 8, 10, 12,
-    14, 16, 17, 18, 7, 98, 23, 68, 69, 70, 71, 91, 92, 93, 125, 112,
-    113, 114, 94, 50, 0, 0, 124, 9, 11, 0, 0, 0, 0, 0, 0, 0,
-    90, 0, 46, 22, 13, 111, 52, 20, 96, 24, 28, 74, 27, 123, 44, 66,
-    0, 45, 2, 4, 48, 0, 0, 21, 0, 0, 0, 0, 0, 120, 122, 67,
+static const uint8_t qcode_to_keycode[Q_KEY_CODE_MAX] = {
+    [Q_KEY_CODE_SHIFT]         = 99,
+    [Q_KEY_CODE_SHIFT_R]       = 110,
+    [Q_KEY_CODE_ALT]           = 19,
+    [Q_KEY_CODE_ALT_R]         = 13,
+    [Q_KEY_CODE_ALTGR]         = 13,
+    [Q_KEY_CODE_CTRL]          = 76,
+    [Q_KEY_CODE_CTRL_R]        = 76,
+    [Q_KEY_CODE_ESC]           = 29,
+    [Q_KEY_CODE_1]             = 30,
+    [Q_KEY_CODE_2]             = 31,
+    [Q_KEY_CODE_3]             = 32,
+    [Q_KEY_CODE_4]             = 33,
+    [Q_KEY_CODE_5]             = 34,
+    [Q_KEY_CODE_6]             = 35,
+    [Q_KEY_CODE_7]             = 36,
+    [Q_KEY_CODE_8]             = 37,
+    [Q_KEY_CODE_9]             = 38,
+    [Q_KEY_CODE_0]             = 39,
+    [Q_KEY_CODE_MINUS]         = 40,
+    [Q_KEY_CODE_EQUAL]         = 41,
+    [Q_KEY_CODE_BACKSPACE]     = 43,
+    [Q_KEY_CODE_TAB]           = 53,
+    [Q_KEY_CODE_Q]             = 54,
+    [Q_KEY_CODE_W]             = 55,
+    [Q_KEY_CODE_E]             = 56,
+    [Q_KEY_CODE_R]             = 57,
+    [Q_KEY_CODE_T]             = 58,
+    [Q_KEY_CODE_Y]             = 59,
+    [Q_KEY_CODE_U]             = 60,
+    [Q_KEY_CODE_I]             = 61,
+    [Q_KEY_CODE_O]             = 62,
+    [Q_KEY_CODE_P]             = 63,
+    [Q_KEY_CODE_BRACKET_LEFT]  = 64,
+    [Q_KEY_CODE_BRACKET_RIGHT] = 65,
+    [Q_KEY_CODE_RET]           = 89,
+    [Q_KEY_CODE_A]             = 77,
+    [Q_KEY_CODE_S]             = 78,
+    [Q_KEY_CODE_D]             = 79,
+    [Q_KEY_CODE_F]             = 80,
+    [Q_KEY_CODE_G]             = 81,
+    [Q_KEY_CODE_H]             = 82,
+    [Q_KEY_CODE_J]             = 83,
+    [Q_KEY_CODE_K]             = 84,
+    [Q_KEY_CODE_L]             = 85,
+    [Q_KEY_CODE_SEMICOLON]     = 86,
+    [Q_KEY_CODE_APOSTROPHE]    = 87,
+    [Q_KEY_CODE_GRAVE_ACCENT]  = 42,
+    [Q_KEY_CODE_BACKSLASH]     = 88,
+    [Q_KEY_CODE_Z]             = 100,
+    [Q_KEY_CODE_X]             = 101,
+    [Q_KEY_CODE_C]             = 102,
+    [Q_KEY_CODE_V]             = 103,
+    [Q_KEY_CODE_B]             = 104,
+    [Q_KEY_CODE_N]             = 105,
+    [Q_KEY_CODE_M]             = 106,
+    [Q_KEY_CODE_COMMA]         = 107,
+    [Q_KEY_CODE_DOT]           = 108,
+    [Q_KEY_CODE_SLASH]         = 109,
+    [Q_KEY_CODE_ASTERISK]      = 47,
+    [Q_KEY_CODE_SPC]           = 121,
+    [Q_KEY_CODE_CAPS_LOCK]     = 119,
+    [Q_KEY_CODE_F1]            = 5,
+    [Q_KEY_CODE_F2]            = 6,
+    [Q_KEY_CODE_F3]            = 8,
+    [Q_KEY_CODE_F4]            = 10,
+    [Q_KEY_CODE_F5]            = 12,
+    [Q_KEY_CODE_F6]            = 14,
+    [Q_KEY_CODE_F7]            = 16,
+    [Q_KEY_CODE_F8]            = 17,
+    [Q_KEY_CODE_F9]            = 18,
+    [Q_KEY_CODE_F10]           = 7,
+    [Q_KEY_CODE_NUM_LOCK]      = 98,
+    [Q_KEY_CODE_SCROLL_LOCK]   = 23,
+    [Q_KEY_CODE_KP_DIVIDE]     = 46,
+    [Q_KEY_CODE_KP_MULTIPLY]   = 47,
+    [Q_KEY_CODE_KP_SUBTRACT]   = 71,
+    [Q_KEY_CODE_KP_ADD]        = 125,
+    [Q_KEY_CODE_KP_ENTER]      = 90,
+    [Q_KEY_CODE_KP_DECIMAL]    = 50,
+    [Q_KEY_CODE_KP_0]          = 94,
+    [Q_KEY_CODE_KP_1]          = 112,
+    [Q_KEY_CODE_KP_2]          = 113,
+    [Q_KEY_CODE_KP_3]          = 114,
+    [Q_KEY_CODE_KP_4]          = 91,
+    [Q_KEY_CODE_KP_5]          = 92,
+    [Q_KEY_CODE_KP_6]          = 93,
+    [Q_KEY_CODE_KP_7]          = 68,
+    [Q_KEY_CODE_KP_8]          = 69,
+    [Q_KEY_CODE_KP_9]          = 70,
+    [Q_KEY_CODE_LESS]          = 124,
+    [Q_KEY_CODE_F11]           = 9,
+    [Q_KEY_CODE_F12]           = 11,
+    [Q_KEY_CODE_HOME]          = 52,
+    [Q_KEY_CODE_PGUP]          = 96,
+    [Q_KEY_CODE_PGDN]          = 123,
+    [Q_KEY_CODE_END]           = 74,
+    [Q_KEY_CODE_LEFT]          = 24,
+    [Q_KEY_CODE_UP]            = 20,
+    [Q_KEY_CODE_DOWN]          = 27,
+    [Q_KEY_CODE_RIGHT]         = 28,
+    [Q_KEY_CODE_INSERT]        = 44,
+    [Q_KEY_CODE_DELETE]        = 66,
+    [Q_KEY_CODE_STOP]          = 1,
+    [Q_KEY_CODE_AGAIN]         = 3,
+    [Q_KEY_CODE_PROPS]         = 25,
+    [Q_KEY_CODE_UNDO]          = 26,
+    [Q_KEY_CODE_FRONT]         = 49,
+    [Q_KEY_CODE_COPY]          = 51,
+    [Q_KEY_CODE_OPEN]          = 72,
+    [Q_KEY_CODE_PASTE]         = 73,
+    [Q_KEY_CODE_FIND]          = 95,
+    [Q_KEY_CODE_CUT]           = 97,
+    [Q_KEY_CODE_LF]            = 111,
+    [Q_KEY_CODE_HELP]          = 118,
+    [Q_KEY_CODE_META_L]        = 120,
+    [Q_KEY_CODE_META_R]        = 122,
+    [Q_KEY_CODE_COMPOSE]       = 67,
+    [Q_KEY_CODE_PRINT]         = 22,
+    [Q_KEY_CODE_SYSRQ]         = 21,
 };
 
-static const uint8_t e0_keycodes[128] = {
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 76, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 109, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 68, 69, 70, 0, 91, 0, 93, 0, 112,
-    113, 114, 94, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    1, 3, 25, 26, 49, 52, 72, 73, 97, 99, 111, 118, 120, 122, 67, 0,
-};
-
-static void sunkbd_event(void *opaque, int ch)
+static void sunkbd_handle_event(DeviceState *dev, QemuConsole *src,
+                                InputEvent *evt)
 {
-    ChannelState *s = opaque;
-    int release = ch & 0x80;
+    ChannelState *s = (ChannelState *)dev;
+    int qcode, keycode;
 
-    trace_escc_sunkbd_event_in(ch);
-    switch (ch) {
-    case 58: // Caps lock press
-        s->caps_lock_mode ^= 1;
-        if (s->caps_lock_mode == 2)
-            return; // Drop second press
-        break;
-    case 69: // Num lock press
-        s->num_lock_mode ^= 1;
-        if (s->num_lock_mode == 2)
-            return; // Drop second press
-        break;
-    case 186: // Caps lock release
-        s->caps_lock_mode ^= 2;
-        if (s->caps_lock_mode == 3)
-            return; // Drop first release
-        break;
-    case 197: // Num lock release
-        s->num_lock_mode ^= 2;
-        if (s->num_lock_mode == 3)
-            return; // Drop first release
-        break;
-    case 0xe0:
-        s->e0_mode = 1;
-        return;
-    default:
-        break;
+    assert(evt->kind == INPUT_EVENT_KIND_KEY);
+    qcode = qemu_input_key_value_to_qcode(evt->key->key);
+    trace_escc_sunkbd_event_in(qcode, QKeyCode_lookup[qcode],
+                               evt->key->down);
+
+    if (qcode == Q_KEY_CODE_CAPS_LOCK) {
+        if (evt->key->down) {
+            s->caps_lock_mode ^= 1;
+            if (s->caps_lock_mode == 2) {
+                return; /* Drop second press */
+            }
+        } else {
+            s->caps_lock_mode ^= 2;
+            if (s->caps_lock_mode == 3) {
+                return; /* Drop first release */
+            }
+        }
     }
-    if (s->e0_mode) {
-        s->e0_mode = 0;
-        ch = e0_keycodes[ch & 0x7f];
-    } else {
-        ch = keycodes[ch & 0x7f];
+
+    if (qcode == Q_KEY_CODE_NUM_LOCK) {
+        if (evt->key->down) {
+            s->num_lock_mode ^= 1;
+            if (s->num_lock_mode == 2) {
+                return; /* Drop second press */
+            }
+        } else {
+            s->num_lock_mode ^= 2;
+            if (s->num_lock_mode == 3) {
+                return; /* Drop first release */
+            }
+        }
     }
-    trace_escc_sunkbd_event_out(ch);
-    put_queue(s, ch | release);
+
+    keycode = qcode_to_keycode[qcode];
+    if (!evt->key->down) {
+        keycode |= 0x80;
+    }
+    trace_escc_sunkbd_event_out(keycode);
+    put_queue(s, keycode);
 }
 
+static QemuInputHandler sunkbd_handler = {
+    .name  = "sun keyboard",
+    .mask  = INPUT_EVENT_MASK_KEY,
+    .event = sunkbd_handle_event,
+};
+
 static void handle_kbd_command(ChannelState *s, int val)
 {
     trace_escc_kbd_command(val);
@@ -800,7 +912,7 @@
     case 0xf:
         clear_queue(s);
         put_queue(s, 0xfe);
-        put_queue(s, 0); // XXX, layout?
+        put_queue(s, 0x21); /*  en-us layout */
         break;
     default:
         break;
@@ -898,7 +1010,8 @@
                                      "QEMU Sun Mouse");
     }
     if (s->chn[1].type == kbd) {
-        qemu_add_kbd_event_handler(sunkbd_event, &s->chn[1]);
+        s->chn[1].hs = qemu_input_handler_register((DeviceState *)(&s->chn[1]),
+                                                   &sunkbd_handler);
     }
 
     return 0;
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 3412079..22b77df 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -24,6 +24,7 @@
 #include "hw/hw.h"
 #include "hw/input/ps2.h"
 #include "ui/console.h"
+#include "ui/input.h"
 #include "sysemu/sysemu.h"
 
 /* debug PC keyboard */
@@ -71,10 +72,12 @@
 #define MOUSE_STATUS_ENABLED    0x20
 #define MOUSE_STATUS_SCALE21    0x10
 
-#define PS2_QUEUE_SIZE 256
+#define PS2_QUEUE_SIZE 16  /* Buffer size required by PS/2 protocol */
 
 typedef struct {
-    uint8_t data[PS2_QUEUE_SIZE];
+    /* Keep the data array 256 bytes long, which compatibility
+     with older qemu versions. */
+    uint8_t data[256];
     int rptr, wptr, count;
 } PS2Queue;
 
@@ -137,7 +140,7 @@
     PS2State *s = (PS2State *)opaque;
     PS2Queue *q = &s->queue;
 
-    if (q->count >= PS2_QUEUE_SIZE)
+    if (q->count >= PS2_QUEUE_SIZE - 1)
         return;
     q->data[q->wptr] = b;
     if (++q->wptr == PS2_QUEUE_SIZE)
@@ -170,6 +173,21 @@
     ps2_queue(&s->common, keycode);
 }
 
+static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
+                               InputEvent *evt)
+{
+    PS2KbdState *s = (PS2KbdState *)dev;
+    int scancodes[3], i, count;
+
+    qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
+    count = qemu_input_key_value_to_scancode(evt->key->key,
+                                             evt->key->down,
+                                             scancodes);
+    for (i = 0; i < count; i++) {
+        ps2_put_keycode(s, scancodes[i]);
+    }
+}
+
 uint32_t ps2_read_data(void *opaque)
 {
     PS2State *s = (PS2State *)opaque;
@@ -352,31 +370,57 @@
     s->mouse_dz -= dz1;
 }
 
-static void ps2_mouse_event(void *opaque,
-                            int dx, int dy, int dz, int buttons_state)
+static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
+                            InputEvent *evt)
 {
-    PS2MouseState *s = opaque;
+    static const int bmap[INPUT_BUTTON_MAX] = {
+        [INPUT_BUTTON_LEFT]   = MOUSE_EVENT_LBUTTON,
+        [INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
+        [INPUT_BUTTON_RIGHT]  = MOUSE_EVENT_RBUTTON,
+    };
+    PS2MouseState *s = (PS2MouseState *)dev;
 
     /* check if deltas are recorded when disabled */
     if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
         return;
 
-    s->mouse_dx += dx;
-    s->mouse_dy -= dy;
-    s->mouse_dz += dz;
-    /* XXX: SDL sometimes generates nul events: we delete them */
-    if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0 &&
-        s->mouse_buttons == buttons_state)
-	return;
-    s->mouse_buttons = buttons_state;
+    switch (evt->kind) {
+    case INPUT_EVENT_KIND_REL:
+        if (evt->rel->axis == INPUT_AXIS_X) {
+            s->mouse_dx += evt->rel->value;
+        } else if (evt->rel->axis == INPUT_AXIS_Y) {
+            s->mouse_dy -= evt->rel->value;
+        }
+        break;
 
-    if (buttons_state) {
+    case INPUT_EVENT_KIND_BTN:
+        if (evt->btn->down) {
+            s->mouse_buttons |= bmap[evt->btn->button];
+            if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
+                s->mouse_dz--;
+            } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
+                s->mouse_dz++;
+            }
+        } else {
+            s->mouse_buttons &= ~bmap[evt->btn->button];
+        }
+        break;
+
+    default:
+        /* keep gcc happy */
+        break;
+    }
+}
+
+static void ps2_mouse_sync(DeviceState *dev)
+{
+    PS2MouseState *s = (PS2MouseState *)dev;
+
+    if (s->mouse_buttons) {
         qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
     }
-
-    if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
-        (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) {
-        for(;;) {
+    if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
+        while (s->common.queue.count < PS2_QUEUE_SIZE - 4) {
             /* if not remote, send event. Multiple events are sent if
                too big deltas */
             ps2_mouse_send_packet(s);
@@ -388,7 +432,9 @@
 
 void ps2_mouse_fake_event(void *opaque)
 {
-    ps2_mouse_event(opaque, 1, 0, 0, 0);
+    PS2MouseState *s = opaque;
+    s->mouse_dx++;
+    ps2_mouse_sync(opaque);
 }
 
 void ps2_write_mouse(void *opaque, int val)
@@ -528,6 +574,34 @@
     s->update_irq(s->update_arg, 0);
 }
 
+static void ps2_common_post_load(PS2State *s)
+{
+    PS2Queue *q = &s->queue;
+    int size;
+    int i;
+    int tmp_data[PS2_QUEUE_SIZE];
+
+    /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */
+    size = q->count > PS2_QUEUE_SIZE ? 0 : q->count;
+
+    /* move the queue elements to the start of data array */
+    if (size > 0) {
+        for (i = 0; i < size; i++) {
+            /* move the queue elements to the temporary buffer */
+            tmp_data[i] = q->data[q->rptr];
+            if (++q->rptr == 256) {
+                q->rptr = 0;
+            }
+        }
+        memcpy(q->data, tmp_data, size);
+    }
+    /* reset rptr/wptr/count */
+    q->rptr = 0;
+    q->wptr = size;
+    q->count = size;
+    s->update_irq(s->update_arg, q->count != 0);
+}
+
 static void ps2_kbd_reset(void *opaque)
 {
     PS2KbdState *s = (PS2KbdState *) opaque;
@@ -600,18 +674,31 @@
 static int ps2_kbd_post_load(void* opaque, int version_id)
 {
     PS2KbdState *s = (PS2KbdState*)opaque;
+    PS2State *ps2 = &s->common;
 
     if (version_id == 2)
         s->scancode_set=2;
+
+    ps2_common_post_load(ps2);
+
     return 0;
 }
 
+static void ps2_kbd_pre_save(void *opaque)
+{
+    PS2KbdState *s = (PS2KbdState *)opaque;
+    PS2State *ps2 = &s->common;
+
+    ps2_common_post_load(ps2);
+}
+
 static const VMStateDescription vmstate_ps2_keyboard = {
     .name = "ps2kbd",
     .version_id = 3,
     .minimum_version_id = 2,
     .minimum_version_id_old = 2,
     .post_load = ps2_kbd_post_load,
+    .pre_save = ps2_kbd_pre_save,
     .fields      = (VMStateField []) {
         VMSTATE_STRUCT(common, PS2KbdState, 0, vmstate_ps2_common, PS2State),
         VMSTATE_INT32(scan_enabled, PS2KbdState),
@@ -629,11 +716,31 @@
     }
 };
 
+static int ps2_mouse_post_load(void *opaque, int version_id)
+{
+    PS2MouseState *s = (PS2MouseState *)opaque;
+    PS2State *ps2 = &s->common;
+
+    ps2_common_post_load(ps2);
+
+    return 0;
+}
+
+static void ps2_mouse_pre_save(void *opaque)
+{
+    PS2MouseState *s = (PS2MouseState *)opaque;
+    PS2State *ps2 = &s->common;
+
+    ps2_common_post_load(ps2);
+}
+
 static const VMStateDescription vmstate_ps2_mouse = {
     .name = "ps2mouse",
     .version_id = 2,
     .minimum_version_id = 2,
     .minimum_version_id_old = 2,
+    .post_load = ps2_mouse_post_load,
+    .pre_save = ps2_mouse_pre_save,
     .fields      = (VMStateField []) {
         VMSTATE_STRUCT(common, PS2MouseState, 0, vmstate_ps2_common, PS2State),
         VMSTATE_UINT8(mouse_status, PS2MouseState),
@@ -650,6 +757,12 @@
     }
 };
 
+static QemuInputHandler ps2_keyboard_handler = {
+    .name  = "QEMU PS/2 Keyboard",
+    .mask  = INPUT_EVENT_MASK_KEY,
+    .event = ps2_keyboard_event,
+};
+
 void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg)
 {
     PS2KbdState *s = (PS2KbdState *)g_malloc0(sizeof(PS2KbdState));
@@ -658,11 +771,19 @@
     s->common.update_arg = update_arg;
     s->scancode_set = 2;
     vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s);
-    qemu_add_kbd_event_handler(ps2_put_keycode, s);
+    qemu_input_handler_register((DeviceState *)s,
+                                &ps2_keyboard_handler);
     qemu_register_reset(ps2_kbd_reset, s);
     return s;
 }
 
+static QemuInputHandler ps2_mouse_handler = {
+    .name  = "QEMU PS/2 Mouse",
+    .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
+    .event = ps2_mouse_event,
+    .sync  = ps2_mouse_sync,
+};
+
 void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg)
 {
     PS2MouseState *s = (PS2MouseState *)g_malloc0(sizeof(PS2MouseState));
@@ -670,7 +791,8 @@
     s->common.update_irq = update_irq;
     s->common.update_arg = update_arg;
     vmstate_register(NULL, 0, &vmstate_ps2_mouse, s);
-    qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse");
+    qemu_input_handler_register((DeviceState *)s,
+                                &ps2_mouse_handler);
     qemu_register_reset(ps2_mouse_reset, s);
     return s;
 }
diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c
index e6e1ffd..baee46f 100644
--- a/hw/scsi/megasas.c
+++ b/hw/scsi/megasas.c
@@ -21,6 +21,7 @@
 #include "hw/hw.h"
 #include "hw/pci/pci.h"
 #include "sysemu/dma.h"
+#include "hw/pci/msi.h"
 #include "hw/pci/msix.h"
 #include "qemu/iov.h"
 #include "hw/scsi/scsi.h"
@@ -43,9 +44,11 @@
 
 #define MEGASAS_FLAG_USE_JBOD      0
 #define MEGASAS_MASK_USE_JBOD      (1 << MEGASAS_FLAG_USE_JBOD)
-#define MEGASAS_FLAG_USE_MSIX      1
+#define MEGASAS_FLAG_USE_MSI       1
+#define MEGASAS_MASK_USE_MSI       (1 << MEGASAS_FLAG_USE_MSI)
+#define MEGASAS_FLAG_USE_MSIX      2
 #define MEGASAS_MASK_USE_MSIX      (1 << MEGASAS_FLAG_USE_MSIX)
-#define MEGASAS_FLAG_USE_QUEUE64   2
+#define MEGASAS_FLAG_USE_QUEUE64   3
 #define MEGASAS_MASK_USE_QUEUE64   (1 << MEGASAS_FLAG_USE_QUEUE64)
 
 static const char *mfi_frame_desc[] = {
@@ -132,6 +135,11 @@
     return s->flags & MEGASAS_MASK_USE_QUEUE64;
 }
 
+static bool megasas_use_msi(MegasasState *s)
+{
+    return s->flags & MEGASAS_MASK_USE_MSI;
+}
+
 static bool megasas_use_msix(MegasasState *s)
 {
     return s->flags & MEGASAS_MASK_USE_MSIX;
@@ -538,6 +546,9 @@
             if (msix_enabled(pci_dev)) {
                 trace_megasas_msix_raise(0);
                 msix_notify(pci_dev, 0);
+            } else if (msi_enabled(pci_dev)) {
+                trace_megasas_msi_raise(0);
+                msi_notify(pci_dev, 0);
             } else {
                 trace_megasas_irq_raise();
                 pci_irq_assert(pci_dev);
@@ -1106,6 +1117,21 @@
     return MFI_STAT_OK;
 }
 
+static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd)
+{
+    uint16_t flags;
+
+    /* mbox0 contains flags */
+    flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]);
+    trace_megasas_dcmd_ld_list_query(cmd->index, flags);
+    if (flags == MR_LD_QUERY_TYPE_ALL ||
+        flags == MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) {
+        return megasas_dcmd_ld_get_list(s, cmd);
+    }
+
+    return MFI_STAT_OK;
+}
+
 static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun,
                                       MegasasCmd *cmd)
 {
@@ -1409,6 +1435,8 @@
       megasas_dcmd_dummy },
     { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST",
       megasas_dcmd_ld_get_list},
+    { MFI_DCMD_LD_LIST_QUERY, "LD_LIST_QUERY",
+      megasas_dcmd_ld_list_query },
     { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO",
       megasas_dcmd_ld_get_info },
     { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP",
@@ -1939,12 +1967,20 @@
         break;
     case MFI_OMSK:
         s->intr_mask = val;
-        if (!megasas_intr_enabled(s) && !msix_enabled(pci_dev)) {
+        if (!megasas_intr_enabled(s) &&
+            !msi_enabled(pci_dev) &&
+            !msix_enabled(pci_dev)) {
             trace_megasas_irq_lower();
             pci_irq_deassert(pci_dev);
         }
         if (megasas_intr_enabled(s)) {
-            trace_megasas_intr_enabled();
+            if (msix_enabled(pci_dev)) {
+                trace_megasas_msix_enabled(0);
+            } else if (msi_enabled(pci_dev)) {
+                trace_megasas_msi_enabled(0);
+            } else {
+                trace_megasas_intr_enabled();
+            }
         } else {
             trace_megasas_intr_disabled();
         }
@@ -2068,6 +2104,7 @@
     .minimum_version_id_old = 0,
     .fields      = (VMStateField[]) {
         VMSTATE_PCI_DEVICE(parent_obj, MegasasState),
+        VMSTATE_MSIX(parent_obj, MegasasState),
 
         VMSTATE_INT32(fw_state, MegasasState),
         VMSTATE_INT32(intr_mask, MegasasState),
@@ -2083,9 +2120,12 @@
 {
     MegasasState *s = MEGASAS(d);
 
-#ifdef USE_MSIX
-    msix_uninit(d, &s->mmio_io);
-#endif
+    if (megasas_use_msix(s)) {
+        msix_uninit(d, &s->mmio_io, &s->mmio_io);
+    }
+    if (megasas_use_msi(s)) {
+        msi_uninit(d);
+    }
     memory_region_destroy(&s->mmio_io);
     memory_region_destroy(&s->port_io);
     memory_region_destroy(&s->queue_io);
@@ -2124,15 +2164,15 @@
     memory_region_init_io(&s->queue_io, OBJECT(s), &megasas_queue_ops, s,
                           "megasas-queue", 0x40000);
 
-#ifdef USE_MSIX
-    /* MSI-X support is currently broken */
+    if (megasas_use_msi(s) &&
+        msi_init(dev, 0x50, 1, true, false)) {
+        s->flags &= ~MEGASAS_MASK_USE_MSI;
+    }
     if (megasas_use_msix(s) &&
-        msix_init(dev, 15, &s->mmio_io, 0, 0x2000)) {
+        msix_init(dev, 15, &s->mmio_io, 0, 0x2000,
+                  &s->mmio_io, 0, 0x3800, 0x68)) {
         s->flags &= ~MEGASAS_MASK_USE_MSIX;
     }
-#else
-    s->flags &= ~MEGASAS_MASK_USE_MSIX;
-#endif
 
     bar_type = PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64;
     pci_register_bar(dev, 0, bar_type, &s->mmio_io);
@@ -2151,7 +2191,7 @@
         s->sas_addr |= PCI_FUNC(dev->devfn);
     }
     if (!s->hba_serial) {
-	s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL);
+        s->hba_serial = g_strdup(MEGASAS_HBA_SERIAL);
     }
     if (s->fw_sge >= MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE) {
         s->fw_sge = MEGASAS_MAX_SGE - MFI_PASS_FRAME_SIZE;
@@ -2164,7 +2204,6 @@
         s->fw_cmds = MEGASAS_MAX_FRAMES;
     }
     trace_megasas_init(s->fw_sge, s->fw_cmds,
-                       megasas_use_msix(s) ? "MSI-X" : "INTx",
                        megasas_is_jbod(s) ? "jbod" : "raid");
     s->fw_luns = (MFI_MAX_LD > MAX_SCSI_DEVS) ?
         MAX_SCSI_DEVS : MFI_MAX_LD;
@@ -2189,6 +2228,13 @@
     return 0;
 }
 
+static void
+megasas_write_config(PCIDevice *pci, uint32_t addr, uint32_t val, int len)
+{
+    pci_default_write_config(pci, addr, val, len);
+    msi_write_config(pci, addr, val, len);
+}
+
 static Property megasas_properties[] = {
     DEFINE_PROP_UINT32("max_sge", MegasasState, fw_sge,
                        MEGASAS_DEFAULT_SGE),
@@ -2196,10 +2242,10 @@
                        MEGASAS_DEFAULT_FRAMES),
     DEFINE_PROP_STRING("hba_serial", MegasasState, hba_serial),
     DEFINE_PROP_UINT64("sas_address", MegasasState, sas_addr, 0),
-#ifdef USE_MSIX
+    DEFINE_PROP_BIT("use_msi", MegasasState, flags,
+                    MEGASAS_FLAG_USE_MSI, false),
     DEFINE_PROP_BIT("use_msix", MegasasState, flags,
                     MEGASAS_FLAG_USE_MSIX, false),
-#endif
     DEFINE_PROP_BIT("use_jbod", MegasasState, flags,
                     MEGASAS_FLAG_USE_JBOD, false),
     DEFINE_PROP_END_OF_LIST(),
@@ -2222,6 +2268,7 @@
     dc->vmsd = &vmstate_megasas;
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
     dc->desc = "LSI MegaRAID SAS 1078";
+    pc->config_write = megasas_write_config;
 }
 
 static const TypeInfo megasas_info = {
diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h
index cd8355b..a3034f6 100644
--- a/hw/scsi/mfi.h
+++ b/hw/scsi/mfi.h
@@ -164,6 +164,7 @@
     MFI_DCMD_PD_BLINK =                 0x02070100,
     MFI_DCMD_PD_UNBLINK =               0x02070200,
     MFI_DCMD_LD_GET_LIST =              0x03010000,
+    MFI_DCMD_LD_LIST_QUERY =            0x03010100,
     MFI_DCMD_LD_GET_INFO =              0x03020000,
     MFI_DCMD_LD_GET_PROP =              0x03030000,
     MFI_DCMD_LD_SET_PROP =              0x03040000,
@@ -411,6 +412,14 @@
     MR_PD_QUERY_TYPE_EXPOSED_TO_HOST =  5, /*query for system drives */
 } mfi_pd_query_type;
 
+typedef enum {
+    MR_LD_QUERY_TYPE_ALL =              0,
+    MR_LD_QUERY_TYPE_EXPOSED_TO_HOST =  1,
+    MR_LD_QUERY_TYPE_USED_TGT_IDS =     2,
+    MR_LD_QUERY_TYPE_CLUSTER_ACCESS =   3,
+    MR_LD_QUERY_TYPE_CLUSTER_LOCALE =   4,
+} mfi_ld_query_type;
+
 /*
  * Other propertities and definitions
  */
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
index 48a28ae..4bcef55 100644
--- a/hw/scsi/scsi-disk.c
+++ b/hw/scsi/scsi-disk.c
@@ -2458,21 +2458,27 @@
     int rc;
 
     if (!s->qdev.conf.bs) {
-        error_report("scsi-block: drive property not set");
+        error_report("drive property not set");
         return -1;
     }
 
     /* check we are using a driver managing SG_IO (version 3 and after) */
-    if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
-        sg_version < 30000) {
-        error_report("scsi-block: scsi generic interface too old");
+    rc = bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version);
+    if (rc < 0) {
+        error_report("cannot get SG_IO version number: %s.  "
+                     "Is this a SCSI device?",
+                     strerror(-rc));
+        return -1;
+    }
+    if (sg_version < 30000) {
+        error_report("scsi generic interface too old");
         return -1;
     }
 
     /* get device type from INQUIRY data */
     rc = get_device_type(s);
     if (rc < 0) {
-        error_report("scsi-block: INQUIRY failed");
+        error_report("INQUIRY failed");
         return -1;
     }
 
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
index 8d92e0d..3733d2c 100644
--- a/hw/scsi/scsi-generic.c
+++ b/hw/scsi/scsi-generic.c
@@ -394,6 +394,7 @@
 
 static int scsi_generic_initfn(SCSIDevice *s)
 {
+    int rc;
     int sg_version;
     struct sg_scsi_id scsiid;
 
@@ -412,8 +413,11 @@
     }
 
     /* check we are using a driver managing SG_IO (version 3 and after */
-    if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0) {
-        error_report("scsi generic interface not supported");
+    rc = bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version);
+    if (rc < 0) {
+        error_report("cannot get SG_IO version number: %s.  "
+                     "Is this a SCSI device?",
+                     strerror(-rc));
         return -1;
     }
     if (sg_version < 30000) {
diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c
index 8509309..df54546 100644
--- a/hw/timer/mc146818rtc.c
+++ b/hw/timer/mc146818rtc.c
@@ -793,19 +793,46 @@
 static void rtc_get_date(Object *obj, Visitor *v, void *opaque,
                          const char *name, Error **errp)
 {
+    Error *err = NULL;
     RTCState *s = MC146818_RTC(obj);
     struct tm current_tm;
 
     rtc_update_time(s);
     rtc_get_time(s, &current_tm);
-    visit_start_struct(v, NULL, "struct tm", name, 0, errp);
-    visit_type_int32(v, &current_tm.tm_year, "tm_year", errp);
-    visit_type_int32(v, &current_tm.tm_mon, "tm_mon", errp);
-    visit_type_int32(v, &current_tm.tm_mday, "tm_mday", errp);
-    visit_type_int32(v, &current_tm.tm_hour, "tm_hour", errp);
-    visit_type_int32(v, &current_tm.tm_min, "tm_min", errp);
-    visit_type_int32(v, &current_tm.tm_sec, "tm_sec", errp);
+    visit_start_struct(v, NULL, "struct tm", name, 0, &err);
+    if (err) {
+        goto out;
+    }
+    visit_type_int32(v, &current_tm.tm_year, "tm_year", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_int32(v, &current_tm.tm_mon, "tm_mon", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_int32(v, &current_tm.tm_mday, "tm_mday", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_int32(v, &current_tm.tm_hour, "tm_hour", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_int32(v, &current_tm.tm_min, "tm_min", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_int32(v, &current_tm.tm_sec, "tm_sec", &err);
+    if (err) {
+        goto out_end;
+    }
+out_end:
+    error_propagate(errp, err);
+    err = NULL;
     visit_end_struct(v, errp);
+out:
+    error_propagate(errp, err);
 }
 
 static void rtc_realizefn(DeviceState *dev, Error **errp)
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 971a921..bf2b588 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -108,6 +108,7 @@
 static void balloon_stats_get_all(Object *obj, struct Visitor *v,
                                   void *opaque, const char *name, Error **errp)
 {
+    Error *err = NULL;
     VirtIOBalloon *s = opaque;
     int i;
 
@@ -116,17 +117,33 @@
         return;
     }
 
-    visit_start_struct(v, NULL, "guest-stats", name, 0, errp);
-    visit_type_int(v, &s->stats_last_update, "last-update", errp);
-
-    visit_start_struct(v, NULL, NULL, "stats", 0, errp);
-    for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) {
-        visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
-                         errp);
+    visit_start_struct(v, NULL, "guest-stats", name, 0, &err);
+    if (err) {
+        goto out;
     }
-    visit_end_struct(v, errp);
+    visit_type_int(v, &s->stats_last_update, "last-update", &err);
+    if (err) {
+        goto out_end;
+    }
 
-    visit_end_struct(v, errp);
+    visit_start_struct(v, NULL, NULL, "stats", 0, &err);
+    if (err) {
+        goto out_end;
+    }
+    for (i = 0; !err && i < VIRTIO_BALLOON_S_NR; i++) {
+        visit_type_int64(v, (int64_t *) &s->stats[i], balloon_stat_names[i],
+                         &err);
+    }
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_struct(v, &err);
+
+out_end:
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_struct(v, &err);
+out:
+    error_propagate(errp, err);
 }
 
 static void balloon_stats_get_poll_interval(Object *obj, struct Visitor *v,
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index f3fa420..ecc0183 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -42,13 +42,9 @@
                         Error **errp);
 
     /* May be NULL */
-    void (*start_optional)(Visitor *v, bool *present, const char *name,
-                           Error **errp);
-    void (*end_optional)(Visitor *v, Error **errp);
+    void (*optional)(Visitor *v, bool *present, const char *name,
+                     Error **errp);
 
-    void (*start_handle)(Visitor *v, void **obj, const char *kind,
-                         const char *name, Error **errp);
-    void (*end_handle)(Visitor *v, Error **errp);
     void (*type_uint8)(Visitor *v, uint8_t *obj, const char *name, Error **errp);
     void (*type_uint16)(Visitor *v, uint16_t *obj, const char *name, Error **errp);
     void (*type_uint32)(Visitor *v, uint32_t *obj, const char *name, Error **errp);
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 29da211..4a0178f 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -39,9 +39,8 @@
 void visit_start_list(Visitor *v, const char *name, Error **errp);
 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp);
 void visit_end_list(Visitor *v, Error **errp);
-void visit_start_optional(Visitor *v, bool *present, const char *name,
-                          Error **errp);
-void visit_end_optional(Visitor *v, Error **errp);
+void visit_optional(Visitor *v, bool *present, const char *name,
+                    Error **errp);
 void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
                          const char *name, Error **errp);
 void visit_type_enum(Visitor *v, int *obj, const char *strings[],
diff --git a/include/ui/input.h b/include/ui/input.h
index 4976f3d..3d3d487 100644
--- a/include/ui/input.h
+++ b/include/ui/input.h
@@ -27,6 +27,7 @@
 QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
                                                    QemuInputHandler *handler);
 void qemu_input_handler_activate(QemuInputHandlerState *s);
+void qemu_input_handler_deactivate(QemuInputHandlerState *s);
 void qemu_input_handler_unregister(QemuInputHandlerState *s);
 void qemu_input_event_send(QemuConsole *src, InputEvent *evt);
 void qemu_input_event_sync(void);
@@ -35,6 +36,10 @@
 void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
 void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
 void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
+int qemu_input_key_value_to_number(const KeyValue *value);
+int qemu_input_key_value_to_qcode(const KeyValue *value);
+int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
+                                     int *codes);
 
 InputEvent *qemu_input_event_new_btn(InputButton btn, bool down);
 void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down);
diff --git a/monitor.c b/monitor.c
index 9af6b0a..593679a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -4269,6 +4269,55 @@
     return (p != NULL ? ++p : typestr);
 }
 
+static void add_completion_option(ReadLineState *rs, const char *str,
+                                  const char *option)
+{
+    if (!str || !option) {
+        return;
+    }
+    if (!strncmp(option, str, strlen(str))) {
+        readline_add_completion(rs, option);
+    }
+}
+
+void chardev_add_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    size_t len;
+    ChardevBackendInfoList *list, *start;
+
+    if (nb_args != 2) {
+        return;
+    }
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+
+    start = list = qmp_query_chardev_backends(NULL);
+    while (list) {
+        const char *chr_name = list->value->name;
+
+        if (!strncmp(chr_name, str, len)) {
+            readline_add_completion(rs, chr_name);
+        }
+        list = list->next;
+    }
+    qapi_free_ChardevBackendInfoList(start);
+}
+
+void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    size_t len;
+    int i;
+
+    if (nb_args != 2) {
+        return;
+    }
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+    for (i = 0; NetClientOptionsKind_lookup[i]; i++) {
+        add_completion_option(rs, str, NetClientOptionsKind_lookup[i]);
+    }
+}
+
 void device_add_completion(ReadLineState *rs, int nb_args, const char *str)
 {
     GSList *list, *elt;
@@ -4339,6 +4388,29 @@
     }
 }
 
+void chardev_remove_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    size_t len;
+    ChardevInfoList *list, *start;
+
+    if (nb_args != 2) {
+        return;
+    }
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+
+    start = list = qmp_query_chardev(NULL);
+    while (list) {
+        ChardevInfo *chr = list->value;
+
+        if (!strncmp(chr->label, str, len)) {
+            readline_add_completion(rs, chr->label);
+        }
+        list = list->next;
+    }
+    qapi_free_ChardevInfoList(start);
+}
+
 void device_del_completion(ReadLineState *rs, int nb_args, const char *str)
 {
     size_t len;
@@ -4376,6 +4448,77 @@
     qapi_free_ObjectPropertyInfoList(start);
 }
 
+void sendkey_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    int i;
+    char *sep;
+    size_t len;
+
+    if (nb_args != 2) {
+        return;
+    }
+    sep = strrchr(str, '-');
+    if (sep) {
+        str = sep + 1;
+    }
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+    for (i = 0; i < Q_KEY_CODE_MAX; i++) {
+        if (!strncmp(str, QKeyCode_lookup[i], len)) {
+            readline_add_completion(rs, QKeyCode_lookup[i]);
+        }
+    }
+}
+
+void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    size_t len;
+
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+    if (nb_args == 2) {
+        NetClientState *ncs[255];
+        int count, i;
+        count = qemu_find_net_clients_except(NULL, ncs,
+                                             NET_CLIENT_OPTIONS_KIND_NONE, 255);
+        for (i = 0; i < count; i++) {
+            const char *name = ncs[i]->name;
+            if (!strncmp(str, name, len)) {
+                readline_add_completion(rs, name);
+            }
+        }
+    } else if (nb_args == 3) {
+        add_completion_option(rs, str, "on");
+        add_completion_option(rs, str, "off");
+    }
+}
+
+void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str)
+{
+    int len, count, i;
+    NetClientState *ncs[255];
+
+    if (nb_args != 2) {
+        return;
+    }
+
+    len = strlen(str);
+    readline_set_completion_index(rs, len);
+    count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC,
+                                         255);
+    for (i = 0; i < count; i++) {
+        QemuOpts *opts;
+        const char *name = ncs[i]->name;
+        if (strncmp(str, name, len)) {
+            continue;
+        }
+        opts = qemu_opts_find(qemu_find_opts_err("netdev", NULL), name);
+        if (opts) {
+            readline_add_completion(rs, name);
+        }
+    }
+}
+
 static void monitor_find_completion_by_table(Monitor *mon,
                                              const mon_cmd_t *cmd_table,
                                              char **args,
@@ -4444,15 +4587,7 @@
             break;
         case 's':
         case 'S':
-            if (!strcmp(cmd->name, "sendkey")) {
-                char *sep = strrchr(str, '-');
-                if (sep)
-                    str = sep + 1;
-                readline_set_completion_index(mon->rs, strlen(str));
-                for (i = 0; i < Q_KEY_CODE_MAX; i++) {
-                    cmd_completion(mon, str, QKeyCode_lookup[i]);
-                }
-            } else if (!strcmp(cmd->name, "help|?")) {
+            if (!strcmp(cmd->name, "help|?")) {
                 monitor_find_completion_by_table(mon, cmd_table,
                                                  &args[1], nb_args - 1);
             }
diff --git a/net/net.c b/net/net.c
index 9db4dba..0ff2e40 100644
--- a/net/net.c
+++ b/net/net.c
@@ -633,7 +633,7 @@
         if (nc->info->type == type) {
             continue;
         }
-        if (!strcmp(nc->name, id)) {
+        if (!id || !strcmp(nc->name, id)) {
             if (ret < max) {
                 ncs[ret] = nc;
             }
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 87c1c78..16382e7 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -484,8 +484,7 @@
 
 
 static void
-opts_start_optional(Visitor *v, bool *present, const char *name,
-                       Error **errp)
+opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
 {
     OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
 
@@ -528,7 +527,7 @@
     /* type_number() is not filled in, but this is not the first visitor to
      * skip some mandatory methods... */
 
-    ov->visitor.start_optional = &opts_start_optional;
+    ov->visitor.optional = &opts_optional;
 
     ov->opts_root = opts;
 
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 6451a21..55f8d40 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -17,46 +17,27 @@
 #include "qapi/visitor.h"
 #include "qapi/visitor-impl.h"
 
-void visit_start_handle(Visitor *v, void **obj, const char *kind,
-                        const char *name, Error **errp)
-{
-    if (!error_is_set(errp) && v->start_handle) {
-        v->start_handle(v, obj, kind, name, errp);
-    }
-}
-
-void visit_end_handle(Visitor *v, Error **errp)
-{
-    if (!error_is_set(errp) && v->end_handle) {
-        v->end_handle(v, errp);
-    }
-}
-
 void visit_start_struct(Visitor *v, void **obj, const char *kind,
                         const char *name, size_t size, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        v->start_struct(v, obj, kind, name, size, errp);
-    }
+    v->start_struct(v, obj, kind, name, size, errp);
 }
 
 void visit_end_struct(Visitor *v, Error **errp)
 {
-    assert(!error_is_set(errp));
     v->end_struct(v, errp);
 }
 
 void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp)
 {
-    if (!error_is_set(errp) && v->start_implicit_struct) {
+    if (v->start_implicit_struct) {
         v->start_implicit_struct(v, obj, size, errp);
     }
 }
 
 void visit_end_implicit_struct(Visitor *v, Error **errp)
 {
-    assert(!error_is_set(errp));
     if (v->end_implicit_struct) {
         v->end_implicit_struct(v, errp);
     }
@@ -64,45 +45,31 @@
 
 void visit_start_list(Visitor *v, const char *name, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        v->start_list(v, name, errp);
-    }
+    v->start_list(v, name, errp);
 }
 
 GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        return v->next_list(v, list, errp);
-    }
-
-    return 0;
+    return v->next_list(v, list, errp);
 }
 
 void visit_end_list(Visitor *v, Error **errp)
 {
-    assert(!error_is_set(errp));
     v->end_list(v, errp);
 }
 
-void visit_start_optional(Visitor *v, bool *present, const char *name,
-                          Error **errp)
+void visit_optional(Visitor *v, bool *present, const char *name,
+                    Error **errp)
 {
-    if (!error_is_set(errp) && v->start_optional) {
-        v->start_optional(v, present, name, errp);
-    }
-}
-
-void visit_end_optional(Visitor *v, Error **errp)
-{
-    if (!error_is_set(errp) && v->end_optional) {
-        v->end_optional(v, errp);
+    if (v->optional) {
+        v->optional(v, present, name, errp);
     }
 }
 
 void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
                          const char *name, Error **errp)
 {
-    if (!error_is_set(errp) && v->get_next_type) {
+    if (v->get_next_type) {
         v->get_next_type(v, obj, qtypes, name, errp);
     }
 }
@@ -110,192 +77,172 @@
 void visit_type_enum(Visitor *v, int *obj, const char *strings[],
                      const char *kind, const char *name, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        v->type_enum(v, obj, strings, kind, name, errp);
-    }
+    v->type_enum(v, obj, strings, kind, name, errp);
 }
 
 void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        v->type_int(v, obj, name, errp);
-    }
+    v->type_int(v, obj, name, errp);
 }
 
 void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_uint8) {
-            v->type_uint8(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            if (value < 0 || value > UINT8_MAX) {
-                error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
-                          "uint8_t");
-                return;
-            }
-            *obj = value;
+
+    if (v->type_uint8) {
+        v->type_uint8(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        if (value < 0 || value > UINT8_MAX) {
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+                      "uint8_t");
+            return;
         }
+        *obj = value;
     }
 }
 
 void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_uint16) {
-            v->type_uint16(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            if (value < 0 || value > UINT16_MAX) {
-                error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
-                          "uint16_t");
-                return;
-            }
-            *obj = value;
+
+    if (v->type_uint16) {
+        v->type_uint16(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        if (value < 0 || value > UINT16_MAX) {
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+                      "uint16_t");
+            return;
         }
+        *obj = value;
     }
 }
 
 void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_uint32) {
-            v->type_uint32(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            if (value < 0 || value > UINT32_MAX) {
-                error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
-                          "uint32_t");
-                return;
-            }
-            *obj = value;
+
+    if (v->type_uint32) {
+        v->type_uint32(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        if (value < 0 || value > UINT32_MAX) {
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+                      "uint32_t");
+            return;
         }
+        *obj = value;
     }
 }
 
 void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_uint64) {
-            v->type_uint64(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            *obj = value;
-        }
+
+    if (v->type_uint64) {
+        v->type_uint64(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        *obj = value;
     }
 }
 
 void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_int8) {
-            v->type_int8(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            if (value < INT8_MIN || value > INT8_MAX) {
-                error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
-                          "int8_t");
-                return;
-            }
-            *obj = value;
+
+    if (v->type_int8) {
+        v->type_int8(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        if (value < INT8_MIN || value > INT8_MAX) {
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+                      "int8_t");
+            return;
         }
+        *obj = value;
     }
 }
 
 void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_int16) {
-            v->type_int16(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            if (value < INT16_MIN || value > INT16_MAX) {
-                error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
-                          "int16_t");
-                return;
-            }
-            *obj = value;
+
+    if (v->type_int16) {
+        v->type_int16(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        if (value < INT16_MIN || value > INT16_MAX) {
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+                      "int16_t");
+            return;
         }
+        *obj = value;
     }
 }
 
 void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_int32) {
-            v->type_int32(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            if (value < INT32_MIN || value > INT32_MAX) {
-                error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
-                          "int32_t");
-                return;
-            }
-            *obj = value;
+
+    if (v->type_int32) {
+        v->type_int32(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        if (value < INT32_MIN || value > INT32_MAX) {
+            error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+                      "int32_t");
+            return;
         }
+        *obj = value;
     }
 }
 
 void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        if (v->type_int64) {
-            v->type_int64(v, obj, name, errp);
-        } else {
-            v->type_int(v, obj, name, errp);
-        }
+    if (v->type_int64) {
+        v->type_int64(v, obj, name, errp);
+    } else {
+        v->type_int(v, obj, name, errp);
     }
 }
 
 void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
 {
     int64_t value;
-    if (!error_is_set(errp)) {
-        if (v->type_size) {
-            v->type_size(v, obj, name, errp);
-        } else if (v->type_uint64) {
-            v->type_uint64(v, obj, name, errp);
-        } else {
-            value = *obj;
-            v->type_int(v, &value, name, errp);
-            *obj = value;
-        }
+
+    if (v->type_size) {
+        v->type_size(v, obj, name, errp);
+    } else if (v->type_uint64) {
+        v->type_uint64(v, obj, name, errp);
+    } else {
+        value = *obj;
+        v->type_int(v, &value, name, errp);
+        *obj = value;
     }
 }
 
 void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        v->type_bool(v, obj, name, errp);
-    }
+    v->type_bool(v, obj, name, errp);
 }
 
 void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        v->type_str(v, obj, name, errp);
-    }
+    v->type_str(v, obj, name, errp);
 }
 
 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
 {
-    if (!error_is_set(errp)) {
-        v->type_number(v, obj, name, errp);
-    }
+    v->type_number(v, obj, name, errp);
 }
 
 void output_type_enum(Visitor *v, int *obj, const char *strings[],
@@ -321,13 +268,15 @@
                      const char *kind, const char *name,
                      Error **errp)
 {
+    Error *local_err = NULL;
     int64_t value = 0;
     char *enum_str;
 
     assert(strings);
 
-    visit_type_str(v, &enum_str, name, errp);
-    if (error_is_set(errp)) {
+    visit_type_str(v, &enum_str, name, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
         return;
     }
 
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index a2bed1e..d861206 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -286,8 +286,8 @@
     }
 }
 
-static void qmp_input_start_optional(Visitor *v, bool *present,
-                                     const char *name, Error **errp)
+static void qmp_input_optional(Visitor *v, bool *present, const char *name,
+                               Error **errp)
 {
     QmpInputVisitor *qiv = to_qiv(v);
     QObject *qobj = qmp_input_get_object(qiv, name, true);
@@ -329,7 +329,7 @@
     v->visitor.type_bool = qmp_input_type_bool;
     v->visitor.type_str = qmp_input_type_str;
     v->visitor.type_number = qmp_input_type_number;
-    v->visitor.start_optional = qmp_input_start_optional;
+    v->visitor.optional = qmp_input_optional;
     v->visitor.get_next_type = qmp_input_get_next_type;
 
     qmp_input_push(v, obj, NULL);
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 793548a..5780944 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -120,8 +120,8 @@
     *obj = val;
 }
 
-static void parse_start_optional(Visitor *v, bool *present,
-                                 const char *name, Error **errp)
+static void parse_optional(Visitor *v, bool *present, const char *name,
+                           Error **errp)
 {
     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 
@@ -155,7 +155,7 @@
     v->visitor.type_bool = parse_type_bool;
     v->visitor.type_str = parse_type_str;
     v->visitor.type_number = parse_type_number;
-    v->visitor.start_optional = parse_start_optional;
+    v->visitor.optional = parse_optional;
 
     v->string = str;
     return v;
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 8d9096f..386f17e 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -2,16 +2,19 @@
 # QAPI command marshaller generator
 #
 # Copyright IBM, Corp. 2011
+# Copyright (C) 2014 Red Hat, Inc.
 #
 # Authors:
 #  Anthony Liguori <aliguori@us.ibm.com>
 #  Michael Roth    <mdroth@linux.vnet.ibm.com>
+#  Markus Armbruster <armbru@redhat.com>
 #
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
 from ordereddict import OrderedDict
 from qapi import *
+import re
 import sys
 import os
 import getopt
@@ -37,6 +40,15 @@
 ''',
                  ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip()
 
+def gen_err_check(errvar):
+    if errvar:
+        return mcgen('''
+if (local_err) {
+    goto out;
+}
+''')
+    return ''
+
 def gen_sync_call(name, args, ret_type, indent=0):
     ret = ""
     arglist=""
@@ -49,15 +61,14 @@
         arglist += "%s, " % (c_var(argname))
     push_indent(indent)
     ret = mcgen('''
-%(retval)sqmp_%(name)s(%(args)serrp);
+%(retval)sqmp_%(name)s(%(args)s&local_err);
 
 ''',
                 name=c_fun(name), args=arglist, retval=retval).rstrip()
     if ret_type:
+        ret += "\n" + gen_err_check('local_err')
         ret += "\n" + mcgen(''''
-if (!error_is_set(errp)) {
-    %(marshal_output_call)s
-}
+%(marshal_output_call)s
 ''',
                             marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip()
     pop_indent(indent)
@@ -67,18 +78,19 @@
 def gen_marshal_output_call(name, ret_type):
     if not ret_type:
         return ""
-    return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name)
+    return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_fun(name)
 
-def gen_visitor_input_containers_decl(args):
+def gen_visitor_input_containers_decl(args, obj):
     ret = ""
 
     push_indent()
     if len(args) > 0:
         ret += mcgen('''
-QmpInputVisitor *mi;
+QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s);
 QapiDeallocVisitor *md;
 Visitor *v;
-''')
+''',
+                     obj=obj)
     pop_indent()
 
     return ret.rstrip()
@@ -106,9 +118,10 @@
     pop_indent()
     return ret.rstrip()
 
-def gen_visitor_input_block(args, obj, dealloc=False):
+def gen_visitor_input_block(args, dealloc=False):
     ret = ""
-    errparg = 'errp'
+    errparg = '&local_err'
+    errarg = 'local_err'
 
     if len(args) == 0:
         return ret
@@ -117,45 +130,45 @@
 
     if dealloc:
         errparg = 'NULL'
+        errarg = None;
         ret += mcgen('''
+qmp_input_visitor_cleanup(mi);
 md = qapi_dealloc_visitor_new();
 v = qapi_dealloc_get_visitor(md);
 ''')
     else:
         ret += mcgen('''
-mi = qmp_input_visitor_new_strict(%(obj)s);
 v = qmp_input_get_visitor(mi);
-''',
-                     obj=obj)
+''')
 
     for argname, argtype, optional, structured in parse_args(args):
         if optional:
             ret += mcgen('''
-visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
-if (has_%(c_name)s) {
+visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
 ''',
                          c_name=c_var(argname), name=argname, errp=errparg)
+            ret += gen_err_check(errarg)
+            ret += mcgen('''
+if (has_%(c_name)s) {
+''',
+                         c_name=c_var(argname))
             push_indent()
         ret += mcgen('''
 %(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
 ''',
                      c_name=c_var(argname), name=argname, argtype=argtype,
                      visitor=type_visitor(argtype), errp=errparg)
+        ret += gen_err_check(errarg)
         if optional:
             pop_indent()
             ret += mcgen('''
 }
-visit_end_optional(v, %(errp)s);
-''', errp=errparg)
+''')
 
     if dealloc:
         ret += mcgen('''
 qapi_dealloc_visitor_cleanup(md);
 ''')
-    else:
-        ret += mcgen('''
-qmp_input_visitor_cleanup(mi);
-''')
     pop_indent()
     return ret.rstrip()
 
@@ -166,16 +179,22 @@
     ret = mcgen('''
 static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
 {
-    QapiDeallocVisitor *md = qapi_dealloc_visitor_new();
+    Error *local_err = NULL;
     QmpOutputVisitor *mo = qmp_output_visitor_new();
+    QapiDeallocVisitor *md;
     Visitor *v;
 
     v = qmp_output_get_visitor(mo);
-    %(visitor)s(v, &ret_in, "unused", errp);
-    if (!error_is_set(errp)) {
-        *ret_out = qmp_output_get_qobject(mo);
+    %(visitor)s(v, &ret_in, "unused", &local_err);
+    if (local_err) {
+        goto out;
     }
+    *ret_out = qmp_output_get_qobject(mo);
+
+out:
+    error_propagate(errp, local_err);
     qmp_output_visitor_cleanup(mo);
+    md = qapi_dealloc_visitor_new();
     v = qapi_dealloc_get_visitor(md);
     %(visitor)s(v, &ret_in, "unused", NULL);
     qapi_dealloc_visitor_cleanup(md);
@@ -200,13 +219,12 @@
     ret = mcgen('''
 %(header)s
 {
+    Error *local_err = NULL;
 ''',
                 header=hdr)
 
     if middle_mode:
         ret += mcgen('''
-    Error *local_err = NULL;
-    Error **errp = &local_err;
     QDict *args = (QDict *)qdict;
 ''')
 
@@ -228,29 +246,32 @@
 %(visitor_input_block)s
 
 ''',
-                     visitor_input_containers_decl=gen_visitor_input_containers_decl(args),
+                     visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"),
                      visitor_input_vars_decl=gen_visitor_input_vars_decl(args),
-                     visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)"))
+                     visitor_input_block=gen_visitor_input_block(args))
     else:
         ret += mcgen('''
+
     (void)args;
 ''')
 
     ret += mcgen('''
-    if (error_is_set(errp)) {
-        goto out;
-    }
 %(sync_call)s
 ''',
                  sync_call=gen_sync_call(name, args, ret_type, indent=4))
-    ret += mcgen('''
+    if re.search('^ *goto out\\;', ret, re.MULTILINE):
+        ret += mcgen('''
 
 out:
 ''')
+    if not middle_mode:
+        ret += mcgen('''
+    error_propagate(errp, local_err);
+''')
     ret += mcgen('''
 %(visitor_input_block_cleanup)s
 ''',
-                 visitor_input_block_cleanup=gen_visitor_input_block(args, None,
+                 visitor_input_block_cleanup=gen_visitor_input_block(args,
                                                                      dealloc=True))
 
     if middle_mode:
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index c6579be..06a79f1 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -2,21 +2,47 @@
 # QAPI visitor generator
 #
 # Copyright IBM, Corp. 2011
+# Copyright (C) 2014 Red Hat, Inc.
 #
 # Authors:
 #  Anthony Liguori <aliguori@us.ibm.com>
 #  Michael Roth    <mdroth@linux.vnet.ibm.com>
+#  Markus Armbruster <armbru@redhat.com>
 #
 # This work is licensed under the terms of the GNU GPL, version 2.
 # See the COPYING file in the top-level directory.
 
 from ordereddict import OrderedDict
 from qapi import *
+import re
 import sys
 import os
 import getopt
 import errno
 
+implicit_structs = []
+
+def generate_visit_implicit_struct(type):
+    global implicit_structs
+    if type in implicit_structs:
+        return ''
+    implicit_structs.append(type)
+    return mcgen('''
+
+static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
+{
+    Error *err = NULL;
+
+    visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err);
+    if (!err) {
+        visit_type_%(c_type)s_fields(m, obj, errp);
+        visit_end_implicit_struct(m, &err);
+    }
+    error_propagate(errp, err);
+}
+''',
+                 c_type=type_name(type))
+
 def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
     substructs = []
     ret = ''
@@ -35,6 +61,19 @@
             nested_field_prefix = "%s%s." % (field_prefix, argname)
             ret += generate_visit_struct_fields(name, nested_field_prefix,
                                                 nested_fn_prefix, argentry)
+            ret += mcgen('''
+
+static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp)
+{
+''',
+                         name=name, full_name=full_name, c_name=c_var(argname))
+            ret += generate_visit_struct_body(full_name, argname, argentry)
+            ret += mcgen('''
+}
+''')
+
+    if base:
+        ret += generate_visit_implicit_struct(base)
 
     ret += mcgen('''
 
@@ -47,12 +86,9 @@
 
     if base:
         ret += mcgen('''
-visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(type)s), &err);
-if (!err) {
-    visit_type_%(type)s_fields(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
-    error_propagate(errp, err);
-    err = NULL;
-    visit_end_implicit_struct(m, &err);
+visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
+if (err) {
+    goto out;
 }
 ''',
                      c_prefix=c_var(field_prefix),
@@ -61,15 +97,18 @@
     for argname, argentry, optional, structured in parse_args(members):
         if optional:
             ret += mcgen('''
-visit_start_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
-if ((*obj)->%(prefix)shas_%(c_name)s) {
+visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
+if (!err && (*obj)->%(prefix)shas_%(c_name)s) {
 ''',
                          c_prefix=c_var(field_prefix), prefix=field_prefix,
                          c_name=c_var(argname), name=argname)
             push_indent()
 
         if structured:
-            ret += generate_visit_struct_body(full_name, argname, argentry)
+            ret += mcgen('''
+visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err);
+''',
+                         full_name=full_name, c_name=c_var(argname))
         else:
             ret += mcgen('''
 visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
@@ -82,12 +121,20 @@
             pop_indent()
             ret += mcgen('''
 }
-visit_end_optional(m, &err);
+''')
+        ret += mcgen('''
+if (err) {
+    goto out;
+}
 ''')
 
     pop_indent()
-    ret += mcgen('''
+    if re.search('^ *goto out\\;', ret, re.MULTILINE):
+        ret += mcgen('''
 
+out:
+''')
+    ret += mcgen('''
     error_propagate(errp, err);
 }
 ''')
@@ -96,9 +143,9 @@
 
 def generate_visit_struct_body(field_prefix, name, members):
     ret = mcgen('''
-if (!error_is_set(errp)) {
+    Error *err = NULL;
+
 ''')
-    push_indent()
 
     if not field_prefix:
         full_name = name
@@ -107,36 +154,26 @@
 
     if len(field_prefix):
         ret += mcgen('''
-Error **errp = &err; /* from outer scope */
-Error *err = NULL;
-visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
+    visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
 ''',
                 name=name)
     else:
         ret += mcgen('''
-Error *err = NULL;
-visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
+    visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
 ''',
                 name=name)
 
     ret += mcgen('''
-if (!err) {
-    if (*obj) {
-        visit_type_%(name)s_fields(m, obj, &err);
-        error_propagate(errp, err);
-        err = NULL;
-    }
-''',
-        name=full_name)
-
-    pop_indent()
-    ret += mcgen('''
-        /* Always call end_struct if start_struct succeeded.  */
+    if (!err) {
+        if (*obj) {
+            visit_type_%(name)s_fields(m, obj, errp);
+        }
         visit_end_struct(m, &err);
     }
     error_propagate(errp, err);
-}
-''')
+''',
+        name=full_name)
+
     return ret
 
 def generate_visit_struct(expr):
@@ -154,9 +191,7 @@
 ''',
                 name=name)
 
-    push_indent()
     ret += generate_visit_struct_body("", name, members)
-    pop_indent()
 
     ret += mcgen('''
 }
@@ -168,24 +203,26 @@
 
 void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
 {
-    GenericList *i, **prev = (GenericList **)obj;
     Error *err = NULL;
+    GenericList *i, **prev;
 
-    if (!error_is_set(errp)) {
-        visit_start_list(m, name, &err);
-        if (!err) {
-            for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) {
-                %(name)sList *native_i = (%(name)sList *)i;
-                visit_type_%(name)s(m, &native_i->value, NULL, &err);
-            }
-            error_propagate(errp, err);
-            err = NULL;
-
-            /* Always call end_list if start_list succeeded.  */
-            visit_end_list(m, &err);
-        }
-        error_propagate(errp, err);
+    visit_start_list(m, name, &err);
+    if (err) {
+        goto out;
     }
+
+    for (prev = (GenericList **)obj;
+         !err && (i = visit_next_list(m, prev, &err)) != NULL;
+         prev = &i) {
+        %(name)sList *native_i = (%(name)sList *)i;
+        visit_type_%(name)s(m, &native_i->value, NULL, &err);
+    }
+
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_list(m, &err);
+out:
+    error_propagate(errp, err);
 }
 ''',
                 name=name)
@@ -207,10 +244,15 @@
 {
     Error *err = NULL;
 
-    if (!error_is_set(errp)) {
-        visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
-        visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
-        switch ((*obj)->kind) {
+    visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
+    if (err) {
+        goto out;
+    }
+    visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
+    if (err) {
+        goto out_end;
+    }
+    switch ((*obj)->kind) {
 ''',
     name=name)
 
@@ -225,22 +267,24 @@
 
         enum_full_value = generate_enum_full_value(disc_type, key)
         ret += mcgen('''
-        case %(enum_full_value)s:
-            visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
-            break;
+    case %(enum_full_value)s:
+        visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
+        break;
 ''',
                 enum_full_value = enum_full_value,
                 c_type = type_name(members[key]),
                 c_name = c_fun(key))
 
     ret += mcgen('''
-        default:
-            abort();
-        }
-        error_propagate(errp, err);
-        err = NULL;
-        visit_end_implicit_struct(m, &err);
+    default:
+        abort();
     }
+out_end:
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_implicit_struct(m, &err);
+out:
+    error_propagate(errp, err);
 }
 ''')
 
@@ -277,40 +321,43 @@
             del base_fields[discriminator]
         ret += generate_visit_struct_fields(name, "", "", base_fields)
 
+    if discriminator:
+        for key in members:
+            ret += generate_visit_implicit_struct(members[key])
+
     ret += mcgen('''
 
 void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
 {
     Error *err = NULL;
 
-    if (!error_is_set(errp)) {
-        visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
-        if (!err) {
-            if (*obj) {
+    visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
+    if (err) {
+        goto out;
+    }
+    if (*obj) {
 ''',
                  name=name)
 
-
-    push_indent()
-    push_indent()
-    push_indent()
-
     if base:
         ret += mcgen('''
-    visit_type_%(name)s_fields(m, obj, &err);
+        visit_type_%(name)s_fields(m, obj, &err);
+        if (err) {
+            goto out_obj;
+        }
 ''',
             name=name)
 
-    pop_indent()
-
     if not discriminator:
         disc_key = "type"
     else:
         disc_key = discriminator
     ret += mcgen('''
         visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err);
-        if (!err) {
-            switch ((*obj)->kind) {
+        if (err) {
+            goto out_obj;
+        }
+        switch ((*obj)->kind) {
 ''',
                  disc_type = disc_type,
                  disc_key = disc_key)
@@ -319,47 +366,32 @@
         if not discriminator:
             fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
         else:
-            fmt = '''visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(c_type)s), &err);
-                if (!err) {
-                    visit_type_%(c_type)s_fields(m, &(*obj)->%(c_name)s, &err);
-                    error_propagate(errp, err);
-                    err = NULL;
-                    visit_end_implicit_struct(m, &err);
-                }'''
+            fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
 
         enum_full_value = generate_enum_full_value(disc_type, key)
         ret += mcgen('''
-            case %(enum_full_value)s:
-                ''' + fmt + '''
-                break;
+        case %(enum_full_value)s:
+            ''' + fmt + '''
+            break;
 ''',
                 enum_full_value = enum_full_value,
                 c_type=type_name(members[key]),
                 c_name=c_fun(key))
 
     ret += mcgen('''
-            default:
-                abort();
-            }
+        default:
+            abort();
         }
+out_obj:
         error_propagate(errp, err);
         err = NULL;
     }
-''')
-    pop_indent()
-    ret += mcgen('''
-        /* Always call end_struct if start_struct succeeded.  */
-        visit_end_struct(m, &err);
-    }
+    visit_end_struct(m, &err);
+out:
     error_propagate(errp, err);
 }
 ''')
 
-    pop_indent();
-    ret += mcgen('''
-}
-''')
-
     return ret
 
 def generate_declaration(name, members, genlist=True, builtin_type=False):
@@ -476,7 +508,7 @@
 /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
 
 /*
- * schema-defined QAPI visitor function
+ * schema-defined QAPI visitor functions
  *
  * Copyright IBM, Corp. 2011
  *
diff --git a/scripts/qapi.py b/scripts/qapi.py
index ec806aa..0265b40 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -73,13 +73,18 @@
 
 class QAPISchema:
 
-    def __init__(self, fp, input_relname=None, include_hist=[], parent_info=None):
+    def __init__(self, fp, input_relname=None, include_hist=[],
+                 previously_included=[], parent_info=None):
+        """ include_hist is a stack used to detect inclusion cycles
+            previously_included is a global state used to avoid multiple
+                                inclusions of the same file"""
         input_fname = os.path.abspath(fp.name)
         if input_relname is None:
             input_relname = fp.name
         self.input_dir = os.path.dirname(input_fname)
         self.input_file = input_relname
         self.include_hist = include_hist + [(input_relname, input_fname)]
+        previously_included.append(input_fname)
         self.parent_info = parent_info
         self.src = fp.read()
         if self.src == '' or self.src[-1] != '\n':
@@ -106,13 +111,16 @@
                        for elem in self.include_hist):
                     raise QAPIExprError(expr_info, "Inclusion loop for %s"
                                         % include)
+                # skip multiple include of the same file
+                if include_path in previously_included:
+                    continue
                 try:
                     fobj = open(include_path, 'r')
                 except IOError as e:
                     raise QAPIExprError(expr_info,
                                         '%s: %s' % (e.strerror, include))
-                exprs_include = QAPISchema(fobj, include,
-                                           self.include_hist, expr_info)
+                exprs_include = QAPISchema(fobj, include, self.include_hist,
+                                           previously_included, expr_info)
                 self.exprs.extend(exprs_include.exprs)
             else:
                 expr_elem = {'expr': expr,
diff --git a/tests/Makefile b/tests/Makefile
index 6b8b6f2..9f7ca61 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -193,7 +193,8 @@
         flat-union-string-discriminator.json \
         include-simple.json include-relpath.json include-format-err.json \
         include-non-file.json include-no-file.json include-before-err.json \
-        include-nested-err.json include-self-cycle.json include-cycle.json)
+        include-nested-err.json include-self-cycle.json include-cycle.json \
+        include-repetition.json)
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
 
diff --git a/tests/qapi-schema/include-repetition-sub.json b/tests/qapi-schema/include-repetition-sub.json
new file mode 100644
index 0000000..6bfffdf
--- /dev/null
+++ b/tests/qapi-schema/include-repetition-sub.json
@@ -0,0 +1,2 @@
+{ 'include': 'comments.json' }
+{ 'include': 'comments.json' }
diff --git a/tests/qapi-schema/include-repetition.err b/tests/qapi-schema/include-repetition.err
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/qapi-schema/include-repetition.err
diff --git a/tests/qapi-schema/include-repetition.exit b/tests/qapi-schema/include-repetition.exit
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tests/qapi-schema/include-repetition.exit
@@ -0,0 +1 @@
+0
diff --git a/tests/qapi-schema/include-repetition.json b/tests/qapi-schema/include-repetition.json
new file mode 100644
index 0000000..ec329dd
--- /dev/null
+++ b/tests/qapi-schema/include-repetition.json
@@ -0,0 +1,3 @@
+{ 'include': 'comments.json' }
+{ 'include': 'include-repetition-sub.json' }
+{ 'include': 'comments.json' }
diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out
new file mode 100644
index 0000000..4ce3dcf
--- /dev/null
+++ b/tests/qapi-schema/include-repetition.out
@@ -0,0 +1,3 @@
+[OrderedDict([('enum', 'Status'), ('data', ['good', 'bad', 'ugly'])])]
+[{'enum_name': 'Status', 'enum_values': ['good', 'bad', 'ugly']}]
+[]
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index 449d285..0f77003 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -72,14 +72,30 @@
 static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
                                   const char *name, Error **errp)
 {
+    Error *err = NULL;
+
     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
-                       errp);
+                       &err);
+    if (err) {
+        goto out;
+    }
 
-    visit_type_int(v, &(*obj)->integer, "integer", errp);
-    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
-    visit_type_str(v, &(*obj)->string, "string", errp);
+    visit_type_int(v, &(*obj)->integer, "integer", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_str(v, &(*obj)->string, "string", &err);
 
-    visit_end_struct(v, errp);
+out_end:
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_struct(v, &err);
+out:
+    error_propagate(errp, err);
 }
 
 static void test_validate_struct(TestInputVisitorData *data,
diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
index a58a3e6..1c8e872 100644
--- a/tests/test-qmp-input-visitor.c
+++ b/tests/test-qmp-input-visitor.c
@@ -199,16 +199,24 @@
 
     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
                        &err);
-    if (!err) {
-        visit_type_int(v, &(*obj)->integer, "integer", &err);
-        visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
-        visit_type_str(v, &(*obj)->string, "string", &err);
-
-        /* Always call end_struct if start_struct succeeded.  */
-        error_propagate(errp, err);
-        err = NULL;
-        visit_end_struct(v, &err);
+    if (err) {
+        goto out;
     }
+    visit_type_int(v, &(*obj)->integer, "integer", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_str(v, &(*obj)->string, "string", &err);
+
+out_end:
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_struct(v, &err);
+out:
     error_propagate(errp, err);
 }
 
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index 2580f3d..9c15458 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -176,14 +176,30 @@
 static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
                                   const char *name, Error **errp)
 {
+    Error *err = NULL;
+
     visit_start_struct(v, (void **)obj, "TestStruct", name, sizeof(TestStruct),
-                       errp);
+                       &err);
+    if (err) {
+        goto out;
+    }
 
-    visit_type_int(v, &(*obj)->integer, "integer", errp);
-    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
-    visit_type_str(v, &(*obj)->string, "string", errp);
+    visit_type_int(v, &(*obj)->integer, "integer", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_str(v, &(*obj)->string, "string", &err);
 
-    visit_end_struct(v, errp);
+out_end:
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_struct(v, &err);
+out:
+    error_propagate(errp, err);
 }
 
 static void test_visitor_out_struct(TestOutputVisitorData *data,
diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c
index 8166cf1..74d6481 100644
--- a/tests/test-visitor-serialization.c
+++ b/tests/test-visitor-serialization.c
@@ -195,13 +195,29 @@
 static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
                                   const char *name, Error **errp)
 {
-    visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), errp);
+    Error *err = NULL;
 
-    visit_type_int(v, &(*obj)->integer, "integer", errp);
-    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
-    visit_type_str(v, &(*obj)->string, "string", errp);
+    visit_start_struct(v, (void **)obj, NULL, name, sizeof(TestStruct), &err);
+    if (err) {
+        goto out;
+    }
 
-    visit_end_struct(v, errp);
+    visit_type_int(v, &(*obj)->integer, "integer", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
+    if (err) {
+        goto out_end;
+    }
+    visit_type_str(v, &(*obj)->string, "string", &err);
+
+out_end:
+    error_propagate(errp, err);
+    err = NULL;
+    visit_end_struct(v, &err);
+out:
+    error_propagate(errp, err);
 }
 
 static TestStruct *struct_create(void)
diff --git a/trace-events b/trace-events
index 2c5b307..b6d289d 100644
--- a/trace-events
+++ b/trace-events
@@ -688,17 +688,21 @@
 megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d"
 megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d"
 megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x"
+megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: DCMD LD list query flags %x"
 megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld"
 megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x"
 megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 ""
 megasas_abort_invalid_context(int cmd, uint64_t context, int abort_cmd) "scmd %d: invalid frame context %" PRIx64 " for abort frame %x"
 megasas_reset(void) "Reset"
-megasas_init(int sges, int cmds, const char *intr, const char *mode) "Using %d sges, %d cmds, %s, %s mode"
+megasas_init(int sges, int cmds, const char *mode) "Using %d sges, %d cmds, %s mode"
 megasas_msix_raise(int vector) "vector %d"
+megasas_msi_raise(int vector) "vector %d"
 megasas_irq_lower(void) "INTx"
 megasas_irq_raise(void) "INTx"
 megasas_intr_enabled(void) "Interrupts enabled"
 megasas_intr_disabled(void) "Interrupts disabled"
+megasas_msix_enabled(int vector) "vector %d"
+megasas_msi_enabled(int vector) "vector %d"
 megasas_mmio_readl(unsigned long addr, uint32_t val) "addr 0x%lx: 0x%x"
 megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx"
 megasas_mmio_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x"
@@ -862,8 +866,8 @@
 escc_mem_readb_ctrl(char channel, uint32_t reg, uint8_t val) "Read channel %c, reg[%d] = %2.2x"
 escc_mem_readb_data(char channel, uint32_t ret) "Read channel %c, ch %d"
 escc_serial_receive_byte(char channel, int ch) "channel %c put ch %d"
-escc_sunkbd_event_in(int ch) "Untranslated keycode %2.2x"
-escc_sunkbd_event_out(int ch) "Translated keycode %2.2x"
+escc_sunkbd_event_in(int ch, const char *name, int down) "QKeyCode 0x%2.2x [%s], down %d"
+escc_sunkbd_event_out(int ch) "Translated keycode 0x%2.2x"
 escc_kbd_command(int val) "Command %d"
 escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=%01x"
 
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 4af420b..6afb52a 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -7,7 +7,8 @@
 vnc-obj-$(CONFIG_VNC_WS) += vnc-ws.o
 vnc-obj-y += vnc-jobs.o
 
-common-obj-y += keymaps.o console.o cursor.o input.o input-legacy.o qemu-pixman.o
+common-obj-y += keymaps.o console.o cursor.o qemu-pixman.o
+common-obj-y += input.o input-keymap.o input-legacy.o
 common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o
 common-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o sdl2.o
 common-obj-$(CONFIG_COCOA) += cocoa.o
diff --git a/ui/input-keymap.c b/ui/input-keymap.c
new file mode 100644
index 0000000..6da4495
--- /dev/null
+++ b/ui/input-keymap.c
@@ -0,0 +1,191 @@
+#include "sysemu/sysemu.h"
+#include "ui/keymaps.h"
+#include "ui/input.h"
+
+static const int qcode_to_number[] = {
+    [Q_KEY_CODE_SHIFT] = 0x2a,
+    [Q_KEY_CODE_SHIFT_R] = 0x36,
+
+    [Q_KEY_CODE_ALT] = 0x38,
+    [Q_KEY_CODE_ALT_R] = 0xb8,
+    [Q_KEY_CODE_ALTGR] = 0x64,
+    [Q_KEY_CODE_ALTGR_R] = 0xe4,
+    [Q_KEY_CODE_CTRL] = 0x1d,
+    [Q_KEY_CODE_CTRL_R] = 0x9d,
+
+    [Q_KEY_CODE_MENU] = 0xdd,
+
+    [Q_KEY_CODE_ESC] = 0x01,
+
+    [Q_KEY_CODE_1] = 0x02,
+    [Q_KEY_CODE_2] = 0x03,
+    [Q_KEY_CODE_3] = 0x04,
+    [Q_KEY_CODE_4] = 0x05,
+    [Q_KEY_CODE_5] = 0x06,
+    [Q_KEY_CODE_6] = 0x07,
+    [Q_KEY_CODE_7] = 0x08,
+    [Q_KEY_CODE_8] = 0x09,
+    [Q_KEY_CODE_9] = 0x0a,
+    [Q_KEY_CODE_0] = 0x0b,
+    [Q_KEY_CODE_MINUS] = 0x0c,
+    [Q_KEY_CODE_EQUAL] = 0x0d,
+    [Q_KEY_CODE_BACKSPACE] = 0x0e,
+
+    [Q_KEY_CODE_TAB] = 0x0f,
+    [Q_KEY_CODE_Q] = 0x10,
+    [Q_KEY_CODE_W] = 0x11,
+    [Q_KEY_CODE_E] = 0x12,
+    [Q_KEY_CODE_R] = 0x13,
+    [Q_KEY_CODE_T] = 0x14,
+    [Q_KEY_CODE_Y] = 0x15,
+    [Q_KEY_CODE_U] = 0x16,
+    [Q_KEY_CODE_I] = 0x17,
+    [Q_KEY_CODE_O] = 0x18,
+    [Q_KEY_CODE_P] = 0x19,
+    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
+    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
+    [Q_KEY_CODE_RET] = 0x1c,
+
+    [Q_KEY_CODE_A] = 0x1e,
+    [Q_KEY_CODE_S] = 0x1f,
+    [Q_KEY_CODE_D] = 0x20,
+    [Q_KEY_CODE_F] = 0x21,
+    [Q_KEY_CODE_G] = 0x22,
+    [Q_KEY_CODE_H] = 0x23,
+    [Q_KEY_CODE_J] = 0x24,
+    [Q_KEY_CODE_K] = 0x25,
+    [Q_KEY_CODE_L] = 0x26,
+    [Q_KEY_CODE_SEMICOLON] = 0x27,
+    [Q_KEY_CODE_APOSTROPHE] = 0x28,
+    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
+
+    [Q_KEY_CODE_BACKSLASH] = 0x2b,
+    [Q_KEY_CODE_Z] = 0x2c,
+    [Q_KEY_CODE_X] = 0x2d,
+    [Q_KEY_CODE_C] = 0x2e,
+    [Q_KEY_CODE_V] = 0x2f,
+    [Q_KEY_CODE_B] = 0x30,
+    [Q_KEY_CODE_N] = 0x31,
+    [Q_KEY_CODE_M] = 0x32,
+    [Q_KEY_CODE_COMMA] = 0x33,
+    [Q_KEY_CODE_DOT] = 0x34,
+    [Q_KEY_CODE_SLASH] = 0x35,
+
+    [Q_KEY_CODE_ASTERISK] = 0x37,
+
+    [Q_KEY_CODE_SPC] = 0x39,
+    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
+    [Q_KEY_CODE_F1] = 0x3b,
+    [Q_KEY_CODE_F2] = 0x3c,
+    [Q_KEY_CODE_F3] = 0x3d,
+    [Q_KEY_CODE_F4] = 0x3e,
+    [Q_KEY_CODE_F5] = 0x3f,
+    [Q_KEY_CODE_F6] = 0x40,
+    [Q_KEY_CODE_F7] = 0x41,
+    [Q_KEY_CODE_F8] = 0x42,
+    [Q_KEY_CODE_F9] = 0x43,
+    [Q_KEY_CODE_F10] = 0x44,
+    [Q_KEY_CODE_NUM_LOCK] = 0x45,
+    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
+
+    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
+    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
+    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
+    [Q_KEY_CODE_KP_ADD] = 0x4e,
+    [Q_KEY_CODE_KP_ENTER] = 0x9c,
+    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
+    [Q_KEY_CODE_SYSRQ] = 0x54,
+
+    [Q_KEY_CODE_KP_0] = 0x52,
+    [Q_KEY_CODE_KP_1] = 0x4f,
+    [Q_KEY_CODE_KP_2] = 0x50,
+    [Q_KEY_CODE_KP_3] = 0x51,
+    [Q_KEY_CODE_KP_4] = 0x4b,
+    [Q_KEY_CODE_KP_5] = 0x4c,
+    [Q_KEY_CODE_KP_6] = 0x4d,
+    [Q_KEY_CODE_KP_7] = 0x47,
+    [Q_KEY_CODE_KP_8] = 0x48,
+    [Q_KEY_CODE_KP_9] = 0x49,
+
+    [Q_KEY_CODE_LESS] = 0x56,
+
+    [Q_KEY_CODE_F11] = 0x57,
+    [Q_KEY_CODE_F12] = 0x58,
+
+    [Q_KEY_CODE_PRINT] = 0xb7,
+
+    [Q_KEY_CODE_HOME] = 0xc7,
+    [Q_KEY_CODE_PGUP] = 0xc9,
+    [Q_KEY_CODE_PGDN] = 0xd1,
+    [Q_KEY_CODE_END] = 0xcf,
+
+    [Q_KEY_CODE_LEFT] = 0xcb,
+    [Q_KEY_CODE_UP] = 0xc8,
+    [Q_KEY_CODE_DOWN] = 0xd0,
+    [Q_KEY_CODE_RIGHT] = 0xcd,
+
+    [Q_KEY_CODE_INSERT] = 0xd2,
+    [Q_KEY_CODE_DELETE] = 0xd3,
+    [Q_KEY_CODE_MAX] = 0,
+};
+
+static int number_to_qcode[0xff];
+
+int qemu_input_key_value_to_number(const KeyValue *value)
+{
+    if (value->kind == KEY_VALUE_KIND_QCODE) {
+        return qcode_to_number[value->qcode];
+    } else {
+        assert(value->kind == KEY_VALUE_KIND_NUMBER);
+        return value->number;
+    }
+}
+
+int qemu_input_key_value_to_qcode(const KeyValue *value)
+{
+    static int first = true;
+
+    if (first) {
+        int qcode, number;
+        first = false;
+        for (qcode = 0; qcode < Q_KEY_CODE_MAX; qcode++) {
+            number = qcode_to_number[qcode];
+            assert(number < ARRAY_SIZE(number_to_qcode));
+            number_to_qcode[number] = qcode;
+        }
+    }
+
+    if (value->kind == KEY_VALUE_KIND_QCODE) {
+        return value->qcode;
+    } else {
+        assert(value->kind == KEY_VALUE_KIND_NUMBER);
+        return number_to_qcode[value->number];
+    }
+}
+
+int qemu_input_key_value_to_scancode(const KeyValue *value, bool down,
+                                     int *codes)
+{
+    int keycode = qemu_input_key_value_to_number(value);
+    int count = 0;
+
+    if (value->kind == KEY_VALUE_KIND_QCODE &&
+        value->qcode == Q_KEY_CODE_PAUSE) {
+        /* specific case */
+        int v = down ? 0 : 0x80;
+        codes[count++] = 0xe1;
+        codes[count++] = 0x1d | v;
+        codes[count++] = 0x45 | v;
+        return count;
+    }
+    if (keycode & SCANCODE_GREY) {
+        codes[count++] = SCANCODE_EMUL0;
+        keycode &= ~SCANCODE_GREY;
+    }
+    if (!down) {
+        keycode |= SCANCODE_UP;
+    }
+    codes[count++] = keycode;
+
+    return count;
+}
diff --git a/ui/input-legacy.c b/ui/input-legacy.c
index 1aa2605..2a53860 100644
--- a/ui/input-legacy.c
+++ b/ui/input-legacy.c
@@ -60,152 +60,6 @@
 static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers =
     QTAILQ_HEAD_INITIALIZER(mouse_handlers);
 
-static const int key_defs[] = {
-    [Q_KEY_CODE_SHIFT] = 0x2a,
-    [Q_KEY_CODE_SHIFT_R] = 0x36,
-
-    [Q_KEY_CODE_ALT] = 0x38,
-    [Q_KEY_CODE_ALT_R] = 0xb8,
-    [Q_KEY_CODE_ALTGR] = 0x64,
-    [Q_KEY_CODE_ALTGR_R] = 0xe4,
-    [Q_KEY_CODE_CTRL] = 0x1d,
-    [Q_KEY_CODE_CTRL_R] = 0x9d,
-
-    [Q_KEY_CODE_MENU] = 0xdd,
-
-    [Q_KEY_CODE_ESC] = 0x01,
-
-    [Q_KEY_CODE_1] = 0x02,
-    [Q_KEY_CODE_2] = 0x03,
-    [Q_KEY_CODE_3] = 0x04,
-    [Q_KEY_CODE_4] = 0x05,
-    [Q_KEY_CODE_5] = 0x06,
-    [Q_KEY_CODE_6] = 0x07,
-    [Q_KEY_CODE_7] = 0x08,
-    [Q_KEY_CODE_8] = 0x09,
-    [Q_KEY_CODE_9] = 0x0a,
-    [Q_KEY_CODE_0] = 0x0b,
-    [Q_KEY_CODE_MINUS] = 0x0c,
-    [Q_KEY_CODE_EQUAL] = 0x0d,
-    [Q_KEY_CODE_BACKSPACE] = 0x0e,
-
-    [Q_KEY_CODE_TAB] = 0x0f,
-    [Q_KEY_CODE_Q] = 0x10,
-    [Q_KEY_CODE_W] = 0x11,
-    [Q_KEY_CODE_E] = 0x12,
-    [Q_KEY_CODE_R] = 0x13,
-    [Q_KEY_CODE_T] = 0x14,
-    [Q_KEY_CODE_Y] = 0x15,
-    [Q_KEY_CODE_U] = 0x16,
-    [Q_KEY_CODE_I] = 0x17,
-    [Q_KEY_CODE_O] = 0x18,
-    [Q_KEY_CODE_P] = 0x19,
-    [Q_KEY_CODE_BRACKET_LEFT] = 0x1a,
-    [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b,
-    [Q_KEY_CODE_RET] = 0x1c,
-
-    [Q_KEY_CODE_A] = 0x1e,
-    [Q_KEY_CODE_S] = 0x1f,
-    [Q_KEY_CODE_D] = 0x20,
-    [Q_KEY_CODE_F] = 0x21,
-    [Q_KEY_CODE_G] = 0x22,
-    [Q_KEY_CODE_H] = 0x23,
-    [Q_KEY_CODE_J] = 0x24,
-    [Q_KEY_CODE_K] = 0x25,
-    [Q_KEY_CODE_L] = 0x26,
-    [Q_KEY_CODE_SEMICOLON] = 0x27,
-    [Q_KEY_CODE_APOSTROPHE] = 0x28,
-    [Q_KEY_CODE_GRAVE_ACCENT] = 0x29,
-
-    [Q_KEY_CODE_BACKSLASH] = 0x2b,
-    [Q_KEY_CODE_Z] = 0x2c,
-    [Q_KEY_CODE_X] = 0x2d,
-    [Q_KEY_CODE_C] = 0x2e,
-    [Q_KEY_CODE_V] = 0x2f,
-    [Q_KEY_CODE_B] = 0x30,
-    [Q_KEY_CODE_N] = 0x31,
-    [Q_KEY_CODE_M] = 0x32,
-    [Q_KEY_CODE_COMMA] = 0x33,
-    [Q_KEY_CODE_DOT] = 0x34,
-    [Q_KEY_CODE_SLASH] = 0x35,
-
-    [Q_KEY_CODE_ASTERISK] = 0x37,
-
-    [Q_KEY_CODE_SPC] = 0x39,
-    [Q_KEY_CODE_CAPS_LOCK] = 0x3a,
-    [Q_KEY_CODE_F1] = 0x3b,
-    [Q_KEY_CODE_F2] = 0x3c,
-    [Q_KEY_CODE_F3] = 0x3d,
-    [Q_KEY_CODE_F4] = 0x3e,
-    [Q_KEY_CODE_F5] = 0x3f,
-    [Q_KEY_CODE_F6] = 0x40,
-    [Q_KEY_CODE_F7] = 0x41,
-    [Q_KEY_CODE_F8] = 0x42,
-    [Q_KEY_CODE_F9] = 0x43,
-    [Q_KEY_CODE_F10] = 0x44,
-    [Q_KEY_CODE_NUM_LOCK] = 0x45,
-    [Q_KEY_CODE_SCROLL_LOCK] = 0x46,
-
-    [Q_KEY_CODE_KP_DIVIDE] = 0xb5,
-    [Q_KEY_CODE_KP_MULTIPLY] = 0x37,
-    [Q_KEY_CODE_KP_SUBTRACT] = 0x4a,
-    [Q_KEY_CODE_KP_ADD] = 0x4e,
-    [Q_KEY_CODE_KP_ENTER] = 0x9c,
-    [Q_KEY_CODE_KP_DECIMAL] = 0x53,
-    [Q_KEY_CODE_SYSRQ] = 0x54,
-
-    [Q_KEY_CODE_KP_0] = 0x52,
-    [Q_KEY_CODE_KP_1] = 0x4f,
-    [Q_KEY_CODE_KP_2] = 0x50,
-    [Q_KEY_CODE_KP_3] = 0x51,
-    [Q_KEY_CODE_KP_4] = 0x4b,
-    [Q_KEY_CODE_KP_5] = 0x4c,
-    [Q_KEY_CODE_KP_6] = 0x4d,
-    [Q_KEY_CODE_KP_7] = 0x47,
-    [Q_KEY_CODE_KP_8] = 0x48,
-    [Q_KEY_CODE_KP_9] = 0x49,
-
-    [Q_KEY_CODE_LESS] = 0x56,
-
-    [Q_KEY_CODE_F11] = 0x57,
-    [Q_KEY_CODE_F12] = 0x58,
-
-    [Q_KEY_CODE_PRINT] = 0xb7,
-
-    [Q_KEY_CODE_HOME] = 0xc7,
-    [Q_KEY_CODE_PGUP] = 0xc9,
-    [Q_KEY_CODE_PGDN] = 0xd1,
-    [Q_KEY_CODE_END] = 0xcf,
-
-    [Q_KEY_CODE_LEFT] = 0xcb,
-    [Q_KEY_CODE_UP] = 0xc8,
-    [Q_KEY_CODE_DOWN] = 0xd0,
-    [Q_KEY_CODE_RIGHT] = 0xcd,
-
-    [Q_KEY_CODE_INSERT] = 0xd2,
-    [Q_KEY_CODE_DELETE] = 0xd3,
-#ifdef NEED_CPU_H
-#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
-    [Q_KEY_CODE_STOP] = 0xf0,
-    [Q_KEY_CODE_AGAIN] = 0xf1,
-    [Q_KEY_CODE_PROPS] = 0xf2,
-    [Q_KEY_CODE_UNDO] = 0xf3,
-    [Q_KEY_CODE_FRONT] = 0xf4,
-    [Q_KEY_CODE_COPY] = 0xf5,
-    [Q_KEY_CODE_OPEN] = 0xf6,
-    [Q_KEY_CODE_PASTE] = 0xf7,
-    [Q_KEY_CODE_FIND] = 0xf8,
-    [Q_KEY_CODE_CUT] = 0xf9,
-    [Q_KEY_CODE_LF] = 0xfa,
-    [Q_KEY_CODE_HELP] = 0xfb,
-    [Q_KEY_CODE_META_L] = 0xfc,
-    [Q_KEY_CODE_META_R] = 0xfd,
-    [Q_KEY_CODE_COMPOSE] = 0xfe,
-#endif
-#endif
-    [Q_KEY_CODE_MAX] = 0,
-};
-
 int index_from_key(const char *key)
 {
     int i;
@@ -220,48 +74,44 @@
     return i;
 }
 
-static int *keycodes;
-static int keycodes_size;
+static KeyValue **keyvalues;
+static int keyvalues_size;
 static QEMUTimer *key_timer;
 
-static int keycode_from_keyvalue(const KeyValue *value)
+static void free_keyvalues(void)
 {
-    if (value->kind == KEY_VALUE_KIND_QCODE) {
-        return key_defs[value->qcode];
-    } else {
-        assert(value->kind == KEY_VALUE_KIND_NUMBER);
-        return value->number;
-    }
-}
-
-static void free_keycodes(void)
-{
-    g_free(keycodes);
-    keycodes = NULL;
-    keycodes_size = 0;
+    g_free(keyvalues);
+    keyvalues = NULL;
+    keyvalues_size = 0;
 }
 
 static void release_keys(void *opaque)
 {
-    while (keycodes_size > 0) {
-        qemu_input_event_send_key_number(NULL, keycodes[--keycodes_size],
-                                         false);
+    while (keyvalues_size > 0) {
+        qemu_input_event_send_key(NULL, keyvalues[--keyvalues_size],
+                                  false);
     }
 
-    free_keycodes();
+    free_keyvalues();
+}
+
+static KeyValue *copy_key_value(KeyValue *src)
+{
+    KeyValue *dst = g_new(KeyValue, 1);
+    memcpy(dst, src, sizeof(*src));
+    return dst;
 }
 
 void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
                   Error **errp)
 {
-    int keycode;
     KeyValueList *p;
 
     if (!key_timer) {
         key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL);
     }
 
-    if (keycodes != NULL) {
+    if (keyvalues != NULL) {
         timer_del(key_timer);
         release_keys(NULL);
     }
@@ -271,51 +121,33 @@
     }
 
     for (p = keys; p != NULL; p = p->next) {
-        /* key down events */
-        keycode = keycode_from_keyvalue(p->value);
-        if (keycode < 0x01 || keycode > 0xff) {
-            error_setg(errp, "invalid hex keycode 0x%x", keycode);
-            free_keycodes();
-            return;
-        }
+        qemu_input_event_send_key(NULL, copy_key_value(p->value), true);
 
-        qemu_input_event_send_key_number(NULL, keycode, true);
-
-        keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1));
-        keycodes[keycodes_size++] = keycode;
+        keyvalues = g_realloc(keyvalues, sizeof(KeyValue *) *
+                              (keyvalues_size + 1));
+        keyvalues[keyvalues_size++] = copy_key_value(p->value);
     }
 
     /* delayed key up events */
     timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
-                   muldiv64(get_ticks_per_sec(), hold_time, 1000));
+              muldiv64(get_ticks_per_sec(), hold_time, 1000));
 }
 
 static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
                              InputEvent *evt)
 {
     QEMUPutKbdEntry *entry = (QEMUPutKbdEntry *)dev;
-    int keycode = keycode_from_keyvalue(evt->key->key);
+    int scancodes[3], i, count;
 
     if (!entry || !entry->put_kbd) {
         return;
     }
-    if (evt->key->key->kind == KEY_VALUE_KIND_QCODE &&
-        evt->key->key->qcode == Q_KEY_CODE_PAUSE) {
-        /* specific case */
-        int v = evt->key->down ? 0 : 0x80;
-        entry->put_kbd(entry->opaque, 0xe1);
-        entry->put_kbd(entry->opaque, 0x1d | v);
-        entry->put_kbd(entry->opaque, 0x45 | v);
-        return;
+    count = qemu_input_key_value_to_scancode(evt->key->key,
+                                             evt->key->down,
+                                             scancodes);
+    for (i = 0; i < count; i++) {
+        entry->put_kbd(entry->opaque, scancodes[i]);
     }
-    if (keycode & SCANCODE_GREY) {
-        entry->put_kbd(entry->opaque, SCANCODE_EMUL0);
-        keycode &= ~SCANCODE_GREY;
-    }
-    if (!evt->key->down) {
-        keycode |= SCANCODE_UP;
-    }
-    entry->put_kbd(entry->opaque, keycode);
 }
 
 static QemuInputHandler legacy_kbd_handler = {
diff --git a/ui/input.c b/ui/input.c
index 1ed0e78..fc91fba 100644
--- a/ui/input.c
+++ b/ui/input.c
@@ -39,6 +39,13 @@
     qemu_input_check_mode_change();
 }
 
+void qemu_input_handler_deactivate(QemuInputHandlerState *s)
+{
+    QTAILQ_REMOVE(&handlers, s, node);
+    QTAILQ_INSERT_TAIL(&handlers, s, node);
+    qemu_input_check_mode_change();
+}
+
 void qemu_input_handler_unregister(QemuInputHandlerState *s)
 {
     QTAILQ_REMOVE(&handlers, s, node);