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 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. */
|
/* Register a new device type. */
|
||||||
void qdev_register(DeviceInfo *info)
|
void qdev_register(DeviceInfo *info)
|
||||||
{
|
{
|
||||||
|
@ -101,6 +105,80 @@ DeviceState *qdev_create(BusState *bus, const char *name)
|
||||||
return dev;
|
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
|
/* Initialize a device. Device properties should be set before calling
|
||||||
this function. IRQs and MMIO regions should be connected/mapped after
|
this function. IRQs and MMIO regions should be connected/mapped after
|
||||||
calling this function. */
|
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 *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
|
||||||
{
|
{
|
||||||
BusState *bus;
|
BusState *bus;
|
||||||
|
|
|
@ -82,6 +82,7 @@ struct CompatProperty {
|
||||||
/*** Board API. This should go away once we have a machine config file. ***/
|
/*** Board API. This should go away once we have a machine config file. ***/
|
||||||
|
|
||||||
DeviceState *qdev_create(BusState *bus, const char *name);
|
DeviceState *qdev_create(BusState *bus, const char *name);
|
||||||
|
DeviceState *qdev_device_add(const char *cmdline);
|
||||||
void qdev_init(DeviceState *dev);
|
void qdev_init(DeviceState *dev);
|
||||||
void qdev_free(DeviceState *dev);
|
void qdev_free(DeviceState *dev);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue