From patchwork Tue Oct 16 19:08:35 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eduardo Habkost X-Patchwork-Id: 191878 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 25B042C00A7 for ; Wed, 17 Oct 2012 06:56:45 +1100 (EST) Received: from localhost ([::1]:35173 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOCWL-00069F-6L for incoming@patchwork.ozlabs.org; Tue, 16 Oct 2012 15:09:41 -0400 Received: from eggs.gnu.org ([208.118.235.92]:51646) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOCUs-0002z6-Qu for qemu-devel@nongnu.org; Tue, 16 Oct 2012 15:08:19 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TOCUn-0001Qm-EO for qemu-devel@nongnu.org; Tue, 16 Oct 2012 15:08:10 -0400 Received: from mx1.redhat.com ([209.132.183.28]:5187) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TOCUn-0001Oq-09 for qemu-devel@nongnu.org; Tue, 16 Oct 2012 15:08:05 -0400 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q9GJ7rbl012464 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 16 Oct 2012 15:07:53 -0400 Received: from blackpad.lan.raisama.net (vpn1-4-87.gru2.redhat.com [10.97.4.87]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q9GJ7q8A025972; Tue, 16 Oct 2012 15:07:52 -0400 Received: by blackpad.lan.raisama.net (Postfix, from userid 500) id E1F8D201138; Tue, 16 Oct 2012 16:08:45 -0300 (BRT) From: Eduardo Habkost To: qemu-devel@nongnu.org Date: Tue, 16 Oct 2012 16:08:35 -0300 Message-Id: <1350414523-8117-5-git-send-email-ehabkost@redhat.com> In-Reply-To: <1350414523-8117-1-git-send-email-ehabkost@redhat.com> References: <1350414523-8117-1-git-send-email-ehabkost@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: peter.maydell@linaro.org, riku.voipio@iki.fi, blauwirbel@gmail.com, Anthony Liguori , Igor Mammedov , =?UTF-8?q?Andreas=20F=C3=A4rber?= Subject: [Qemu-devel] [PATCH 04/12] qdev: rename qdev.c to qdev-core.c X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Just to make clear that it's the qdev core code, corresponding to qdev-core.h. Signed-off-by: Eduardo Habkost --- hw/Makefile.objs | 2 +- hw/qdev-core.c | 716 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/qdev.c | 716 ------------------------------------------------------- 3 files changed, 717 insertions(+), 717 deletions(-) create mode 100644 hw/qdev-core.c delete mode 100644 hw/qdev.c diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 16f23c0..70f2014 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -180,7 +180,7 @@ common-obj-$(CONFIG_SD) += sd.o common-obj-y += bt.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o common-obj-y += bt-hci-csr.o common-obj-y += msmouse.o ps2.o -common-obj-y += qdev.o qdev-properties.o qdev-monitor.o +common-obj-y += qdev-core.o qdev-properties.o qdev-monitor.o common-obj-y += qdev-system.o qdev-properties-system.o common-obj-$(CONFIG_BRLAPI) += baum.o diff --git a/hw/qdev-core.c b/hw/qdev-core.c new file mode 100644 index 0000000..ee19dd5 --- /dev/null +++ b/hw/qdev-core.c @@ -0,0 +1,716 @@ +/* + * Dynamic device configuration and creation. + * + * Copyright (c) 2009 CodeSourcery + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* The theory here is that it should be possible to create a machine without + knowledge of specific devices. Historically board init routines have + passed a bunch of arguments to each device, requiring the board know + exactly which device it is dealing with. This file provides an abstract + API for device configuration and initialization. Devices will generally + inherit from a particular bus (e.g. PCI or I2C) rather than + this API directly. */ + +#include "qdev.h" +#include "sysemu.h" +#include "error.h" +#include "qapi/qapi-visit-core.h" + +int qdev_hotplug = 0; +static bool qdev_hot_added = false; +static bool qdev_hot_removed = false; + +const VMStateDescription *qdev_get_vmsd(DeviceState *dev) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + return dc->vmsd; +} + +const char *qdev_fw_name(DeviceState *dev) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (dc->fw_name) { + return dc->fw_name; + } + + return object_get_typename(OBJECT(dev)); +} + +static void qdev_property_add_legacy(DeviceState *dev, Property *prop, + Error **errp); + +static void bus_remove_child(BusState *bus, DeviceState *child) +{ + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + if (kid->child == child) { + char name[32]; + + snprintf(name, sizeof(name), "child[%d]", kid->index); + QTAILQ_REMOVE(&bus->children, kid, sibling); + object_property_del(OBJECT(bus), name, NULL); + g_free(kid); + return; + } + } +} + +static void bus_add_child(BusState *bus, DeviceState *child) +{ + char name[32]; + BusChild *kid = g_malloc0(sizeof(*kid)); + + if (qdev_hotplug) { + assert(bus->allow_hotplug); + } + + kid->index = bus->max_index++; + kid->child = child; + + QTAILQ_INSERT_HEAD(&bus->children, kid, sibling); + + snprintf(name, sizeof(name), "child[%d]", kid->index); + object_property_add_link(OBJECT(bus), name, + object_get_typename(OBJECT(child)), + (Object **)&kid->child, + NULL); +} + +void qdev_set_parent_bus(DeviceState *dev, BusState *bus) +{ + dev->parent_bus = bus; + bus_add_child(bus, 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. + On failure, destroy the device and return negative value. + Return 0 on success. */ +int qdev_init(DeviceState *dev) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + int rc; + + assert(dev->state == DEV_STATE_CREATED); + + rc = dc->init(dev); + if (rc < 0) { + qdev_free(dev); + return rc; + } + + if (!OBJECT(dev)->parent) { + static int unattached_count = 0; + gchar *name = g_strdup_printf("device[%d]", unattached_count++); + + object_property_add_child(container_get(qdev_get_machine(), + "/unattached"), + name, OBJECT(dev), NULL); + g_free(name); + } + + if (qdev_get_vmsd(dev)) { + vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, + dev->instance_id_alias, + dev->alias_required_for_version); + } + dev->state = DEV_STATE_INITIALIZED; + if (dev->hotplugged) { + device_reset(dev); + } + return 0; +} + +void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, + int required_for_version) +{ + assert(dev->state == DEV_STATE_CREATED); + dev->instance_id_alias = alias_id; + dev->alias_required_for_version = required_for_version; +} + +void qdev_unplug(DeviceState *dev, Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dev->parent_bus->allow_hotplug) { + error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); + return; + } + assert(dc->unplug != NULL); + + qdev_hot_removed = true; + + if (dc->unplug(dev) < 0) { + error_set(errp, QERR_UNDEFINED_ERROR); + return; + } +} + +static int qdev_reset_one(DeviceState *dev, void *opaque) +{ + device_reset(dev); + + return 0; +} + +static int qbus_reset_one(BusState *bus, void *opaque) +{ + BusClass *bc = BUS_GET_CLASS(bus); + if (bc->reset) { + return bc->reset(bus); + } + return 0; +} + +void qdev_reset_all(DeviceState *dev) +{ + qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL); +} + +void qbus_reset_all_fn(void *opaque) +{ + BusState *bus = opaque; + qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); +} + +/* can be used as ->unplug() callback for the simple cases */ +int qdev_simple_unplug_cb(DeviceState *dev) +{ + /* just zap it */ + qdev_free(dev); + return 0; +} + + +/* Like qdev_init(), but terminate program via error_report() 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) +{ + const char *typename = object_get_typename(OBJECT(dev)); + + if (qdev_init(dev) < 0) { + error_report("Initialization of device %s failed", typename); + exit(1); + } +} + +/* Unlink device from bus and free the structure. */ +void qdev_free(DeviceState *dev) +{ + object_delete(OBJECT(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; +} + +bool qdev_machine_modified(void) +{ + return qdev_hot_added || qdev_hot_removed; +} + +BusState *qdev_get_parent_bus(DeviceState *dev) +{ + return dev->parent_bus; +} + +BusState *qdev_get_child_bus(DeviceState *dev, const char *name) +{ + BusState *bus; + + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + if (strcmp(name, bus->name) == 0) { + return bus; + } + } + return NULL; +} + +int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque) +{ + BusChild *kid; + int err; + + if (busfn) { + err = busfn(bus, opaque); + if (err) { + return err; + } + } + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + err = qdev_walk_children(kid->child, devfn, busfn, opaque); + if (err < 0) { + return err; + } + } + + return 0; +} + +int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque) +{ + BusState *bus; + int err; + + if (devfn) { + err = devfn(dev, opaque); + if (err) { + return err; + } + } + + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + err = qbus_walk_children(bus, devfn, busfn, opaque); + if (err < 0) { + return err; + } + } + + return 0; +} + +DeviceState *qdev_find_recursive(BusState *bus, const char *id) +{ + BusChild *kid; + DeviceState *ret; + BusState *child; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + + 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_realize(BusState *bus) +{ + const char *typename = object_get_typename(OBJECT(bus)); + char *buf; + int i,len; + + if (bus->name) { + /* use supplied name */ + } else if (bus->parent && bus->parent->id) { + /* parent device has id -> use it for bus name */ + len = strlen(bus->parent->id) + 16; + buf = g_malloc(len); + snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus); + bus->name = buf; + } else { + /* no id -> use lowercase bus type for bus name */ + len = strlen(typename) + 16; + buf = g_malloc(len); + len = snprintf(buf, len, "%s.%d", typename, + bus->parent ? bus->parent->num_child_bus : 0); + for (i = 0; i < len; i++) + buf[i] = qemu_tolower(buf[i]); + bus->name = buf; + } + + if (bus->parent) { + QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); + bus->parent->num_child_bus++; + object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); + } else if (bus != sysbus_get_default()) { + /* TODO: once all bus devices are qdevified, + only reset handler for main_system_bus should be registered here. */ + qemu_register_reset(qbus_reset_all_fn, bus); + } +} + +void qbus_create_inplace(BusState *bus, const char *typename, + DeviceState *parent, const char *name) +{ + object_initialize(bus, typename); + + bus->parent = parent; + bus->name = name ? g_strdup(name) : NULL; + qbus_realize(bus); +} + +BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) +{ + BusState *bus; + + bus = BUS(object_new(typename)); + bus->qom_allocated = true; + + bus->parent = parent; + bus->name = name ? g_strdup(name) : NULL; + qbus_realize(bus); + + return bus; +} + +void qbus_free(BusState *bus) +{ + if (bus->qom_allocated) { + object_delete(OBJECT(bus)); + } else { + object_finalize(OBJECT(bus)); + if (bus->glib_allocated) { + g_free(bus); + } + } +} + +static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev) +{ + BusClass *bc = BUS_GET_CLASS(bus); + + if (bc->get_fw_dev_path) { + return bc->get_fw_dev_path(dev); + } + + return NULL; +} + +static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) +{ + int l = 0; + + if (dev && dev->parent_bus) { + char *d; + l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); + d = bus_get_fw_dev_path(dev->parent_bus, dev); + if (d) { + l += snprintf(p + l, size - l, "%s", d); + g_free(d); + } else { + l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); + } + } + l += snprintf(p + l , size - l, "/"); + + return l; +} + +char* qdev_get_fw_dev_path(DeviceState *dev) +{ + char path[128]; + int l; + + l = qdev_get_fw_dev_path_helper(dev, path, 128); + + path[l-1] = '\0'; + + return g_strdup(path); +} + +char *qdev_get_dev_path(DeviceState *dev) +{ + BusClass *bc; + + if (!dev || !dev->parent_bus) { + return NULL; + } + + bc = BUS_GET_CLASS(dev->parent_bus); + if (bc->get_dev_path) { + return bc->get_dev_path(dev); + } + + return NULL; +} + +/** + * Legacy property handling + */ + +static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + + char buffer[1024]; + char *ptr = buffer; + + prop->info->print(dev, prop, buffer, sizeof(buffer)); + visit_type_str(v, &ptr, name, errp); +} + +static void qdev_set_legacy_property(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + Error *local_err = NULL; + char *ptr = NULL; + int ret; + + if (dev->state != DEV_STATE_CREATED) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + visit_type_str(v, &ptr, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ret = prop->info->parse(dev, prop, ptr); + error_set_from_qdev_prop_error(errp, ret, dev, prop, ptr); + g_free(ptr); +} + +/** + * @qdev_add_legacy_property - adds a legacy property + * + * Do not use this is new code! Properties added through this interface will + * be given names and types in the "legacy" namespace. + * + * Legacy properties are string versions of other OOM properties. The format + * of the string depends on the property type. + */ +void qdev_property_add_legacy(DeviceState *dev, Property *prop, + Error **errp) +{ + gchar *name, *type; + + /* Register pointer properties as legacy properties */ + if (!prop->info->print && !prop->info->parse && + (prop->info->set || prop->info->get)) { + return; + } + + name = g_strdup_printf("legacy-%s", prop->name); + type = g_strdup_printf("legacy<%s>", + prop->info->legacy_name ?: prop->info->name); + + object_property_add(OBJECT(dev), name, type, + prop->info->print ? qdev_get_legacy_property : prop->info->get, + prop->info->parse ? qdev_set_legacy_property : prop->info->set, + NULL, + prop, errp); + + g_free(type); + g_free(name); +} + +/** + * @qdev_property_add_static - add a @Property to a device. + * + * Static properties access data in a struct. The actual type of the + * property and the field depends on the property type. + */ +void qdev_property_add_static(DeviceState *dev, Property *prop, + Error **errp) +{ + Error *local_err = NULL; + Object *obj = OBJECT(dev); + + /* + * TODO qdev_prop_ptr does not have getters or setters. It must + * go now that it can be replaced with links. The test should be + * removed along with it: all static properties are read/write. + */ + if (!prop->info->get && !prop->info->set) { + return; + } + + object_property_add(obj, prop->name, prop->info->name, + prop->info->get, prop->info->set, + prop->info->release, + prop, &local_err); + + if (local_err) { + error_propagate(errp, local_err); + return; + } + if (prop->qtype == QTYPE_NONE) { + return; + } + + if (prop->qtype == QTYPE_QBOOL) { + object_property_set_bool(obj, prop->defval, prop->name, &local_err); + } else if (prop->info->enum_table) { + object_property_set_str(obj, prop->info->enum_table[prop->defval], + prop->name, &local_err); + } else if (prop->qtype == QTYPE_QINT) { + object_property_set_int(obj, prop->defval, prop->name, &local_err); + } + assert_no_error(local_err); +} + +static void device_initfn(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + ObjectClass *class; + Property *prop; + + if (qdev_hotplug) { + dev->hotplugged = 1; + qdev_hot_added = true; + } + + dev->instance_id_alias = -1; + dev->state = DEV_STATE_CREATED; + + class = object_get_class(OBJECT(dev)); + do { + for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) { + qdev_property_add_legacy(dev, prop, NULL); + qdev_property_add_static(dev, prop, NULL); + } + class = object_class_get_parent(class); + } while (class != object_class_by_name(TYPE_DEVICE)); + qdev_prop_set_globals(dev); + + object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS, + (Object **)&dev->parent_bus, NULL); +} + +/* Unlink device from bus and free the structure. */ +static void device_finalize(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + BusState *bus; + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (dev->state == DEV_STATE_INITIALIZED) { + while (dev->num_child_bus) { + bus = QLIST_FIRST(&dev->child_bus); + qbus_free(bus); + } + if (qdev_get_vmsd(dev)) { + vmstate_unregister(dev, qdev_get_vmsd(dev), dev); + } + if (dc->exit) { + dc->exit(dev); + } + if (dev->opts) { + qemu_opts_del(dev->opts); + } + } + if (dev->parent_bus) { + bus_remove_child(dev->parent_bus, dev); + } +} + +static void device_class_base_init(ObjectClass *class, void *data) +{ + DeviceClass *klass = DEVICE_CLASS(class); + + /* We explicitly look up properties in the superclasses, + * so do not propagate them to the subclasses. + */ + klass->props = NULL; +} + +void device_reset(DeviceState *dev) +{ + DeviceClass *klass = DEVICE_GET_CLASS(dev); + + if (klass->reset) { + klass->reset(dev); + } +} + +Object *qdev_get_machine(void) +{ + static Object *dev; + + if (dev == NULL) { + dev = container_get(object_get_root(), "/machine"); + } + + return dev; +} + +static TypeInfo device_type_info = { + .name = TYPE_DEVICE, + .parent = TYPE_OBJECT, + .instance_size = sizeof(DeviceState), + .instance_init = device_initfn, + .instance_finalize = device_finalize, + .class_base_init = device_class_base_init, + .abstract = true, + .class_size = sizeof(DeviceClass), +}; + +static void qbus_initfn(Object *obj) +{ + BusState *bus = BUS(obj); + + QTAILQ_INIT(&bus->children); +} + +static void qbus_finalize(Object *obj) +{ + BusState *bus = BUS(obj); + BusChild *kid; + + while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { + DeviceState *dev = kid->child; + qdev_free(dev); + } + if (bus->parent) { + QLIST_REMOVE(bus, sibling); + bus->parent->num_child_bus--; + } else { + assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ + qemu_unregister_reset(qbus_reset_all_fn, bus); + } + g_free((char *)bus->name); +} + +static const TypeInfo bus_info = { + .name = TYPE_BUS, + .parent = TYPE_OBJECT, + .instance_size = sizeof(BusState), + .abstract = true, + .class_size = sizeof(BusClass), + .instance_init = qbus_initfn, + .instance_finalize = qbus_finalize, +}; + +static void qdev_register_types(void) +{ + type_register_static(&bus_info); + type_register_static(&device_type_info); +} + +type_init(qdev_register_types) diff --git a/hw/qdev.c b/hw/qdev.c deleted file mode 100644 index ee19dd5..0000000 --- a/hw/qdev.c +++ /dev/null @@ -1,716 +0,0 @@ -/* - * Dynamic device configuration and creation. - * - * Copyright (c) 2009 CodeSourcery - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* The theory here is that it should be possible to create a machine without - knowledge of specific devices. Historically board init routines have - passed a bunch of arguments to each device, requiring the board know - exactly which device it is dealing with. This file provides an abstract - API for device configuration and initialization. Devices will generally - inherit from a particular bus (e.g. PCI or I2C) rather than - this API directly. */ - -#include "qdev.h" -#include "sysemu.h" -#include "error.h" -#include "qapi/qapi-visit-core.h" - -int qdev_hotplug = 0; -static bool qdev_hot_added = false; -static bool qdev_hot_removed = false; - -const VMStateDescription *qdev_get_vmsd(DeviceState *dev) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - return dc->vmsd; -} - -const char *qdev_fw_name(DeviceState *dev) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->fw_name) { - return dc->fw_name; - } - - return object_get_typename(OBJECT(dev)); -} - -static void qdev_property_add_legacy(DeviceState *dev, Property *prop, - Error **errp); - -static void bus_remove_child(BusState *bus, DeviceState *child) -{ - BusChild *kid; - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - if (kid->child == child) { - char name[32]; - - snprintf(name, sizeof(name), "child[%d]", kid->index); - QTAILQ_REMOVE(&bus->children, kid, sibling); - object_property_del(OBJECT(bus), name, NULL); - g_free(kid); - return; - } - } -} - -static void bus_add_child(BusState *bus, DeviceState *child) -{ - char name[32]; - BusChild *kid = g_malloc0(sizeof(*kid)); - - if (qdev_hotplug) { - assert(bus->allow_hotplug); - } - - kid->index = bus->max_index++; - kid->child = child; - - QTAILQ_INSERT_HEAD(&bus->children, kid, sibling); - - snprintf(name, sizeof(name), "child[%d]", kid->index); - object_property_add_link(OBJECT(bus), name, - object_get_typename(OBJECT(child)), - (Object **)&kid->child, - NULL); -} - -void qdev_set_parent_bus(DeviceState *dev, BusState *bus) -{ - dev->parent_bus = bus; - bus_add_child(bus, 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. - On failure, destroy the device and return negative value. - Return 0 on success. */ -int qdev_init(DeviceState *dev) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - int rc; - - assert(dev->state == DEV_STATE_CREATED); - - rc = dc->init(dev); - if (rc < 0) { - qdev_free(dev); - return rc; - } - - if (!OBJECT(dev)->parent) { - static int unattached_count = 0; - gchar *name = g_strdup_printf("device[%d]", unattached_count++); - - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), - name, OBJECT(dev), NULL); - g_free(name); - } - - if (qdev_get_vmsd(dev)) { - vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, - dev->instance_id_alias, - dev->alias_required_for_version); - } - dev->state = DEV_STATE_INITIALIZED; - if (dev->hotplugged) { - device_reset(dev); - } - return 0; -} - -void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, - int required_for_version) -{ - assert(dev->state == DEV_STATE_CREATED); - dev->instance_id_alias = alias_id; - dev->alias_required_for_version = required_for_version; -} - -void qdev_unplug(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (!dev->parent_bus->allow_hotplug) { - error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); - return; - } - assert(dc->unplug != NULL); - - qdev_hot_removed = true; - - if (dc->unplug(dev) < 0) { - error_set(errp, QERR_UNDEFINED_ERROR); - return; - } -} - -static int qdev_reset_one(DeviceState *dev, void *opaque) -{ - device_reset(dev); - - return 0; -} - -static int qbus_reset_one(BusState *bus, void *opaque) -{ - BusClass *bc = BUS_GET_CLASS(bus); - if (bc->reset) { - return bc->reset(bus); - } - return 0; -} - -void qdev_reset_all(DeviceState *dev) -{ - qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL); -} - -void qbus_reset_all_fn(void *opaque) -{ - BusState *bus = opaque; - qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); -} - -/* can be used as ->unplug() callback for the simple cases */ -int qdev_simple_unplug_cb(DeviceState *dev) -{ - /* just zap it */ - qdev_free(dev); - return 0; -} - - -/* Like qdev_init(), but terminate program via error_report() 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) -{ - const char *typename = object_get_typename(OBJECT(dev)); - - if (qdev_init(dev) < 0) { - error_report("Initialization of device %s failed", typename); - exit(1); - } -} - -/* Unlink device from bus and free the structure. */ -void qdev_free(DeviceState *dev) -{ - object_delete(OBJECT(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; -} - -bool qdev_machine_modified(void) -{ - return qdev_hot_added || qdev_hot_removed; -} - -BusState *qdev_get_parent_bus(DeviceState *dev) -{ - return dev->parent_bus; -} - -BusState *qdev_get_child_bus(DeviceState *dev, const char *name) -{ - BusState *bus; - - QLIST_FOREACH(bus, &dev->child_bus, sibling) { - if (strcmp(name, bus->name) == 0) { - return bus; - } - } - return NULL; -} - -int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn, - qbus_walkerfn *busfn, void *opaque) -{ - BusChild *kid; - int err; - - if (busfn) { - err = busfn(bus, opaque); - if (err) { - return err; - } - } - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - err = qdev_walk_children(kid->child, devfn, busfn, opaque); - if (err < 0) { - return err; - } - } - - return 0; -} - -int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn, - qbus_walkerfn *busfn, void *opaque) -{ - BusState *bus; - int err; - - if (devfn) { - err = devfn(dev, opaque); - if (err) { - return err; - } - } - - QLIST_FOREACH(bus, &dev->child_bus, sibling) { - err = qbus_walk_children(bus, devfn, busfn, opaque); - if (err < 0) { - return err; - } - } - - return 0; -} - -DeviceState *qdev_find_recursive(BusState *bus, const char *id) -{ - BusChild *kid; - DeviceState *ret; - BusState *child; - - QTAILQ_FOREACH(kid, &bus->children, sibling) { - DeviceState *dev = kid->child; - - 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_realize(BusState *bus) -{ - const char *typename = object_get_typename(OBJECT(bus)); - char *buf; - int i,len; - - if (bus->name) { - /* use supplied name */ - } else if (bus->parent && bus->parent->id) { - /* parent device has id -> use it for bus name */ - len = strlen(bus->parent->id) + 16; - buf = g_malloc(len); - snprintf(buf, len, "%s.%d", bus->parent->id, bus->parent->num_child_bus); - bus->name = buf; - } else { - /* no id -> use lowercase bus type for bus name */ - len = strlen(typename) + 16; - buf = g_malloc(len); - len = snprintf(buf, len, "%s.%d", typename, - bus->parent ? bus->parent->num_child_bus : 0); - for (i = 0; i < len; i++) - buf[i] = qemu_tolower(buf[i]); - bus->name = buf; - } - - if (bus->parent) { - QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); - bus->parent->num_child_bus++; - object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus), NULL); - } else if (bus != sysbus_get_default()) { - /* TODO: once all bus devices are qdevified, - only reset handler for main_system_bus should be registered here. */ - qemu_register_reset(qbus_reset_all_fn, bus); - } -} - -void qbus_create_inplace(BusState *bus, const char *typename, - DeviceState *parent, const char *name) -{ - object_initialize(bus, typename); - - bus->parent = parent; - bus->name = name ? g_strdup(name) : NULL; - qbus_realize(bus); -} - -BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) -{ - BusState *bus; - - bus = BUS(object_new(typename)); - bus->qom_allocated = true; - - bus->parent = parent; - bus->name = name ? g_strdup(name) : NULL; - qbus_realize(bus); - - return bus; -} - -void qbus_free(BusState *bus) -{ - if (bus->qom_allocated) { - object_delete(OBJECT(bus)); - } else { - object_finalize(OBJECT(bus)); - if (bus->glib_allocated) { - g_free(bus); - } - } -} - -static char *bus_get_fw_dev_path(BusState *bus, DeviceState *dev) -{ - BusClass *bc = BUS_GET_CLASS(bus); - - if (bc->get_fw_dev_path) { - return bc->get_fw_dev_path(dev); - } - - return NULL; -} - -static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) -{ - int l = 0; - - if (dev && dev->parent_bus) { - char *d; - l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); - d = bus_get_fw_dev_path(dev->parent_bus, dev); - if (d) { - l += snprintf(p + l, size - l, "%s", d); - g_free(d); - } else { - l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); - } - } - l += snprintf(p + l , size - l, "/"); - - return l; -} - -char* qdev_get_fw_dev_path(DeviceState *dev) -{ - char path[128]; - int l; - - l = qdev_get_fw_dev_path_helper(dev, path, 128); - - path[l-1] = '\0'; - - return g_strdup(path); -} - -char *qdev_get_dev_path(DeviceState *dev) -{ - BusClass *bc; - - if (!dev || !dev->parent_bus) { - return NULL; - } - - bc = BUS_GET_CLASS(dev->parent_bus); - if (bc->get_dev_path) { - return bc->get_dev_path(dev); - } - - return NULL; -} - -/** - * Legacy property handling - */ - -static void qdev_get_legacy_property(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - - char buffer[1024]; - char *ptr = buffer; - - prop->info->print(dev, prop, buffer, sizeof(buffer)); - visit_type_str(v, &ptr, name, errp); -} - -static void qdev_set_legacy_property(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - Error *local_err = NULL; - char *ptr = NULL; - int ret; - - if (dev->state != DEV_STATE_CREATED) { - error_set(errp, QERR_PERMISSION_DENIED); - return; - } - - visit_type_str(v, &ptr, name, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - ret = prop->info->parse(dev, prop, ptr); - error_set_from_qdev_prop_error(errp, ret, dev, prop, ptr); - g_free(ptr); -} - -/** - * @qdev_add_legacy_property - adds a legacy property - * - * Do not use this is new code! Properties added through this interface will - * be given names and types in the "legacy" namespace. - * - * Legacy properties are string versions of other OOM properties. The format - * of the string depends on the property type. - */ -void qdev_property_add_legacy(DeviceState *dev, Property *prop, - Error **errp) -{ - gchar *name, *type; - - /* Register pointer properties as legacy properties */ - if (!prop->info->print && !prop->info->parse && - (prop->info->set || prop->info->get)) { - return; - } - - name = g_strdup_printf("legacy-%s", prop->name); - type = g_strdup_printf("legacy<%s>", - prop->info->legacy_name ?: prop->info->name); - - object_property_add(OBJECT(dev), name, type, - prop->info->print ? qdev_get_legacy_property : prop->info->get, - prop->info->parse ? qdev_set_legacy_property : prop->info->set, - NULL, - prop, errp); - - g_free(type); - g_free(name); -} - -/** - * @qdev_property_add_static - add a @Property to a device. - * - * Static properties access data in a struct. The actual type of the - * property and the field depends on the property type. - */ -void qdev_property_add_static(DeviceState *dev, Property *prop, - Error **errp) -{ - Error *local_err = NULL; - Object *obj = OBJECT(dev); - - /* - * TODO qdev_prop_ptr does not have getters or setters. It must - * go now that it can be replaced with links. The test should be - * removed along with it: all static properties are read/write. - */ - if (!prop->info->get && !prop->info->set) { - return; - } - - object_property_add(obj, prop->name, prop->info->name, - prop->info->get, prop->info->set, - prop->info->release, - prop, &local_err); - - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (prop->qtype == QTYPE_NONE) { - return; - } - - if (prop->qtype == QTYPE_QBOOL) { - object_property_set_bool(obj, prop->defval, prop->name, &local_err); - } else if (prop->info->enum_table) { - object_property_set_str(obj, prop->info->enum_table[prop->defval], - prop->name, &local_err); - } else if (prop->qtype == QTYPE_QINT) { - object_property_set_int(obj, prop->defval, prop->name, &local_err); - } - assert_no_error(local_err); -} - -static void device_initfn(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - ObjectClass *class; - Property *prop; - - if (qdev_hotplug) { - dev->hotplugged = 1; - qdev_hot_added = true; - } - - dev->instance_id_alias = -1; - dev->state = DEV_STATE_CREATED; - - class = object_get_class(OBJECT(dev)); - do { - for (prop = DEVICE_CLASS(class)->props; prop && prop->name; prop++) { - qdev_property_add_legacy(dev, prop, NULL); - qdev_property_add_static(dev, prop, NULL); - } - class = object_class_get_parent(class); - } while (class != object_class_by_name(TYPE_DEVICE)); - qdev_prop_set_globals(dev); - - object_property_add_link(OBJECT(dev), "parent_bus", TYPE_BUS, - (Object **)&dev->parent_bus, NULL); -} - -/* Unlink device from bus and free the structure. */ -static void device_finalize(Object *obj) -{ - DeviceState *dev = DEVICE(obj); - BusState *bus; - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dev->state == DEV_STATE_INITIALIZED) { - while (dev->num_child_bus) { - bus = QLIST_FIRST(&dev->child_bus); - qbus_free(bus); - } - if (qdev_get_vmsd(dev)) { - vmstate_unregister(dev, qdev_get_vmsd(dev), dev); - } - if (dc->exit) { - dc->exit(dev); - } - if (dev->opts) { - qemu_opts_del(dev->opts); - } - } - if (dev->parent_bus) { - bus_remove_child(dev->parent_bus, dev); - } -} - -static void device_class_base_init(ObjectClass *class, void *data) -{ - DeviceClass *klass = DEVICE_CLASS(class); - - /* We explicitly look up properties in the superclasses, - * so do not propagate them to the subclasses. - */ - klass->props = NULL; -} - -void device_reset(DeviceState *dev) -{ - DeviceClass *klass = DEVICE_GET_CLASS(dev); - - if (klass->reset) { - klass->reset(dev); - } -} - -Object *qdev_get_machine(void) -{ - static Object *dev; - - if (dev == NULL) { - dev = container_get(object_get_root(), "/machine"); - } - - return dev; -} - -static TypeInfo device_type_info = { - .name = TYPE_DEVICE, - .parent = TYPE_OBJECT, - .instance_size = sizeof(DeviceState), - .instance_init = device_initfn, - .instance_finalize = device_finalize, - .class_base_init = device_class_base_init, - .abstract = true, - .class_size = sizeof(DeviceClass), -}; - -static void qbus_initfn(Object *obj) -{ - BusState *bus = BUS(obj); - - QTAILQ_INIT(&bus->children); -} - -static void qbus_finalize(Object *obj) -{ - BusState *bus = BUS(obj); - BusChild *kid; - - while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { - DeviceState *dev = kid->child; - qdev_free(dev); - } - if (bus->parent) { - QLIST_REMOVE(bus, sibling); - bus->parent->num_child_bus--; - } else { - assert(bus != sysbus_get_default()); /* main_system_bus is never freed */ - qemu_unregister_reset(qbus_reset_all_fn, bus); - } - g_free((char *)bus->name); -} - -static const TypeInfo bus_info = { - .name = TYPE_BUS, - .parent = TYPE_OBJECT, - .instance_size = sizeof(BusState), - .abstract = true, - .class_size = sizeof(BusClass), - .instance_init = qbus_initfn, - .instance_finalize = qbus_finalize, -}; - -static void qdev_register_types(void) -{ - type_register_static(&bus_info); - type_register_static(&device_type_info); -} - -type_init(qdev_register_types)