aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Hoffmann <kraxel@redhat.com>2009-07-15 13:59:25 +0200
committerAnthony Liguori <aliguori@us.ibm.com>2009-07-27 08:39:29 -0500
commit8ffb1bcf56a4b62d80c8dbefa715cd16394255e0 (patch)
treef15357f1f5645b5aaf2f3829dd6ce27c7c7a4a73
parentd271de9f1bd7c4671af8cc8edca4ac677371cfff (diff)
qdev: bus walker + qdev_device_add()
This patch implements a parser and qdev tree walker for bus paths and adds qdev_device_add on top of this. A bus path can be: (1) full path, i.e. /i440FX-pcihost/pci.0/lsi/scsi.0 (2) bus name, i.e. "scsi.0". Best used together with id= to make sure this is unique. (3) relative path starting with a bus name, i.e. "pci.0/lsi/scsi.0" For the (common) case of a single child bus being attached to a device it is enougth to specify the device only, i.e. "pci.0/lsi" will be accepted too. qdev_device_add() adds devices and accepts bus= parameters to find the bus the device should be attached to. Without bus= being specified it takes the first bus it finds where the device can be attached to (i.e. first pci bus for pci devices, ...). Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
-rw-r--r--hw/qdev.c251
-rw-r--r--hw/qdev.h1
2 files changed, 252 insertions, 0 deletions
diff --git a/hw/qdev.c b/hw/qdev.c
index 3be321ad57..97a9665424 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -35,6 +35,10 @@ static BusState *main_system_bus;
static DeviceInfo *device_info_list;
+static BusState *qbus_find_recursive(BusState *bus, const char *name,
+ const BusInfo *info);
+static BusState *qbus_find(const char *path);
+
/* Register a new device type. */
void qdev_register(DeviceInfo *info)
{
@@ -101,6 +105,80 @@ DeviceState *qdev_create(BusState *bus, const char *name)
return dev;
}
+DeviceState *qdev_device_add(const char *cmdline)
+{
+ DeviceInfo *info;
+ DeviceState *qdev;
+ BusState *bus;
+ char driver[32], path[128] = "";
+ char tag[32], value[256];
+ const char *params = NULL;
+ int n = 0;
+
+ if (1 != sscanf(cmdline, "%32[^,],%n", driver, &n)) {
+ fprintf(stderr, "device parse error: \"%s\"\n", cmdline);
+ return NULL;
+ }
+ if (strcmp(driver, "?") == 0) {
+ for (info = device_info_list; info != NULL; info = info->next) {
+ fprintf(stderr, "name \"%s\", bus %s\n", info->name, info->bus_info->name);
+ }
+ return NULL;
+ }
+ if (n) {
+ params = cmdline + n;
+ get_param_value(path, sizeof(path), "bus", params);
+ }
+ info = qdev_find_info(NULL, driver);
+ if (!info) {
+ fprintf(stderr, "Device \"%s\" not found. Try -device '?' for a list.\n",
+ driver);
+ return NULL;
+ }
+ if (info->no_user) {
+ fprintf(stderr, "device \"%s\" can't be added via command line\n",
+ info->name);
+ return NULL;
+ }
+
+ if (strlen(path)) {
+ bus = qbus_find(path);
+ if (!bus)
+ return NULL;
+ qdev = qdev_create(bus, driver);
+ } else {
+ bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
+ if (!bus)
+ return NULL;
+ qdev = qdev_create(bus, driver);
+ }
+
+ if (params) {
+ while (params[0]) {
+ if (2 != sscanf(params, "%31[^=]=%255[^,]%n", tag, value, &n)) {
+ fprintf(stderr, "parse error at \"%s\"\n", params);
+ break;
+ }
+ params += n;
+ if (params[0] == ',')
+ params++;
+ if (strcmp(tag, "bus") == 0)
+ continue;
+ if (strcmp(tag, "id") == 0) {
+ qdev->id = qemu_strdup(value);
+ continue;
+ }
+ if (-1 == qdev_prop_parse(qdev, tag, value)) {
+ fprintf(stderr, "can't set property \"%s\" to \"%s\" for \"%s\"\n",
+ tag, value, driver);
+ }
+ }
+ }
+
+ qdev_init(qdev);
+ return qdev;
+}
+
/* Initialize a device. Device properties should be set before calling
this function. IRQs and MMIO regions should be connected/mapped after
calling this function. */
@@ -229,6 +307,179 @@ void scsi_bus_new(DeviceState *host, SCSIAttachFn attach)
}
}
+static BusState *qbus_find_recursive(BusState *bus, const char *name,
+ const BusInfo *info)
+{
+ DeviceState *dev;
+ BusState *child, *ret;
+ int match = 1;
+
+ if (name && (strcmp(bus->name, name) != 0)) {
+ match = 0;
+ }
+ if (info && (bus->info != info)) {
+ match = 0;
+ }
+ if (match) {
+ return bus;
+ }
+
+ LIST_FOREACH(dev, &bus->children, sibling) {
+ LIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qbus_find_recursive(child, name, info);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void qbus_list_bus(DeviceState *dev, char *dest, int len)
+{
+ BusState *child;
+ const char *sep = " ";
+ int pos = 0;
+
+ pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
+ dev->id ? dev->id : dev->info->name);
+ LIST_FOREACH(child, &dev->child_bus, sibling) {
+ pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
+ sep = ", ";
+ }
+}
+
+static void qbus_list_dev(BusState *bus, char *dest, int len)
+{
+ DeviceState *dev;
+ const char *sep = " ";
+ int pos = 0;
+
+ pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
+ bus->name);
+ LIST_FOREACH(dev, &bus->children, sibling) {
+ pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
+ sep, dev->info->name);
+ if (dev->id)
+ pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id);
+ sep = ", ";
+ }
+}
+
+static BusState *qbus_find_bus(DeviceState *dev, char *elem)
+{
+ BusState *child;
+
+ LIST_FOREACH(child, &dev->child_bus, sibling) {
+ if (strcmp(child->name, elem) == 0) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static DeviceState *qbus_find_dev(BusState *bus, char *elem)
+{
+ DeviceState *dev;
+
+ /*
+ * try to match in order:
+ * (1) instance id, if present
+ * (2) driver name
+ * (3) driver alias, if present
+ */
+ LIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->id && strcmp(dev->id, elem) == 0) {
+ return dev;
+ }
+ }
+ LIST_FOREACH(dev, &bus->children, sibling) {
+ if (strcmp(dev->info->name, elem) == 0) {
+ return dev;
+ }
+ }
+ LIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
+ return dev;
+ }
+ }
+ return NULL;
+}
+
+static BusState *qbus_find(const char *path)
+{
+ DeviceState *dev;
+ BusState *bus;
+ char elem[128], msg[256];
+ int pos, len;
+
+ /* find start element */
+ if (path[0] == '/') {
+ bus = main_system_bus;
+ pos = 0;
+ } else {
+ if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
+ fprintf(stderr, "path parse error (\"%s\")\n", path);
+ return NULL;
+ }
+ bus = qbus_find_recursive(main_system_bus, elem, NULL);
+ if (!bus) {
+ fprintf(stderr, "bus \"%s\" not found\n", elem);
+ return NULL;
+ }
+ pos = len;
+ }
+
+ for (;;) {
+ if (path[pos] == '\0') {
+ /* we are done */
+ return bus;
+ }
+
+ /* find device */
+ if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
+ fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
+ return NULL;
+ }
+ pos += len;
+ dev = qbus_find_dev(bus, elem);
+ if (!dev) {
+ qbus_list_dev(bus, msg, sizeof(msg));
+ fprintf(stderr, "device \"%s\" not found\n%s\n", elem, msg);
+ return NULL;
+ }
+ if (path[pos] == '\0') {
+ /* last specified element is a device. If it has exactly
+ * one child bus accept it nevertheless */
+ switch (dev->num_child_bus) {
+ case 0:
+ fprintf(stderr, "device has no child bus (%s)\n", path);
+ return NULL;
+ case 1:
+ return LIST_FIRST(&dev->child_bus);
+ default:
+ qbus_list_bus(dev, msg, sizeof(msg));
+ fprintf(stderr, "device has multiple child busses (%s)\n%s\n",
+ path, msg);
+ return NULL;
+ }
+ }
+
+ /* find bus */
+ if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
+ fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
+ return NULL;
+ }
+ pos += len;
+ bus = qbus_find_bus(dev, elem);
+ if (!bus) {
+ qbus_list_bus(dev, msg, sizeof(msg));
+ fprintf(stderr, "child bus \"%s\" not found\n%s\n", elem, msg);
+ return NULL;
+ }
+ }
+}
+
BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
{
BusState *bus;
diff --git a/hw/qdev.h b/hw/qdev.h
index d9c78eba60..af2ee0f8b9 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -82,6 +82,7 @@ struct CompatProperty {
/*** Board API. This should go away once we have a machine config file. ***/
DeviceState *qdev_create(BusState *bus, const char *name);
+DeviceState *qdev_device_add(const char *cmdline);
void qdev_init(DeviceState *dev);
void qdev_free(DeviceState *dev);