diff options
author | Gerd Hoffmann <kraxel@redhat.com> | 2009-07-15 13:59:25 +0200 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2009-07-27 08:39:29 -0500 |
commit | 8ffb1bcf56a4b62d80c8dbefa715cd16394255e0 (patch) | |
tree | f15357f1f5645b5aaf2f3829dd6ce27c7c7a4a73 /hw/qdev.c | |
parent | d271de9f1bd7c4671af8cc8edca4ac677371cfff (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>
Diffstat (limited to 'hw/qdev.c')
-rw-r--r-- | hw/qdev.c | 251 |
1 files changed, 251 insertions, 0 deletions
@@ -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; |