|  | /* | 
|  | *  Dynamic device configuration and creation -- buses. | 
|  | * | 
|  | *  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.1 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 <http://www.gnu.org/licenses/>. | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "hw/qdev-properties.h" | 
|  | #include "qemu/ctype.h" | 
|  | #include "qemu/module.h" | 
|  | #include "qapi/error.h" | 
|  |  | 
|  | void qbus_set_hotplug_handler(BusState *bus, Object *handler) | 
|  | { | 
|  | object_property_set_link(OBJECT(bus), QDEV_HOTPLUG_HANDLER_PROPERTY, | 
|  | handler, &error_abort); | 
|  | } | 
|  |  | 
|  | void qbus_set_bus_hotplug_handler(BusState *bus) | 
|  | { | 
|  | qbus_set_hotplug_handler(bus, OBJECT(bus)); | 
|  | } | 
|  |  | 
|  | int qbus_walk_children(BusState *bus, | 
|  | qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, | 
|  | qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, | 
|  | void *opaque) | 
|  | { | 
|  | BusChild *kid; | 
|  | int err; | 
|  |  | 
|  | if (pre_busfn) { | 
|  | err = pre_busfn(bus, opaque); | 
|  | if (err) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | WITH_RCU_READ_LOCK_GUARD() { | 
|  | QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { | 
|  | err = qdev_walk_children(kid->child, | 
|  | pre_devfn, pre_busfn, | 
|  | post_devfn, post_busfn, opaque); | 
|  | if (err < 0) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (post_busfn) { | 
|  | err = post_busfn(bus, opaque); | 
|  | if (err) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void bus_cold_reset(BusState *bus) | 
|  | { | 
|  | resettable_reset(OBJECT(bus), RESET_TYPE_COLD); | 
|  | } | 
|  |  | 
|  | bool bus_is_in_reset(BusState *bus) | 
|  | { | 
|  | return resettable_is_in_reset(OBJECT(bus)); | 
|  | } | 
|  |  | 
|  | static ResettableState *bus_get_reset_state(Object *obj) | 
|  | { | 
|  | BusState *bus = BUS(obj); | 
|  | return &bus->reset; | 
|  | } | 
|  |  | 
|  | static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb, | 
|  | void *opaque, ResetType type) | 
|  | { | 
|  | BusState *bus = BUS(obj); | 
|  | BusChild *kid; | 
|  |  | 
|  | WITH_RCU_READ_LOCK_GUARD() { | 
|  | QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { | 
|  | cb(OBJECT(kid->child), opaque, type); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void qbus_init_internal(BusState *bus, DeviceState *parent, | 
|  | const char *name) | 
|  | { | 
|  | const char *typename = object_get_typename(OBJECT(bus)); | 
|  | BusClass *bc; | 
|  | int i, bus_id; | 
|  |  | 
|  | bus->parent = parent; | 
|  |  | 
|  | if (name) { | 
|  | bus->name = g_strdup(name); | 
|  | } else if (bus->parent && bus->parent->id) { | 
|  | /* parent device has id -> use it plus parent-bus-id for bus name */ | 
|  | bus_id = bus->parent->num_child_bus; | 
|  | bus->name = g_strdup_printf("%s.%d", bus->parent->id, bus_id); | 
|  | } else { | 
|  | /* no id -> use lowercase bus type plus global bus-id for bus name */ | 
|  | bc = BUS_GET_CLASS(bus); | 
|  | bus_id = bc->automatic_ids++; | 
|  | bus->name = g_strdup_printf("%s.%d", typename, bus_id); | 
|  | for (i = 0; bus->name[i]; i++) { | 
|  | bus->name[i] = qemu_tolower(bus->name[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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)); | 
|  | object_unref(OBJECT(bus)); | 
|  | } else { | 
|  | /* The only bus without a parent is the main system bus */ | 
|  | assert(bus == sysbus_get_default()); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void bus_unparent(Object *obj) | 
|  | { | 
|  | BusState *bus = BUS(obj); | 
|  | BusChild *kid; | 
|  |  | 
|  | /* Only the main system bus has no parent, and that bus is never freed */ | 
|  | assert(bus->parent); | 
|  |  | 
|  | while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { | 
|  | DeviceState *dev = kid->child; | 
|  | object_unparent(OBJECT(dev)); | 
|  | } | 
|  | QLIST_REMOVE(bus, sibling); | 
|  | bus->parent->num_child_bus--; | 
|  | bus->parent = NULL; | 
|  | } | 
|  |  | 
|  | void qbus_init(void *bus, size_t size, const char *typename, | 
|  | DeviceState *parent, const char *name) | 
|  | { | 
|  | object_initialize(bus, size, typename); | 
|  | qbus_init_internal(bus, parent, name); | 
|  | } | 
|  |  | 
|  | BusState *qbus_new(const char *typename, DeviceState *parent, const char *name) | 
|  | { | 
|  | BusState *bus; | 
|  |  | 
|  | bus = BUS(object_new(typename)); | 
|  | qbus_init_internal(bus, parent, name); | 
|  |  | 
|  | return bus; | 
|  | } | 
|  |  | 
|  | bool qbus_realize(BusState *bus, Error **errp) | 
|  | { | 
|  | return object_property_set_bool(OBJECT(bus), "realized", true, errp); | 
|  | } | 
|  |  | 
|  | void qbus_unrealize(BusState *bus) | 
|  | { | 
|  | object_property_set_bool(OBJECT(bus), "realized", false, &error_abort); | 
|  | } | 
|  |  | 
|  | static bool bus_get_realized(Object *obj, Error **errp) | 
|  | { | 
|  | BusState *bus = BUS(obj); | 
|  |  | 
|  | return bus->realized; | 
|  | } | 
|  |  | 
|  | static void bus_set_realized(Object *obj, bool value, Error **errp) | 
|  | { | 
|  | BusState *bus = BUS(obj); | 
|  | BusClass *bc = BUS_GET_CLASS(bus); | 
|  | BusChild *kid; | 
|  |  | 
|  | if (value && !bus->realized) { | 
|  | if (bc->realize) { | 
|  | bc->realize(bus, errp); | 
|  | } | 
|  |  | 
|  | /* TODO: recursive realization */ | 
|  | } else if (!value && bus->realized) { | 
|  | WITH_RCU_READ_LOCK_GUARD() { | 
|  | QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { | 
|  | DeviceState *dev = kid->child; | 
|  | qdev_unrealize(dev); | 
|  | } | 
|  | } | 
|  | if (bc->unrealize) { | 
|  | bc->unrealize(bus); | 
|  | } | 
|  | } | 
|  |  | 
|  | bus->realized = value; | 
|  | } | 
|  |  | 
|  | static void qbus_initfn(Object *obj) | 
|  | { | 
|  | BusState *bus = BUS(obj); | 
|  |  | 
|  | QTAILQ_INIT(&bus->children); | 
|  | object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, | 
|  | TYPE_HOTPLUG_HANDLER, | 
|  | (Object **)&bus->hotplug_handler, | 
|  | object_property_allow_set_link, | 
|  | 0); | 
|  | object_property_add_bool(obj, "realized", | 
|  | bus_get_realized, bus_set_realized); | 
|  | } | 
|  |  | 
|  | static char *default_bus_get_fw_dev_path(DeviceState *dev) | 
|  | { | 
|  | return g_strdup(object_get_typename(OBJECT(dev))); | 
|  | } | 
|  |  | 
|  | static void bus_class_init(ObjectClass *class, void *data) | 
|  | { | 
|  | BusClass *bc = BUS_CLASS(class); | 
|  | ResettableClass *rc = RESETTABLE_CLASS(class); | 
|  |  | 
|  | class->unparent = bus_unparent; | 
|  | bc->get_fw_dev_path = default_bus_get_fw_dev_path; | 
|  |  | 
|  | rc->get_state = bus_get_reset_state; | 
|  | rc->child_foreach = bus_reset_child_foreach; | 
|  | } | 
|  |  | 
|  | static void qbus_finalize(Object *obj) | 
|  | { | 
|  | BusState *bus = BUS(obj); | 
|  |  | 
|  | g_free(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, | 
|  | .class_init = bus_class_init, | 
|  | .interfaces = (InterfaceInfo[]) { | 
|  | { TYPE_RESETTABLE_INTERFACE }, | 
|  | { } | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static void bus_register_types(void) | 
|  | { | 
|  | type_register_static(&bus_info); | 
|  | } | 
|  |  | 
|  | type_init(bus_register_types) |