@@ -606,8 +606,7 @@ ETEXI
.args_type = "device:O",
.params = "driver[,prop=value][,...]",
.help = "add device, like -device on the command line",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_device_add,
+ .mhandler.cmd = hmp_device_add,
},
STEXI
@@ -934,3 +934,27 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
qemu_mod_timer(status->timer, qemu_get_clock_ms(rt_clock));
}
}
+
+void hmp_device_add(Monitor *mon, const QDict *qdict)
+{
+ KeyValuesList *opts_list = NULL;
+ const QDictEntry *entry;
+ Error *err = NULL;
+
+ for (entry = qdict_first(qdict); entry; entry = qdict_next(qdict, entry)) {
+ const char *key = qdict_entry_key(entry);
+ const char *value = qstring_get_str(qobject_to_qstring(qdict_entry_value(entry)));
+ KeyValuesList *kv;
+
+ kv = g_malloc0(sizeof(*kv));
+ kv->value = g_malloc0(sizeof(*kv->value));
+ kv->value->key = g_strdup(key);
+ kv->value->value = g_strdup(value);
+ kv->next = opts_list;
+ opts_list = kv;
+ }
+
+ qmp_device_add(opts_list, &err);
+ qapi_free_KeyValuesList(opts_list);
+ hmp_handle_error(mon, &err);
+}
@@ -60,5 +60,6 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict);
void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict);
void hmp_block_job_cancel(Monitor *mon, const QDict *qdict);
void hmp_migrate(Monitor *mon, const QDict *qdict);
+void hmp_device_add(Monitor *mon, const QDict *qdict);
#endif
@@ -19,6 +19,7 @@
#include "qdev.h"
#include "monitor.h"
+#include "qmp-commands.h"
/*
* Aliases were a bad idea from the start. Let's keep them
@@ -388,7 +389,7 @@ static BusState *qbus_find(const char *path)
}
}
-DeviceState *qdev_device_add(QemuOpts *opts)
+DeviceState *qdev_device_add(QemuOpts *opts, Error **errp)
{
ObjectClass *obj;
DeviceClass *k;
@@ -398,7 +399,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
driver = qemu_opt_get(opts, "driver");
if (!driver) {
- qerror_report(QERR_MISSING_PARAMETER, "driver");
+ error_set(errp, QERR_MISSING_PARAMETER, "driver");
return NULL;
}
@@ -414,7 +415,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
}
if (!obj) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "device type");
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, "driver", "device type");
return NULL;
}
@@ -428,20 +429,18 @@ DeviceState *qdev_device_add(QemuOpts *opts)
return NULL;
}
if (bus->info != k->bus_info) {
- qerror_report(QERR_BAD_BUS_FOR_DEVICE,
- driver, bus->info->name);
+ error_set(errp, QERR_BAD_BUS_FOR_DEVICE, driver, bus->info->name);
return NULL;
}
} else {
bus = qbus_find_recursive(sysbus_get_default(), NULL, k->bus_info);
if (!bus) {
- qerror_report(QERR_NO_BUS_FOR_DEVICE,
- driver, k->bus_info->name);
+ error_set(errp, QERR_NO_BUS_FOR_DEVICE, driver, k->bus_info->name);
return NULL;
}
}
if (qdev_hotplug && !bus->allow_hotplug) {
- qerror_report(QERR_BUS_NO_HOTPLUG, bus->name);
+ error_set(errp, QERR_BUS_NO_HOTPLUG, bus->name);
return NULL;
}
@@ -463,7 +462,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
return NULL;
}
if (qdev_init(qdev) < 0) {
- qerror_report(QERR_DEVICE_INIT_FAILED, driver);
+ error_set(errp, QERR_DEVICE_INIT_FAILED, driver);
return NULL;
}
if (qdev->id) {
@@ -555,23 +554,58 @@ void do_info_qdm(Monitor *mon)
object_class_foreach(qdev_print_devinfo, TYPE_DEVICE, false, NULL);
}
-int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data)
+void qmp_device_add(KeyValuesList *opts, Error **errp)
{
- QemuOpts *opts;
+ Error *local_err = NULL;
+ QemuOptsList *opts_list;
+ QemuOpts *qopts = NULL;
+ const char *id = NULL;
+ KeyValuesList *kv;
+
+ for (kv = opts; kv; kv = kv->next) {
+ if (!strcmp(kv->value->key, "id")) {
+ id = kv->value->key;
+ break;
+ }
+ }
- opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict);
- if (!opts) {
- return -1;
+ opts_list = qemu_find_opts_err("device", &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
+ }
+
+ qopts = qemu_opts_create(opts_list, id, 1, &local_err);
+ if (local_err) {
+ goto exit_err;
+ }
+
+ for (kv = opts; kv; kv = kv->next) {
+ qemu_opt_set_err(qopts, kv->value->key, kv->value->value, &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
+ }
}
+
+#if 0
+ FIXME: move the help to the HMP counterpart
if (!monitor_cur_is_qmp() && qdev_device_help(opts)) {
qemu_opts_del(opts);
return 0;
}
- if (!qdev_device_add(opts)) {
- qemu_opts_del(opts);
- return -1;
+#endif
+
+ qdev_device_add(qopts, &local_err);
+ if (error_is_set(&local_err)) {
+ goto exit_err;
}
- return 0;
+
+ return;
+
+exit_err:
+ if (qopts) {
+ qemu_opts_del(qopts);
+ }
+ error_propagate(errp, local_err);
}
int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
@@ -143,7 +143,7 @@ DeviceState *qdev_create(BusState *bus, const char *name);
DeviceState *qdev_try_create(BusState *bus, const char *name);
bool qdev_exists(const char *name);
int qdev_device_help(QemuOpts *opts);
-DeviceState *qdev_device_add(QemuOpts *opts);
+DeviceState *qdev_device_add(QemuOpts *opts, Error **errp);
int qdev_init(DeviceState *dev) QEMU_WARN_UNUSED_RESULT;
void qdev_init_nofail(DeviceState *dev);
void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
@@ -1716,3 +1716,26 @@
# Since: 0.14.0
##
{ 'type': 'KeyValues', 'data': {'key': 'str', 'value': 'str'} }
+
+##
+# @device_add:
+#
+# Add a new device to the guest
+#
+# @device-optoins: a list of KeyValues of options specific to each device.
+# There are two driver indepedent options: 'driver' (which is
+# actually _required_) and 'id'.
+#
+# Returns: Nothing on success
+# If @driver is not a valid device type, DeviceNotFound
+# If @opts contains an invalid parameter for this device,
+# InvalidParameter
+#
+# Notes: The semantics of @opts is not well defined. Future commands will be
+# introduced that provide stronger typing for device creation.
+# This command performs hotplug of devices for guests. When the command
+# returns, the device may not be visible to the guest.
+#
+# Since: 0.14.0
+##
+{ 'command': 'device_add', 'data': {'device-options': '**' } }
@@ -279,8 +279,7 @@ EQMP
.args_type = "device:O",
.params = "driver[,prop=value][,...]",
.help = "add device, like -device on the command line",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_device_add,
+ .mhandler.cmd_new = qmp_marshal_input_device_add,
},
SQMP
@@ -1821,7 +1821,8 @@ static int device_init_func(QemuOpts *opts, void *opaque)
{
DeviceState *dev;
- dev = qdev_device_add(opts);
+ /* FIXME: */
+ dev = qdev_device_add(opts, NULL);
if (!dev)
return -1;
return 0;