@@ -39,7 +39,6 @@ DeviceInfo *device_info_list;
static BusState *qbus_find_recursive(BusState *bus, const char *name,
const BusInfo *info);
-static BusState *qbus_find(const char *path, bool report_errors);
/* Register a new device type. */
void qdev_register(DeviceInfo *info)
@@ -514,7 +513,7 @@ static DeviceState *qdev_find_id_recursive(BusState *bus, const char *id)
return qdev_iterate_recursive(bus, find_id_callback, (void *)id);
}
-static int qdev_instance_no(DeviceState *dev)
+int qdev_instance_no(DeviceState *dev)
{
struct DeviceState *sibling;
int instance = 0;
@@ -611,7 +610,7 @@ static DeviceState *qbus_find_dev(BusState *bus, const char *elem)
return NULL;
}
-static BusState *qbus_find(const char *path, bool report_errors)
+BusState *qbus_find(const char *path, bool report_errors)
{
DeviceState *dev;
BusState *bus = main_system_bus;
@@ -678,7 +677,7 @@ static BusState *qbus_find(const char *path, bool report_errors)
}
}
-static DeviceState *qdev_find(const char *path)
+DeviceState *qdev_find(const char *path, bool report_errors)
{
const char *dev_name;
DeviceState *dev;
@@ -688,7 +687,7 @@ static DeviceState *qdev_find(const char *path)
/* search for unique ID recursively if path is not absolute */
if (path[0] != '/') {
dev = qdev_find_id_recursive(main_system_bus, path);
- if (!dev) {
+ if (!dev && report_errors) {
qerror_report(QERR_DEVICE_NOT_FOUND, path);
}
return dev;
@@ -702,8 +701,10 @@ static DeviceState *qdev_find(const char *path)
bus = qbus_find(bus_path, false);
qemu_free(bus_path);
if (!bus) {
- /* retry with full path to generate correct error message */
- bus = qbus_find(path, true);
+ if (report_errors) {
+ /* retry with full path to generate correct error message */
+ bus = qbus_find(path, report_errors);
+ }
if (!bus) {
return NULL;
}
@@ -711,7 +712,7 @@ static DeviceState *qdev_find(const char *path)
}
dev = qbus_find_dev(bus, dev_name);
- if (!dev) {
+ if (!dev && report_errors) {
qerror_report(QERR_DEVICE_NOT_FOUND, dev_name);
qbus_list_dev(bus);
}
@@ -880,7 +881,7 @@ int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
const char *path = qdict_get_str(qdict, "device");
DeviceState *dev;
- dev = qdev_find(path);
+ dev = qdev_find(path, true);
if (!dev) {
return -1;
}
@@ -171,6 +171,8 @@ CharDriverState *qdev_init_chardev(DeviceState *dev);
BusState *qdev_get_parent_bus(DeviceState *dev);
void *qdev_iterate_recursive(BusState *bus, qdev_iteratefn callback,
void *opaque);
+DeviceState *qdev_find(const char *path, bool report_errors);
+int qdev_instance_no(DeviceState *dev);
/*** BUS API. ***/
@@ -178,6 +180,7 @@ void qbus_create_inplace(BusState *bus, BusInfo *info,
DeviceState *parent, const char *name);
BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name);
void qbus_free(BusState *bus);
+BusState *qbus_find(const char *path, bool report_errors);
#define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev)
@@ -64,12 +64,14 @@
*
* 'F' filename
* 'B' block device name
+ * 'q' qdev bus path
+ * 'Q' qdev device path
* 's' string (accept optional quote)
* 'O' option string of the form NAME=VALUE,...
* parsed according to QemuOptsList given by its name
* Example: 'device:O' uses qemu_device_opts.
* Command completion for specific keys can be requested via
- * appending '(NAME:TYPE,...)' with 'F', 'B' as type.
+ * appending '(NAME:TYPE,...)' with 'F', 'B', 'q', 'Q' as type.
* Example: 'device:O(bus:Q)' to expand 'bus=...' as qtree path.
* Restriction: only lists with empty desc are supported
* TODO lift the restriction
@@ -3318,6 +3320,8 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
switch(c) {
case 'F':
case 'B':
+ case 'q':
+ case 'Q':
case 's':
{
int ret;
@@ -3342,6 +3346,14 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
monitor_printf(mon, "%s: block device name expected\n",
cmdname);
break;
+ case 'q':
+ monitor_printf(mon, "%s: qdev bus path expected\n",
+ cmdname);
+ break;
+ case 'Q':
+ monitor_printf(mon, "%s: qdev device path expected\n",
+ cmdname);
+ break;
default:
monitor_printf(mon, "%s: string expected\n", cmdname);
break;
@@ -3833,6 +3845,99 @@ static void block_completion_it(void *opaque, BlockDriverState *bs)
}
}
+static void add_qdev_completion(const char *parent_path, const char *name,
+ bool trailing_slash)
+{
+ size_t parent_len = strlen(parent_path);
+ size_t name_len = strlen(name);
+ char *completion = qemu_malloc(parent_len + name_len + 2);
+
+ memcpy(completion, parent_path, parent_len);
+ memcpy(completion + parent_len, name, name_len);
+ if (trailing_slash) {
+ completion[parent_len + name_len] = '/';
+ completion[parent_len + name_len + 1] = 0;
+ } else {
+ completion[parent_len + name_len] = 0;
+ }
+ readline_add_completion(cur_mon->rs, completion);
+
+ qemu_free(completion);
+}
+
+static void *inspect_qdev_id(DeviceState *dev, void *opaque)
+{
+ const char *input = opaque;
+
+ if (dev->id && strncmp(dev->id, input, strlen(input)) == 0) {
+ add_qdev_completion("", dev->id, false);
+ }
+ return NULL;
+}
+
+static void qdev_completion(const char *input, bool find_bus)
+{
+ size_t parent_len, name_len;
+ char *parent_path;
+ const char *p;
+ char *name;
+ DeviceState *dev;
+ BusState *bus;
+
+ p = strrchr(input, '/');
+ if (!p) {
+ if (*input == '\0') {
+ readline_add_completion(cur_mon->rs, "/");
+ }
+ if (!find_bus) {
+ qdev_iterate_recursive(NULL, inspect_qdev_id, (void *)input);
+ }
+ return;
+ }
+
+ p++;
+ parent_path = qemu_strndup(input, p - input);
+ bus = qbus_find(parent_path, false);
+
+ if (bus) {
+ parent_len = strlen(parent_path);
+ name_len = strlen(bus->name);
+ if (bus->parent && strncmp(bus->name, p, strlen(p)) == 0 &&
+ (parent_len - 1 < name_len ||
+ strncmp(parent_path + parent_len - 1 - name_len, bus->name,
+ name_len) != 0)) {
+ add_qdev_completion(parent_path, bus->name, true);
+ }
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
+ name_len = strlen(dev->info->name) + 16;
+ name = qemu_malloc(name_len);
+ snprintf(name, name_len, "%s.%d", dev->info->name,
+ qdev_instance_no(dev));
+ if (strncmp(name, p, strlen(p)) == 0) {
+ if (!find_bus) {
+ add_qdev_completion(parent_path, name, false);
+ }
+ if (!QTAILQ_EMPTY(&dev->child_bus)) {
+ add_qdev_completion(parent_path, name, true);
+ }
+ }
+ qemu_free(name);
+ }
+ } else {
+ parent_path[strlen(parent_path) - 1] = 0;
+ dev = qdev_find(parent_path, false);
+ if (dev) {
+ parent_path[strlen(parent_path)] = '/';
+ QTAILQ_FOREACH(bus, &dev->child_bus, sibling) {
+ if (strncmp(bus->name, p, strlen(p)) == 0) {
+ add_qdev_completion(parent_path, bus->name, true);
+ }
+ }
+ }
+ }
+ qemu_free(parent_path);
+}
+
/* NOTE: this parser is an approximate form of the real command parser */
static void parse_cmdline(const char *cmdline,
int *pnb_args, char **args)
@@ -3878,6 +3983,12 @@ static bool process_completion_type(char type, const char *str)
readline_set_completion_index(cur_mon->rs, strlen(str));
bdrv_iterate(block_completion_it, (void *)str);
return true;
+ case 'q':
+ case 'Q':
+ /* qdev bus/device path completion */
+ readline_set_completion_index(cur_mon->rs, strlen(str));
+ qdev_completion(str, (type == 'q'));
+ return true;
default:
return false;
}
@@ -4048,6 +4159,8 @@ static int check_arg(const CmdArgs *cmd_args, QDict *args)
switch (cmd_args->type) {
case 'F':
case 'B':
+ case 'q':
+ case 'Q':
case 's':
if (qobject_type(value) != QTYPE_QSTRING) {
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "string");
@@ -660,7 +660,7 @@ ETEXI
{
.name = "device_add",
- .args_type = "device:O(drive:B)",
+ .args_type = "device:O(bus:q,drive:B)",
.params = "driver[,prop=value][,...]",
.help = "add device, like -device on the command line",
.user_print = monitor_user_noop,
@@ -703,7 +703,7 @@ EQMP
{
.name = "device_del",
- .args_type = "device:s",
+ .args_type = "device:Q",
.params = "device",
.help = "remove device",
.user_print = monitor_user_noop,