qdev: bus walker + qdev_device_add()
[qemu.git] / hw / qdev.c
index 8506d85fac5527a1fa1445b300eba0aace098439..97a9665424dbd42d7dba9924dfc5e6fdce3d9571 100644 (file)
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -14,8 +14,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  */
 
 /* The theory here is that it should be possible to create a machine without
 #include "sysemu.h"
 #include "monitor.h"
 
-struct DeviceProperty {
-    const char *name;
-    DevicePropType type;
-    union {
-        uint64_t i;
-        void *ptr;
-    } value;
-    DeviceProperty *next;
-};
-
-struct DeviceType {
-    DeviceInfo *info;
-    DeviceType *next;
-};
-
 /* This is a nasty hack to allow passing a NULL bus to qdev_create.  */
 static BusState *main_system_bus;
-extern struct BusInfo system_bus_info;
 
-static DeviceType *device_type_list;
+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)
 {
-    DeviceType *t;
-
     assert(info->size >= sizeof(DeviceState));
+    assert(!info->next);
+
+    info->next = device_info_list;
+    device_info_list = info;
+}
+
+static DeviceInfo *qdev_find_info(BusInfo *bus_info, const char *name)
+{
+    DeviceInfo *info;
+
+    /* first check device names */
+    for (info = device_info_list; info != NULL; info = info->next) {
+        if (bus_info && info->bus_info != bus_info)
+            continue;
+        if (strcmp(info->name, name) != 0)
+            continue;
+        return info;
+    }
 
-    t = qemu_mallocz(sizeof(DeviceType));
-    t->next = device_type_list;
-    device_type_list = t;
-    t->info = info;
+    /* failing that check the aliases */
+    for (info = device_info_list; info != NULL; info = info->next) {
+        if (bus_info && info->bus_info != bus_info)
+            continue;
+        if (!info->alias)
+            continue;
+        if (strcmp(info->alias, name) != 0)
+            continue;
+        return info;
+    }
+    return NULL;
 }
 
 /* Create a new device.  This only initializes the device state structure
@@ -70,7 +80,7 @@ void qdev_register(DeviceInfo *info)
    initialize the actual device emulation.  */
 DeviceState *qdev_create(BusState *bus, const char *name)
 {
-    DeviceType *t;
+    DeviceInfo *info;
     DeviceState *dev;
 
     if (!bus) {
@@ -80,92 +90,118 @@ DeviceState *qdev_create(BusState *bus, const char *name)
         bus = main_system_bus;
     }
 
-    for (t = device_type_list; t; t = t->next) {
-        if (t->info->bus_info != bus->info)
-            continue;
-        if (strcmp(t->info->name, name) != 0)
-            continue;
-        break;
-    }
-    if (!t) {
+    info = qdev_find_info(bus->info, name);
+    if (!info) {
         hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name);
     }
 
-    dev = qemu_mallocz(t->info->size);
-    dev->type = t;
+    dev = qemu_mallocz(info->size);
+    dev->info = info;
     dev->parent_bus = bus;
+    qdev_prop_set_defaults(dev, dev->info->props);
+    qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
+    qdev_prop_set_compat(dev);
     LIST_INSERT_HEAD(&bus->children, dev, sibling);
     return dev;
 }
 
-/* Initialize a device.  Device properties should be set before calling
-   this function.  IRQs and MMIO regions should be connected/mapped after
-   calling this function.  */
-void qdev_init(DeviceState *dev)
-{
-    dev->type->info->init(dev, dev->type->info);
-}
-
-/* Unlink device from bus and free the structure.  */
-void qdev_free(DeviceState *dev)
-{
-    LIST_REMOVE(dev, sibling);
-    free(dev);
-}
-
-static DeviceProperty *create_prop(DeviceState *dev, const char *name,
-                                   DevicePropType type)
+DeviceState *qdev_device_add(const char *cmdline)
 {
-    DeviceProperty *prop;
-
-    /* TODO: Check for duplicate properties.  */
-    prop = qemu_mallocz(sizeof(*prop));
-    prop->name = qemu_strdup(name);
-    prop->type = type;
-    prop->next = dev->props;
-    dev->props = prop;
-
-    return prop;
-}
+    DeviceInfo *info;
+    DeviceState *qdev;
+    BusState *bus;
+    char driver[32], path[128] = "";
+    char tag[32], value[256];
+    const char *params = NULL;
+    int n = 0;
 
-void qdev_set_prop_int(DeviceState *dev, const char *name, uint64_t value)
-{
-    DeviceProperty *prop;
+    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;
+    }
 
-    prop = create_prop(dev, name, PROP_TYPE_INT);
-    prop->value.i = value;
-}
+    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);
+    }
 
-void qdev_set_prop_dev(DeviceState *dev, const char *name, DeviceState *value)
-{
-    DeviceProperty *prop;
+    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);
+            }
+        }
+    }
 
-    prop = create_prop(dev, name, PROP_TYPE_DEV);
-    prop->value.ptr = value;
+    qdev_init(qdev);
+    return qdev;
 }
 
-void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value)
+/* Initialize a device.  Device properties should be set before calling
+   this function.  IRQs and MMIO regions should be connected/mapped after
+   calling this function.  */
+void qdev_init(DeviceState *dev)
 {
-    DeviceProperty *prop;
-
-    prop = create_prop(dev, name, PROP_TYPE_PTR);
-    prop->value.ptr = value;
+    dev->info->init(dev, dev->info);
 }
 
-void qdev_set_netdev(DeviceState *dev, NICInfo *nd)
+/* Unlink device from bus and free the structure.  */
+void qdev_free(DeviceState *dev)
 {
-    assert(!dev->nd);
-    dev->nd = nd;
+    LIST_REMOVE(dev, sibling);
+    qemu_free(dev->id);
+    qemu_free(dev);
 }
 
