diff options
41 files changed, 1618 insertions, 859 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 97c9fa1f7f..1de05f0525 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -659,6 +659,12 @@ S: Supported 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 52355b8bce..d649424e5c 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 @@ typedef struct IscsiLun { 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 @@ typedef struct IscsiAIOCB { #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 @@ static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors, 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 @@ retry: 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 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs, 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 @@ iscsi_getlength(BlockDriverState *bs) 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 @@ retry: return -EIO; } + iscsi_allocationmap_clear(iscsilun, sector_num, nb_sectors); + return 0; } @@ -809,13 +889,14 @@ coroutine_fn iscsi_co_write_zeroes(BlockDriverState *bs, int64_t sector_num, 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 @@ retry: 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 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, 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 @@ static void iscsi_close(BlockDriverState *bs) 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 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset) 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 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) 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 26312d84e8..dea0d505a7 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -48,7 +48,7 @@ The QAPI schema definitions can be modularized using the 'include' directive: { '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 @@ node structure that can be used to chain together a list of such types in 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 @@ created code. 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 @@ Example: 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...] + + #ifndef EXAMPLE_QAPI_TYPES_H + #define EXAMPLE_QAPI_TYPES_H - #include "qapi/qapi-types-core.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 @@ $(prefix)qapi-visit.h: declarations for previously mentioned visitor 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); + visit_start_list(m, name, &err); + if (err) { + goto out; + } - for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) { + 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, errp); + visit_type_UserDefOne(m, &native_i->value, NULL, &err); } - visit_end_list(m, errp); + 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 @@ $(prefix)qmp-commands.h: Function prototypes for the QMP commands Example: - mdroth@illuin:~/w/qemu2.git$ cat qapi-generated/example-qmp-marshal.c - /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ - - #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" + $ cat qapi-generated/example-qmp-marshal.c +[Uninteresting stuff omitted...] - #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); + 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", errp); + visit_type_UserDefOne(v, &ret_in, "unused", NULL); qapi_dealloc_visitor_cleanup(md); - - - *ret_out = qmp_output_get_qobject(mo); } - 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); + visit_type_UserDefOne(v, &arg1, "arg1", &local_err); + if (local_err) { + goto out; + } - if (error_is_set(errp)) { + retval = qmp_my_command(arg1, &local_err); + if (local_err) { goto out; } - retval = qmp_my_command(arg1, errp); - qmp_marshal_output_my_command(retval, ret, errp); + + 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 8971f1b153..2e462c04aa 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -556,6 +556,7 @@ ETEXI .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 @@ ETEXI { .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 @@ ETEXI .params = "id", .help = "remove host network device", .mhandler.cmd = hmp_netdev_del, + .command_completion = netdev_del_completion, }, STEXI @@ -1339,6 +1342,7 @@ ETEXI .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 @@ ETEXI .params = "args", .help = "add chardev", .mhandler.cmd = hmp_chardev_add, + .command_completion = chardev_add_completion, }, STEXI @@ -1638,6 +1643,7 @@ ETEXI .params = "id", .help = "remove chardev", .mhandler.cmd = hmp_chardev_remove, + .command_completion = chardev_remove_completion, }, STEXI @@ -1393,6 +1393,7 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict) 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 @@ void hmp_object_add(Monitor *mon, const QDict *qdict) 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); @@ -97,5 +97,11 @@ void object_add_completion(ReadLineState *rs, int nb_args, const char *str); 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 6397f6f282..d9a20aa931 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 @@ typedef struct ChannelState { 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 @@ MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, 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; - - 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; + ChannelState *s = (ChannelState *)dev; + int qcode, keycode; + + 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 */ + } + } + } + + keycode = qcode_to_keycode[qcode]; + if (!evt->key->down) { + keycode |= 0x80; } - trace_escc_sunkbd_event_out(ch); - put_queue(s, ch | release); + 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 @@ static void handle_kbd_command(ChannelState *s, int val) 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 @@ static int escc_init1(SysBusDevice *dev) "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 34120796b1..22b77dfe5c 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 @@ void ps2_queue(void *opaque, int b) 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 @@ static void ps2_put_keycode(void *opaque, int keycode) 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 @@ static void ps2_mouse_send_packet(PS2MouseState *s) 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) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + 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; } +} - if (!(s->mouse_status & MOUSE_STATUS_REMOTE) && - (s->common.queue.count < (PS2_QUEUE_SIZE - 16))) { - for(;;) { +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)) { + 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 @@ static void ps2_mouse_event(void *opaque, 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 @@ static void ps2_common_reset(PS2State *s) 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 const VMStateDescription vmstate_ps2_keyboard_ledstate = { 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 const VMStateDescription vmstate_ps2_keyboard = { } }; +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 const VMStateDescription vmstate_ps2_mouse = { } }; +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 @@ void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg) 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 @@ void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg) 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 e6e1ffd1bb..baee46f981 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 @@ static bool megasas_use_queue64(MegasasState *s) 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 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) 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 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) 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 @@ static const struct dcmd_cmd_tbl_t { 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 @@ static void megasas_mmio_write(void *opaque, hwaddr addr, 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 @@ static const VMStateDescription vmstate_megasas = { .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 @@ static void megasas_scsi_uninit(PCIDevice *d) { 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 @@ static int megasas_scsi_init(PCIDevice *dev) 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 @@ static int megasas_scsi_init(PCIDevice *dev) 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 @@ static int megasas_scsi_init(PCIDevice *dev) 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 @@ static int megasas_scsi_init(PCIDevice *dev) 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 @@ static Property megasas_properties[] = { 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 @@ static void megasas_class_init(ObjectClass *oc, void *data) 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 cd8355badf..a3034f6239 100644 --- a/hw/scsi/mfi.h +++ b/hw/scsi/mfi.h @@ -164,6 +164,7 @@ typedef enum { 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 @@ typedef enum { 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 48a28ae199..4bcef551a6 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -2458,21 +2458,27 @@ static int scsi_block_initfn(SCSIDevice *dev) 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 8d92e0da15..3733d2c36c 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -394,6 +394,7 @@ static void scsi_destroy(SCSIDevice *s) static int scsi_generic_initfn(SCSIDevice *s) { + int rc; int sg_version; struct sg_scsi_id scsiid; @@ -412,8 +413,11 @@ static int scsi_generic_initfn(SCSIDevice *s) } /* 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 8509309fa7..df54546562 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -793,19 +793,46 @@ static const MemoryRegionOps cmos_ops = { 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, ¤t_tm); - visit_start_struct(v, NULL, "struct tm", name, 0, errp); - visit_type_int32(v, ¤t_tm.tm_year, "tm_year", errp); - visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", errp); - visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", errp); - visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", errp); - visit_type_int32(v, ¤t_tm.tm_min, "tm_min", errp); - visit_type_int32(v, ¤t_tm.tm_sec, "tm_sec", errp); + visit_start_struct(v, NULL, "struct tm", name, 0, &err); + if (err) { + goto out; + } + visit_type_int32(v, ¤t_tm.tm_year, "tm_year", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, ¤t_tm.tm_mon, "tm_mon", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, ¤t_tm.tm_mday, "tm_mday", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, ¤t_tm.tm_hour, "tm_hour", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, ¤t_tm.tm_min, "tm_min", &err); + if (err) { + goto out_end; + } + visit_type_int32(v, ¤t_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 971a921777..bf2b588b24 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -108,6 +108,7 @@ static void balloon_stats_poll_cb(void *opaque) 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 @@ static void balloon_stats_get_all(Object *obj, struct Visitor *v, 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, "guest-stats", name, 0, &err); + if (err) { + goto out; + } + visit_type_int(v, &s->stats_last_update, "last-update", &err); + if (err) { + goto out_end; + } - visit_start_struct(v, NULL, NULL, "stats", 0, errp); - for (i = 0; i < VIRTIO_BALLOON_S_NR; i++) { + 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], - errp); + &err); } - visit_end_struct(v, errp); - - visit_end_struct(v, errp); + 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 f3fa420245..ecc0183196 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -42,13 +42,9 @@ struct Visitor 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 29da211b47..4a0178fa46 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -39,9 +39,8 @@ void visit_end_implicit_struct(Visitor *v, Error **errp); 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 4976f3da2c..3d3d487f18 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -27,6 +27,7 @@ struct QemuInputHandler { 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 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool down); 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); @@ -4269,6 +4269,55 @@ static const char *next_arg_type(const char *typestr) 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 @@ static void device_del_bus_completion(ReadLineState *rs, BusState *bus, } } +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 @@ void object_del_completion(ReadLineState *rs, int nb_args, const char *str) 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 @@ static void monitor_find_completion_by_table(Monitor *mon, 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); } @@ -633,7 +633,7 @@ int qemu_find_net_clients_except(const char *id, NetClientState **ncs, 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 87c1c789c9..16382e7a65 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -484,8 +484,7 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) 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 @@ opts_visitor_new(const QemuOpts *opts) /* 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 6451a21a28..55f8d4068c 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_end_implicit_struct(Visitor *v, Error **errp) 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_get_next_type(Visitor *v, int *obj, const int *qtypes, 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 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[], 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 a2bed1ef10..d8612062f1 100644 --- a/qapi/qmp-input-visitor.c +++ b/qapi/qmp-input-visitor.c @@ -286,8 +286,8 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name, } } -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 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj) 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 793548ae3a..5780944792 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -120,8 +120,8 @@ static void parse_type_number(Visitor *v, double *obj, const char *name, *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 @@ StringInputVisitor *string_input_visitor_new(const char *str) 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 8d9096f65c..386f17ef44 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 @@ def generate_command_decl(name, args, ret_type): ''', 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 @@ def gen_sync_call(name, args, ret_type, indent=0): 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 @@ if (!error_is_set(errp)) { 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 @@ bool has_%(argname)s = false; 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 @@ def gen_visitor_input_block(args, obj, dealloc=False): 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 @@ def gen_marshal_output(name, args, ret_type, middle_mode): 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 @@ def gen_marshal_input(name, args, ret_type, middle_mode): 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 @@ def gen_marshal_input(name, args, ret_type, middle_mode): %(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 c6579beed5..06a79f1631 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 @@ def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = 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 @@ static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error * 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 @@ if (!err) { 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 @@ visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err); 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 @@ visit_end_optional(m, &err); 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 (!error_is_set(errp)) { 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; + if (!err) { + if (*obj) { + visit_type_%(name)s_fields(m, obj, errp); + } + visit_end_struct(m, &err); } + error_propagate(errp, err); ''', name=full_name) - pop_indent() - ret += mcgen(''' - /* Always call end_struct if start_struct succeeded. */ - visit_end_struct(m, &err); - } - error_propagate(errp, err); -} -''') return ret def generate_visit_struct(expr): @@ -154,9 +191,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** ''', name=name) - push_indent() ret += generate_visit_struct_body("", name, members) - pop_indent() ret += mcgen(''' } @@ -168,24 +203,26 @@ def generate_visit_list(name, members): 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 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** { 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 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** 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 @@ def generate_visit_union(expr): 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 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** 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 @@ fdecl.write(mcgen(''' /* 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 ec806aabeb..0265b404dd 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -73,13 +73,18 @@ class QAPIExprError(Exception): 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 @@ class QAPISchema: 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 6b8b6f273a..9f7ca61ae3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -193,7 +193,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ 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 0000000000..6bfffdfd55 --- /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 0000000000..e69de29bb2 --- /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 0000000000..573541ac97 --- /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 0000000000..ec329dde58 --- /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 0000000000..4ce3dcf12f --- /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 449d285e56..0f770034b1 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -72,14 +72,30 @@ typedef struct TestStruct static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) { - visit_start_struct(v, (void **)obj, "TestStruct", 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, "TestStruct", 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 void test_validate_struct(TestInputVisitorData *data, diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index a58a3e6fdb..1c8e87295c 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -199,16 +199,24 @@ static void visit_type_TestStruct(Visitor *v, TestStruct **obj, 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 2580f3debf..9c154581d7 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -176,14 +176,30 @@ typedef struct TestStruct static void visit_type_TestStruct(Visitor *v, TestStruct **obj, const char *name, Error **errp) { - visit_start_struct(v, (void **)obj, "TestStruct", 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, "TestStruct", 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 void test_visitor_out_struct(TestOutputVisitorData *data, diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index 8166cf1b05..74d6481992 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -195,13 +195,29 @@ typedef struct TestStruct 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 2c5b30763e..b6d289d720 100644 --- a/trace-events +++ b/trace-events @@ -688,17 +688,21 @@ megasas_dcmd_ld_get_list(int cmd, int num, int max) "scmd %d: DCMD LD get list: 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_writeb_data(char channel, uint32_t val) "Write channel %c, ch %d" 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 4af420bfa8..6afb52a93c 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -7,7 +7,8 @@ vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o 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 0000000000..6da4495103 --- /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 1aa2605b75..2a538607a2 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -60,152 +60,6 @@ static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = 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 @@ int index_from_key(const char *key) 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 @@ void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, } 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_number(NULL, keycode, true); + qemu_input_event_send_key(NULL, copy_key_value(p->value), 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; - } - if (keycode & SCANCODE_GREY) { - entry->put_kbd(entry->opaque, SCANCODE_EMUL0); - keycode &= ~SCANCODE_GREY; - } - if (!evt->key->down) { - keycode |= SCANCODE_UP; + 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]); } - entry->put_kbd(entry->opaque, keycode); } static QemuInputHandler legacy_kbd_handler = { diff --git a/ui/input.c b/ui/input.c index 1ed0e783b1..fc91fba83c 100644 --- a/ui/input.c +++ b/ui/input.c @@ -39,6 +39,13 @@ void qemu_input_handler_activate(QemuInputHandlerState *s) 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); |