* 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 "qdev.h"
#include "sysemu.h"
#include "monitor.h"
+#include "qerror.h"
-struct DeviceProperty {
- const char *name;
- DevicePropType type;
- union {
- uint64_t i;
- void *ptr;
- } value;
- DeviceProperty *next;
-};
-
-struct DeviceType {
- const char *name;
- DeviceInfo *info;
- int size;
- DeviceType *next;
-};
+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 DeviceType *device_type_list;
+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(const char *name, int size, DeviceInfo *info)
+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;
- assert(size >= sizeof(DeviceState));
+ /* 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->name = qemu_strdup(name);
- t->size = size;
- 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
initialize the actual device emulation. */
DeviceState *qdev_create(BusState *bus, const char *name)
{
- DeviceType *t;
+ DeviceInfo *info;
DeviceState *dev;
- for (t = device_type_list; t; t = t->next) {
- if (strcmp(t->name, name) == 0) {
- break;
- }
- }
- if (!t) {
- hw_error("Unknown device '%s'\n", name);
- }
-
- dev = qemu_mallocz(t->size);
- dev->type = t;
-
if (!bus) {
- /* ???: This assumes system busses have no additional state. */
if (!main_system_bus) {
- main_system_bus = qbus_create(BUS_TYPE_SYSTEM, sizeof(BusState),
- NULL, "main-system-bus");
+ main_system_bus = qbus_create(&system_bus_info, NULL, "main-system-bus");
}
bus = main_system_bus;
}
- if (t->info->bus_type != bus->type) {
- /* TODO: Print bus type names. */
- hw_error("Device '%s' on wrong bus type (%d/%d)", name,
- t->info->bus_type, bus->type);
+
+ 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(info->size);
+ dev->info = info;
dev->parent_bus = bus;
- LIST_INSERT_HEAD(&bus->children, dev, sibling);
+ qdev_prop_set_defaults(dev, dev->info->props);
+ qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
+ 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;
}
-/* 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)
+static int qdev_print_devinfo(DeviceInfo *info, char *dest, int len)
{
- dev->type->info->init(dev, dev->type->info);
+ int pos = 0;
+ int ret;
+
+ 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;
}
-/* Unlink device from bus and free the structure. */
-void qdev_free(DeviceState *dev)
+static int set_property(const char *name, const char *value, void *opaque)
{
- LIST_REMOVE(dev, sibling);
- free(dev);
+ 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;
}
-static DeviceProperty *create_prop(DeviceState *dev, const char *name,
- DevicePropType type)
+DeviceState *qdev_device_add(QemuOpts *opts)
{
- DeviceProperty *prop;
+ const char *driver, *path, *id;
+ DeviceInfo *info;
+ DeviceState *qdev;
+ BusState *bus;
- /* 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;
+ 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));
+ qemu_error("%s\n", msg);
+ }
+ return NULL;
+ }
- return prop;
-}
+ /* find driver */
+ info = qdev_find_info(NULL, driver);
+ if (!info) {
+ qemu_error_new(QERR_DEVICE_NOT_FOUND, driver);
+ return NULL;
+ }
+ if (info->no_user) {
+ qemu_error("device \"%s\" can't be added via command line\n",
+ info->name);
+ return NULL;
+ }
-void qdev_set_prop_int(DeviceState *dev, const char *name, uint64_t value)
-{
- DeviceProperty *prop;
+ /* find bus */
+ path = qemu_opt_get(opts, "bus");
+ if (path != NULL) {
+ bus = qbus_find(path);
+ } else {
+ bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
+ }
+ 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;
+ }
- prop = create_prop(dev, name, PROP_TYPE_INT);
- prop->value.i = value;
+ /* 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;
}
-void qdev_set_prop_dev(DeviceState *dev, const char *name, DeviceState *value)
+static void qdev_reset(void *opaque)
{
- DeviceProperty *prop;
-
- prop = create_prop(dev, name, PROP_TYPE_DEV);
- prop->value.ptr = value;
+ DeviceState *dev = opaque;
+ if (dev->info->reset)
+ dev->info->reset(dev);
}
-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.
+ On failure, destroy the device and return negative value.
+ Return 0 on success. */
+int qdev_init(DeviceState *dev)
{
- DeviceProperty *prop;
+ int rc;
- prop = create_prop(dev, name, PROP_TYPE_PTR);
- prop->value.ptr = value;
+ 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;
}
-void qdev_set_netdev(DeviceState *dev, NICInfo *nd)
+int qdev_unplug(DeviceState *dev)
{
- assert(!dev->nd);
- dev->nd = nd;
-}
+ 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);
+}
-/* Get a character (serial) device interface. */
-CharDriverState *qdev_init_chardev(DeviceState *dev)
+/* can be used as ->unplug() callback for the simple cases */
+int qdev_simple_unplug_cb(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->name, "virtio", 6) == 0) {
- return virtcon_hds[next_virtconsole++];
- } else {
- return serial_hds[next_serial++];
- }
+ /* just zap it */
+ qdev_free(dev);
+ return 0;
}
-BusState *qdev_get_parent_bus(DeviceState *dev)
+/* 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)
{
- return dev->parent_bus;
+ DeviceInfo *info = dev->info;
+
+ if (qdev_init(dev) < 0)
+ hw_error("Initialization of device %s failed\n", info->name);
}
-static DeviceProperty *find_prop(DeviceState *dev, const char *name,
- DevicePropType type)
+/* Unlink device from bus and free the structure. */
+void qdev_free(DeviceState *dev)
{
- DeviceProperty *prop;
+ BusState *bus;
- for (prop = dev->props; prop; prop = prop->next) {
- if (strcmp(prop->name, name) == 0) {
- assert (prop->type == type);
- return prop;
+ 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);
}
- return NULL;
+ qemu_unregister_reset(qdev_reset, dev);
+ QLIST_REMOVE(dev, sibling);
+ qemu_free(dev);
}
-uint64_t qdev_get_prop_int(DeviceState *dev, const char *name, uint64_t def)
+void qdev_machine_creation_done(void)
{
- DeviceProperty *prop;
-
- prop = find_prop(dev, name, PROP_TYPE_INT);
- if (!prop) {
- return def;
- }
-
- return prop->value.i;
+ /*
+ * ok, initial machine setup is done, starting from now we can
+ * only create hotpluggable devices
+ */
+ qdev_hotplug = 1;
}
-void *qdev_get_prop_ptr(DeviceState *dev, const char *name)
+/* Get a character (serial) device interface. */
+CharDriverState *qdev_init_chardev(DeviceState *dev)
{
- DeviceProperty *prop;
-
- prop = find_prop(dev, name, PROP_TYPE_PTR);
- assert(prop);
- return prop->value.ptr;
+ static int next_serial;
+ static int next_virtconsole;
+ /* FIXME: This is a nasty hack that needs to go away. */
+ if (strncmp(dev->info->name, "virtio", 6) == 0) {
+ return virtcon_hds[next_virtconsole++];
+ } else {
+ return serial_hds[next_serial++];
+ }
}
-DeviceState *qdev_get_prop_dev(DeviceState *dev, const char *name)
+BusState *qdev_get_parent_bus(DeviceState *dev)
{
- DeviceProperty *prop;
-
- prop = find_prop(dev, name, PROP_TYPE_DEV);
- if (!prop) {
- return NULL;
- }
- return prop->value.ptr;
+ return dev->parent_bus;
}
void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
dev->gpio_out[n] = pin;
}
-VLANClientState *qdev_get_vlan_client(DeviceState *dev,
- NetCanReceive *can_receive,
- NetReceive *receive,
- NetReceiveIOV *receive_iov,
- NetCleanup *cleanup,
- void *opaque)
-{
- NICInfo *nd = dev->nd;
- assert(nd);
- return qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
- receive, receive_iov, cleanup, opaque);
-}
-
-
-void qdev_get_macaddr(DeviceState *dev, uint8_t *macaddr)
+void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
{
- 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];
BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type)
{
int unit = next_block_unit[type]++;
- int index;
+ DriveInfo *dinfo;
- index = drive_get_index(type, 0, unit);
- if (index == -1) {
- return NULL;
- }
- return drives_table[index].bdrv;
+ dinfo = drive_get(type, 0, unit);
+ return dinfo ? dinfo->bdrv : NULL;
}
BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
{
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;
+static BusState *qbus_find_recursive(BusState *bus, const char *name,
+ const BusInfo *info)
+{
+ DeviceState *dev;
+ BusState *child, *ret;
+ int match = 1;
-/* 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)
+ if (name && (strcmp(bus->name, name) != 0)) {
+ match = 0;
+ }
+ if (info && (bus->info != info)) {
+ match = 0;
+ }
+ if (match) {
+ return bus;
+ }
+
+ 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;
+ const char *sep = " ";
+ int pos = 0;
+
+ pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
+ dev->id ? dev->id : dev->info->name);
+ QLIST_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);
+ QLIST_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)
{
- int bus = next_scsi_bus++;
- int unit;
- int index;
+ BusState *child;
- for (unit = 0; unit < MAX_SCSI_DEVS; unit++) {
- index = drive_get_index(IF_SCSI, bus, unit);
- if (index == -1) {
- continue;
- }
- attach(host, drives_table[index].bdrv, unit);
- }
+ QLIST_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
+ */
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->id && strcmp(dev->id, elem) == 0) {
+ return dev;
+ }
+ }
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (strcmp(dev->info->name, elem) == 0) {
+ return dev;
+ }
+ }
+ QLIST_FOREACH(dev, &bus->children, sibling) {
+ if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
+ return dev;
+ }
+ }
+ return NULL;
}
-BusState *qbus_create(BusType type, size_t size,
- DeviceState *parent, const char *name)
+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) {
+ qemu_error("path parse error (\"%s\")\n", path);
+ return NULL;
+ }
+ bus = qbus_find_recursive(main_system_bus, elem, NULL);
+ if (!bus) {
+ qemu_error("bus \"%s\" not found\n", elem);
+ return NULL;
+ }
+ pos = len;
+ }
+
+ for (;;) {
+ if (path[pos] == '\0') {
+ /* we are done */
+ return bus;
+ }
- bus = qemu_mallocz(size);
- bus->type = type;
+ /* find device */
+ if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
+ 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));
+ qemu_error("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:
+ qemu_error("device has no child bus (%s)\n", path);
+ return NULL;
+ case 1:
+ return QLIST_FIRST(&dev->child_bus);
+ default:
+ qbus_list_bus(dev, msg, sizeof(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) {
+ 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));
+ qemu_error("child bus \"%s\" not found\n%s\n", elem, msg);
+ return NULL;
+ }
+ }
+}
+
+void qbus_create_inplace(BusState *bus, BusInfo *info,
+ DeviceState *parent, const char *name)
+{
+ char *buf;
+ int i,len;
+
+ bus->info = info;
bus->parent = parent;
- bus->name = qemu_strdup(name);
- LIST_INIT(&bus->children);
+
+ 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] = qemu_tolower(buf[i]);
+ bus->name = buf;
+ }
+
+ 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;
}
-static const char *bus_type_names[] = {
- [ BUS_TYPE_SYSTEM ] = "System",
- [ BUS_TYPE_PCI ] = "PCI",
- [ BUS_TYPE_SCSI ] = "SCSI",
- [ BUS_TYPE_I2C ] = "I2C",
- [ BUS_TYPE_SSI ] = "SSI",
-};
+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);
+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->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);
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->name);
- break;
- default:
- qdev_printf("prop-unknown%d %s\n", prop->type, prop->name);
- break;
- }
- }
- switch (dev->parent_bus->type) {
- case BUS_TYPE_SYSTEM:
- sysbus_dev_print(mon, dev, indent);
- break;
- default:
- break;
- }
- LIST_FOREACH(child, &dev->child_bus, sibling) {
+ 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);
+ 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_type_names[bus->type]);
- LIST_FOREACH(dev, &bus->children, sibling) {
+ qdev_printf("type %s\n", bus->info->name);
+ QLIST_FOREACH(dev, &bus->children, sibling) {
qdev_print(mon, dev, indent);
}
}
if (main_system_bus)
qbus_print(mon, main_system_bus, 0);
}
+
+void do_info_qdm(Monitor *mon)
+{
+ DeviceInfo *info;
+ char msg[256];
+
+ for (info = device_info_list; info != NULL; info = info->next) {
+ qdev_print_devinfo(info, msg, sizeof(msg));
+ 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);
+}