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>
This commit is contained in:
parent
d271de9f1b
commit
8ffb1bcf56
251
hw/qdev.c
251
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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user