-
 /* Get a character (serial) device interface.  */
 CharDriverState *qdev_init_chardev(DeviceState *dev)
 {
     static int next_serial;
     static int next_virtconsole;
     /* FIXME: This is a nasty hack that needs to go away.  */
-    if (strncmp(dev->type->info->name, "virtio", 6) == 0) {
+    if (strncmp(dev->info->name, "virtio", 6) == 0) {
         return virtcon_hds[next_virtconsole++];
     } else {
         return serial_hds[next_serial++];
@@ -177,52 +213,6 @@ BusState *qdev_get_parent_bus(DeviceState *dev)
     return dev->parent_bus;
 }
 
-static DeviceProperty *find_prop(DeviceState *dev, const char *name,
-                                 DevicePropType type)
-{
-    DeviceProperty *prop;
-
-    for (prop = dev->props; prop; prop = prop->next) {
-        if (strcmp(prop->name, name) == 0) {
-            assert (prop->type == type);
-            return prop;
-        }
-    }
-    return NULL;
-}
-
-uint64_t qdev_get_prop_int(DeviceState *dev, const char *name, uint64_t def)
-{
-    DeviceProperty *prop;
-
-    prop = find_prop(dev, name, PROP_TYPE_INT);
-    if (!prop) {
-        return def;
-    }
-
-    return prop->value.i;
-}
-
-void *qdev_get_prop_ptr(DeviceState *dev, const char *name)
-{
-    DeviceProperty *prop;
-
-    prop = find_prop(dev, name, PROP_TYPE_PTR);
-    assert(prop);
-    return prop->value.ptr;
-}
-
-DeviceState *qdev_get_prop_dev(DeviceState *dev, const char *name)
-{
-    DeviceProperty *prop;
-
-    prop = find_prop(dev, name, PROP_TYPE_DEV);
-    if (!prop) {
-        return NULL;
-    }
-    return prop->value.ptr;
-}
-
 void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
 {
     assert(dev->num_gpio_in == 0);
@@ -258,8 +248,9 @@ VLANClientState *qdev_get_vlan_client(DeviceState *dev,
 {
     NICInfo *nd = dev->nd;
     assert(nd);
-    return qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
-                                receive, receive_iov, cleanup, opaque);
+    nd->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
+                                  receive, receive_iov, cleanup, opaque);
+    return nd->vc;
 }
 
 
@@ -316,17 +307,213 @@ 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;
+    char *buf;
+    int i,len;
 
     bus = qemu_mallocz(info->size);
     bus->info = info;
     bus->parent = parent;
-    bus->name = qemu_strdup(name);
+
+    if (name) {
+        /* use supplied name */
+        bus->name = qemu_strdup(name);
+    } else if (parent && parent->id) {
+        /* parent device has id -> use it for bus name */
+        len = strlen(parent->id) + 16;
+        buf = qemu_malloc(len);
+        snprintf(buf, len, "%s.%d", parent->id, parent->num_child_bus);
+        bus->name = buf;
+    } else {
+        /* no id -> use lowercase bus type for bus name */
+        len = strlen(info->name) + 16;
+        buf = qemu_malloc(len);
+        len = snprintf(buf, len, "%s.%d", info->name,
+                       parent ? parent->num_child_bus : 0);
+        for (i = 0; i < len; i++)
+            buf[i] = tolower(buf[i]);
+        bus->name = buf;
+    }
+
     LIST_INIT(&bus->children);
     if (parent) {
         LIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
+        parent->num_child_bus++;
     }
     return bus;
 }
@@ -334,11 +521,27 @@ BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
 #define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
 static void qbus_print(Monitor *mon, BusState *bus, int indent);
 
+static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
+                             const char *prefix, int indent)
+{
+    char buf[64];
+
+    if (!props)
+        return;
+    while (props->name) {
+        if (props->info->print) {
+            props->info->print(dev, props, buf, sizeof(buf));
+            qdev_printf("%s-prop: %s = %s\n", prefix, props->name, buf);
+        }
+        props++;
+    }
+}
+
 static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
 {
-    DeviceProperty *prop;
     BusState *child;
-    qdev_printf("dev: %s\n", dev->type->info->name);
+    qdev_printf("dev: %s, id \"%s\"\n", dev->info->name,
+                dev->id ? dev->id : "");
     indent += 2;
     if (dev->num_gpio_in) {
         qdev_printf("gpio-in %d\n", dev->num_gpio_in);
@@ -346,24 +549,8 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
     if (dev->num_gpio_out) {
         qdev_printf("gpio-out %d\n", dev->num_gpio_out);
     }
-    for (prop = dev->props; prop; prop = prop->next) {
-        switch (prop->type) {
-        case PROP_TYPE_INT:
-            qdev_printf("prop-int %s 0x%" PRIx64 "\n", prop->name,
-                        prop->value.i);
-            break;
-        case PROP_TYPE_PTR:
-            qdev_printf("prop-ptr %s\n", prop->name);
-            break;
-        case PROP_TYPE_DEV:
-            qdev_printf("prop-dev %s %s\n", prop->name,
-                        ((DeviceState *)prop->value.ptr)->type->info->name);
-            break;
-        default:
-            qdev_printf("prop-unknown%d %s\n", prop->type, prop->name);
-            break;
-        }
-    }
+    qdev_print_props(mon, dev, dev->info->props, "dev", indent);
+    qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
     if (dev->parent_bus->info->print_dev)
         dev->parent_bus->info->print_dev(mon, dev, indent);
     LIST_FOREACH(child, &dev->child_bus, sibling) {
This page took 0.037062 seconds and 4 git commands to generate.