#include "qdev.h"
#include "sysemu.h"
#include "monitor.h"
+#include "qerror.h"
+
+static int qdev_hotplug = 0;
/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
static BusState *main_system_bus;
-static DeviceInfo *device_info_list;
+DeviceInfo *device_info_list;
static BusState *qbus_find_recursive(BusState *bus, const char *name,
const BusInfo *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);
+ qdev_prop_set_globals(dev);
+ QLIST_INSERT_HEAD(&bus->children, dev, sibling);
+ if (qdev_hotplug) {
+ assert(bus->allow_hotplug);
+ dev->hotplugged = 1;
+ }
+ dev->state = DEV_STATE_CREATED;
return dev;
}
static int qdev_print_devinfo(DeviceInfo *info, char *dest, int len)
{
int pos = 0;
+ int ret;
- pos += snprintf(dest+pos, len-pos, "name \"%s\", bus %s",
- info->name, info->bus_info->name);
- if (info->alias)
- pos += snprintf(dest+pos, len-pos, ", alias \"%s\"", info->alias);
- if (info->desc)
- pos += snprintf(dest+pos, len-pos, ", desc \"%s\"", info->desc);
- if (info->no_user)
- pos += snprintf(dest+pos, len-pos, ", no-user");
+ ret = snprintf(dest+pos, len-pos, "name \"%s\", bus %s",
+ info->name, info->bus_info->name);
+ pos += MIN(len-pos,ret);
+ if (info->alias) {
+ ret = snprintf(dest+pos, len-pos, ", alias \"%s\"", info->alias);
+ pos += MIN(len-pos,ret);
+ }
+ if (info->desc) {
+ ret = snprintf(dest+pos, len-pos, ", desc \"%s\"", info->desc);
+ pos += MIN(len-pos,ret);
+ }
+ if (info->no_user) {
+ ret = snprintf(dest+pos, len-pos, ", no-user");
+ pos += MIN(len-pos,ret);
+ }
return pos;
}
-DeviceState *qdev_device_add(const char *cmdline)
+static int set_property(const char *name, const char *value, void *opaque)
+{
+ DeviceState *dev = opaque;
+
+ if (strcmp(name, "driver") == 0)
+ return 0;
+ if (strcmp(name, "bus") == 0)
+ return 0;
+
+ if (qdev_prop_parse(dev, name, value) == -1) {
+ qemu_error("can't set property \"%s\" to \"%s\" for \"%s\"\n",
+ name, value, dev->info->name);
+ return -1;
+ }
+ return 0;
+}
+
+DeviceState *qdev_device_add(QemuOpts *opts)
{
+ const char *driver, *path, *id;
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);
+ driver = qemu_opt_get(opts, "driver");
+ if (!driver) {
+ qemu_error("-device: no driver specified\n");
return NULL;
}
if (strcmp(driver, "?") == 0) {
char msg[256];
for (info = device_info_list; info != NULL; info = info->next) {
qdev_print_devinfo(info, msg, sizeof(msg));
- fprintf(stderr, "%s\n", msg);
+ qemu_error("%s\n", msg);
}
return NULL;
}
- if (n) {
- params = cmdline + n;
- get_param_value(path, sizeof(path), "bus", params);
- }
+
+ /* find driver */
info = qdev_find_info(NULL, driver);
if (!info) {
- fprintf(stderr, "Device \"%s\" not found. Try -device '?' for a list.\n",
- driver);
+ qemu_error_new(QERR_DEVICE_NOT_FOUND, driver);
return NULL;
}
if (info->no_user) {
- fprintf(stderr, "device \"%s\" can't be added via command line\n",
- info->name);
+ qemu_error("device \"%s\" can't be added via command line\n",
+ info->name);
return NULL;
}
- if (strlen(path)) {
+ /* find bus */
+ path = qemu_opt_get(opts, "bus");
+ if (path != NULL) {
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);
- }
- }
+ if (!bus) {
+ qemu_error("Did not find %s bus for %s\n",
+ path ? path : info->bus_info->name, info->name);
+ return NULL;
+ }
+ if (qdev_hotplug && !bus->allow_hotplug) {
+ qemu_error("Bus %s does not support hotplugging\n",
+ bus->name);
+ return NULL;
}
- qdev_init(qdev);
+ /* create device, set properties */
+ qdev = qdev_create(bus, driver);
+ id = qemu_opts_id(opts);
+ if (id) {
+ qdev->id = id;
+ }
+ if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
+ qdev_free(qdev);
+ return NULL;
+ }
+ if (qdev_init(qdev) < 0) {
+ qemu_error("Error initializing device %s\n", driver);
+ return NULL;
+ }
+ qdev->opts = opts;
return qdev;
}
+static void qdev_reset(void *opaque)
+{
+ DeviceState *dev = opaque;
+ if (dev->info->reset)
+ dev->info->reset(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)
+ calling this function.
+ On failure, destroy the device and return negative value.
+ Return 0 on success. */
+int qdev_init(DeviceState *dev)
+{
+ int rc;
+
+ assert(dev->state == DEV_STATE_CREATED);
+ rc = dev->info->init(dev, dev->info);
+ if (rc < 0) {
+ qdev_free(dev);
+ return rc;
+ }
+ qemu_register_reset(qdev_reset, dev);
+ if (dev->info->vmsd)
+ vmstate_register(-1, dev->info->vmsd, dev);
+ dev->state = DEV_STATE_INITIALIZED;
+ return 0;
+}
+
+int qdev_unplug(DeviceState *dev)
+{
+ if (!dev->parent_bus->allow_hotplug) {
+ qemu_error("Bus %s does not support hotplugging\n",
+ dev->parent_bus->name);
+ return -1;
+ }
+ assert(dev->info->unplug != NULL);
+
+ return dev->info->unplug(dev);
+}
+
+/* can be used as ->unplug() callback for the simple cases */
+int qdev_simple_unplug_cb(DeviceState *dev)
{
- dev->info->init(dev, dev->info);
+ /* just zap it */
+ qdev_free(dev);
+ return 0;
+}
+
+/* Like qdev_init(), but terminate program via hw_error() instead of
+ returning an error value. This is okay during machine creation.
+ Don't use for hotplug, because there callers need to recover from
+ failure. Exception: if you know the device's init() callback can't
+ fail, then qdev_init_nofail() can't fail either, and is therefore
+ usable even then. But relying on the device implementation that
+ way is somewhat unclean, and best avoided. */
+void qdev_init_nofail(DeviceState *dev)
+{
+ DeviceInfo *info = dev->info;
+
+ if (qdev_init(dev) < 0)
+ hw_error("Initialization of device %s failed\n", info->name);
}
/* Unlink device from bus and free the structure. */
void qdev_free(DeviceState *dev)
{
- LIST_REMOVE(dev, sibling);
- qemu_free(dev->id);
+ BusState *bus;
+
+ if (dev->state == DEV_STATE_INITIALIZED) {
+ while (dev->num_child_bus) {
+ bus = QLIST_FIRST(&dev->child_bus);
+ qbus_free(bus);
+ }
+ if (dev->info->vmsd)
+ vmstate_unregister(dev->info->vmsd, dev);
+ if (dev->info->exit)
+ dev->info->exit(dev);
+ if (dev->opts)
+ qemu_opts_del(dev->opts);
+ }
+ qemu_unregister_reset(qdev_reset, dev);
+ QLIST_REMOVE(dev, sibling);
qemu_free(dev);
}
+void qdev_machine_creation_done(void)
+{
+ /*
+ * ok, initial machine setup is done, starting from now we can
+ * only create hotpluggable devices
+ */
+ qdev_hotplug = 1;
+}
+
/* Get a character (serial) device interface. */
CharDriverState *qdev_init_chardev(DeviceState *dev)
{
dev->gpio_out[n] = pin;
}
-VLANClientState *qdev_get_vlan_client(DeviceState *dev,
- NetCanReceive *can_receive,
- NetReceive *receive,
- NetReceiveIOV *receive_iov,
- NetCleanup *cleanup,
- void *opaque)
+void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
{
- NICInfo *nd = dev->nd;
- assert(nd);
- nd->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
- receive, receive_iov, cleanup, opaque);
- return nd->vc;
-}
-
-
-void qdev_get_macaddr(DeviceState *dev, uint8_t *macaddr)
-{
- memcpy(macaddr, dev->nd->macaddr, 6);
+ qdev_prop_set_macaddr(dev, "mac", nd->macaddr);
+ if (nd->vlan)
+ qdev_prop_set_vlan(dev, "vlan", nd->vlan);
+ if (nd->netdev)
+ qdev_prop_set_netdev(dev, "netdev", nd->netdev);
+ if (nd->nvectors != NIC_NVECTORS_UNSPECIFIED &&
+ qdev_prop_exists(dev, "vectors")) {
+ qdev_prop_set_uint32(dev, "vectors", nd->nvectors);
+ }
}
static int next_block_unit[IF_COUNT];
{
BusState *bus;
- LIST_FOREACH(bus, &dev->child_bus, sibling) {
+ QLIST_FOREACH(bus, &dev->child_bus, sibling) {
if (strcmp(name, bus->name) == 0) {
return bus;
}
return NULL;
}
-static int next_scsi_bus;
-
-/* Create a scsi bus, and attach devices to it. */
-/* TODO: Actually create a scsi bus for hotplug to use. */
-void scsi_bus_new(DeviceState *host, SCSIAttachFn attach)
-{
- int bus = next_scsi_bus++;
- int unit;
- DriveInfo *dinfo;
-
- for (unit = 0; unit < MAX_SCSI_DEVS; unit++) {
- dinfo = drive_get(IF_SCSI, bus, unit);
- if (!dinfo) {
- continue;
- }
- attach(host, dinfo->bdrv, unit);
- }
-}
-
static BusState *qbus_find_recursive(BusState *bus, const char *name,
const BusInfo *info)
{
return bus;
}
- LIST_FOREACH(dev, &bus->children, sibling) {
- LIST_FOREACH(child, &dev->child_bus, sibling) {
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
ret = qbus_find_recursive(child, name, info);
if (ret) {
return ret;
return NULL;
}
+static DeviceState *qdev_find_recursive(BusState *bus, const char *id)
+{
+ DeviceState *dev, *ret;
+ BusState *child;
+
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->id && strcmp(dev->id, id) == 0)
+ return dev;
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
+ ret = qdev_find_recursive(child, id);
+ if (ret) {
+ return ret;
+ }
+ }
+ }
+ return NULL;
+}
+
static void qbus_list_bus(DeviceState *dev, char *dest, int len)
{
BusState *child;
pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
dev->id ? dev->id : dev->info->name);
- LIST_FOREACH(child, &dev->child_bus, sibling) {
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
sep = ", ";
}
pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
bus->name);
- LIST_FOREACH(dev, &bus->children, sibling) {
+ QLIST_FOREACH(dev, &bus->children, sibling) {
pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
sep, dev->info->name);
if (dev->id)
{
BusState *child;
- LIST_FOREACH(child, &dev->child_bus, sibling) {
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
if (strcmp(child->name, elem) == 0) {
return child;
}
* (2) driver name
* (3) driver alias, if present
*/
- LIST_FOREACH(dev, &bus->children, sibling) {
+ QLIST_FOREACH(dev, &bus->children, sibling) {
if (dev->id && strcmp(dev->id, elem) == 0) {
return dev;
}
}
- LIST_FOREACH(dev, &bus->children, sibling) {
+ QLIST_FOREACH(dev, &bus->children, sibling) {
if (strcmp(dev->info->name, elem) == 0) {
return dev;
}
}
- LIST_FOREACH(dev, &bus->children, sibling) {
+ QLIST_FOREACH(dev, &bus->children, sibling) {
if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
return dev;
}
pos = 0;
} else {
if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
- fprintf(stderr, "path parse error (\"%s\")\n", path);
+ qemu_error("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);
+ qemu_error("bus \"%s\" not found\n", elem);
return NULL;
}
pos = len;
/* find device */
if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
- fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
+ qemu_error("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);
+ qemu_error("device \"%s\" not found\n%s\n", elem, msg);
return NULL;
}
if (path[pos] == '\0') {
* one child bus accept it nevertheless */
switch (dev->num_child_bus) {
case 0:
- fprintf(stderr, "device has no child bus (%s)\n", path);
+ qemu_error("device has no child bus (%s)\n", path);
return NULL;
case 1:
- return LIST_FIRST(&dev->child_bus);
+ return QLIST_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);
+ qemu_error("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);
+ qemu_error("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);
+ qemu_error("child bus \"%s\" not found\n%s\n", elem, msg);
return NULL;
}
}
}
-BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
+void qbus_create_inplace(BusState *bus, 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;
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]);
+ buf[i] = qemu_tolower(buf[i]);
bus->name = buf;
}
- LIST_INIT(&bus->children);
+ QLIST_INIT(&bus->children);
if (parent) {
- LIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
+ QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
parent->num_child_bus++;
}
+
+}
+
+BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
+{
+ BusState *bus;
+
+ bus = qemu_mallocz(info->size);
+ bus->qdev_allocated = 1;
+ qbus_create_inplace(bus, info, parent, name);
return bus;
}
+void qbus_free(BusState *bus)
+{
+ DeviceState *dev;
+
+ while ((dev = QLIST_FIRST(&bus->children)) != NULL) {
+ qdev_free(dev);
+ }
+ if (bus->parent) {
+ QLIST_REMOVE(bus, sibling);
+ bus->parent->num_child_bus--;
+ }
+ if (bus->qdev_allocated) {
+ qemu_free(bus);
+ }
+}
+
#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
static void qbus_print(Monitor *mon, BusState *bus, int 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) {
+ QLIST_FOREACH(child, &dev->child_bus, sibling) {
qbus_print(mon, child, indent);
}
}
qdev_printf("bus: %s\n", bus->name);
indent += 2;
qdev_printf("type %s\n", bus->info->name);
- LIST_FOREACH(dev, &bus->children, sibling) {
+ QLIST_FOREACH(dev, &bus->children, sibling) {
qdev_print(mon, dev, indent);
}
}
qbus_print(mon, main_system_bus, 0);
}
-void do_info_qdrv(Monitor *mon)
+void do_info_qdm(Monitor *mon)
{
DeviceInfo *info;
char msg[256];
monitor_printf(mon, "%s\n", msg);
}
}
+
+void do_device_add(Monitor *mon, const QDict *qdict)
+{
+ QemuOpts *opts;
+
+ opts = qemu_opts_parse(&qemu_device_opts,
+ qdict_get_str(qdict, "config"), "driver");
+ if (opts)
+ qdev_device_add(opts);
+}
+
+void do_device_del(Monitor *mon, const QDict *qdict)
+{
+ const char *id = qdict_get_str(qdict, "id");
+ DeviceState *dev;
+
+ dev = qdev_find_recursive(main_system_bus, id);
+ if (NULL == dev) {
+ qemu_error("Device '%s' not found\n", id);
+ return;
+ }
+ qdev_unplug(dev);
+}