@@ -39,7 +39,7 @@ DeviceInfo *device_info_list;
static BusState *qbus_find_recursive(BusState *bus, const char *name,
const BusInfo *info);
-static BusState *qbus_find(const char *path);
+static BusState *qbus_find_internal(const char *path, bool report_errors);
/* Register a new device type. */
void qdev_register(DeviceInfo *info)
@@ -217,7 +217,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
/* find bus */
path = qemu_opt_get(opts, "bus");
if (path != NULL) {
- bus = qbus_find(path);
+ bus = qbus_find_internal(path, true);
if (!bus) {
return NULL;
}
@@ -575,7 +575,7 @@ static DeviceState *qbus_find_dev(BusState *bus, const char *elem)
return NULL;
}
-static BusState *qbus_find(const char *path)
+static BusState *qbus_find_internal(const char *path, bool report_errors)
{
DeviceState *dev, *next_dev;
BusState *bus;
@@ -593,7 +593,9 @@ static BusState *qbus_find(const char *path)
}
bus = qbus_find_recursive(main_system_bus, elem, NULL);
if (!bus) {
- qerror_report(QERR_BUS_NOT_FOUND, elem);
+ if (report_errors) {
+ qerror_report(QERR_BUS_NOT_FOUND, elem);
+ }
return NULL;
}
pos = len;
@@ -616,8 +618,10 @@ static BusState *qbus_find(const char *path)
pos += len;
dev = qbus_find_dev(bus, elem);
if (!dev) {
- qerror_report(QERR_DEVICE_NOT_FOUND, elem);
- qbus_list_dev(bus);
+ if (report_errors) {
+ qerror_report(QERR_DEVICE_NOT_FOUND, elem);
+ qbus_list_dev(bus);
+ }
return NULL;
}
@@ -631,13 +635,17 @@ search_dev_bus:
* one child bus accept it nevertheless */
switch (dev->num_child_bus) {
case 0:
- qerror_report(QERR_DEVICE_NO_BUS, elem);
+ if (report_errors) {
+ qerror_report(QERR_DEVICE_NO_BUS, elem);
+ }
return NULL;
case 1:
return QTAILQ_FIRST(&dev->child_bus);
default:
- qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem);
- qbus_list_bus(dev);
+ if (report_errors) {
+ qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem);
+ qbus_list_bus(dev);
+ }
return NULL;
}
}
@@ -659,14 +667,21 @@ search_dev_bus:
goto search_dev_bus;
}
}
- qerror_report(QERR_BUS_NOT_FOUND, elem);
- qbus_list_bus(dev);
+ if (report_errors) {
+ qerror_report(QERR_BUS_NOT_FOUND, elem);
+ qbus_list_bus(dev);
+ }
return NULL;
}
}
}
-static DeviceState *qdev_find(const char *path)
+BusState *qbus_find(const char *path)
+{
+ return qbus_find_internal(path, false);
+}
+
+static DeviceState *qdev_find_internal(const char *path, bool report_errors)
{
const char *dev_name;
DeviceState *dev;
@@ -686,7 +701,7 @@ static DeviceState *qdev_find(const char *path)
bus_path = qemu_strdup(path);
bus_path[dev_name - path] = 0;
- bus = qbus_find(bus_path);
+ bus = qbus_find_internal(bus_path, report_errors);
qemu_free(bus_path);
if (!bus) {
@@ -695,13 +710,18 @@ 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);
}
return dev;
}
+DeviceState *qdev_find(const char *path)
+{
+ return qdev_find_internal(path, false);
+}
+
void qbus_create_inplace(BusState *bus, BusInfo *info,
DeviceState *parent, const char *name)
{
@@ -863,7 +883,7 @@ int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
const char *path = qdict_get_str(qdict, "path");
DeviceState *dev;
- dev = qdev_find(path);
+ dev = qdev_find_internal(path, true);
if (!dev) {
qerror_report(QERR_DEVICE_NOT_FOUND, path);
return -1;
@@ -165,6 +165,7 @@ void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n);
CharDriverState *qdev_init_chardev(DeviceState *dev);
BusState *qdev_get_parent_bus(DeviceState *dev);
+DeviceState *qdev_find(const char *path);
/*** BUS API. ***/
@@ -172,6 +173,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);
#define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev)
@@ -64,6 +64,7 @@
*
* 'F' filename
* 'B' block device name
+ * '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
@@ -3437,6 +3438,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon,
switch(c) {
case 'F':
case 'B':
+ case 'Q':
case 's':
{
int ret;
@@ -3461,6 +3463,10 @@ 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 device path expected\n",
+ cmdname);
+ break;
default:
monitor_printf(mon, "%s: string expected\n", cmdname);
break;
@@ -3947,6 +3953,79 @@ 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 qdev_completion(const char *input)
+{
+ size_t parent_len, name_len;
+ char *parent_path = NULL;
+ const char *p, *name;
+ DeviceState *dev;
+ BusState *bus;
+
+ p = strrchr(input, '/');
+ if (!p) {
+ if (*input == '\0') {
+ readline_add_completion(cur_mon->rs, "/");
+ }
+ return;
+ }
+
+ p++;
+ parent_path = qemu_strndup(input, p - input);
+ bus = qbus_find(parent_path);
+
+ 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 = dev->id ? : dev->info->name;
+ if (strncmp(name, p, strlen(p)) == 0) {
+ add_qdev_completion(parent_path, name, false);
+ if (!QTAILQ_EMPTY(&dev->child_bus)) {
+ add_qdev_completion(parent_path, name, true);
+ }
+ }
+ }
+ } else {
+ parent_path[strlen(parent_path) - 1] = 0;
+ dev = qdev_find(parent_path);
+ 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)
@@ -4044,6 +4123,11 @@ static void monitor_find_completion(const char *cmdline)
readline_set_completion_index(cur_mon->rs, strlen(str));
bdrv_iterate(block_completion_it, (void *)str);
break;
+ case 'Q':
+ /* qdev device path completion */
+ readline_set_completion_index(cur_mon->rs, strlen(str));
+ qdev_completion(str);
+ break;
case 's':
/* XXX: more generic ? */
if (!strcmp(cmd->name, "info")) {
@@ -4122,6 +4206,7 @@ static int check_arg(const CmdArgs *cmd_args, QDict *args)
switch (cmd_args->type) {
case 'F':
case 'B':
+ case 'Q':
case 's':
if (qobject_type(value) != QTYPE_QSTRING) {
qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "string");
@@ -703,7 +703,7 @@ EQMP
{
.name = "device_del",
- .args_type = "path:s",
+ .args_type = "path:Q",
.params = "device",
.help = "remove device",
.user_print = monitor_user_noop,