qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 4/6] qdev: bus walker + qdev_device_add()


From: Gerd Hoffmann
Subject: [Qemu-devel] [PATCH 4/6] qdev: bus walker + qdev_device_add()
Date: Wed, 15 Jul 2009 13:59:25 +0200

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 <address@hidden>
---
 hw/qdev.c |  251 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/qdev.h |    1 +
 2 files changed, 252 insertions(+), 0 deletions(-)

diff --git a/hw/qdev.c b/hw/qdev.c
index 2b772f8..f96395b 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -37,6 +37,10 @@ extern struct BusInfo system_bus_info;
 
 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)
 {
@@ -103,6 +107,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.  */
@@ -231,6 +309,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 67c4dd5..f2b8b2e 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -81,6 +81,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);
 
-- 
1.6.2.5





reply via email to

[Prev in Thread] Current Thread [Next in Thread]