/* * 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 . */ #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(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_create_inplace(void *bus, size_t size, const char *typename, DeviceState *parent, const char *name) { object_initialize(bus, size, typename); qbus_init(bus, parent, name); } BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) { BusState *bus; bus = BUS(object_new(typename)); qbus_init(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))); } /** * bus_phases_reset: * Transition reset method for buses to allow moving * smoothly from legacy reset method to multi-phases */ static void bus_phases_reset(BusState *bus) { ResettableClass *rc = RESETTABLE_GET_CLASS(bus); if (rc->phases.enter) { rc->phases.enter(OBJECT(bus), RESET_TYPE_COLD); } if (rc->phases.hold) { rc->phases.hold(OBJECT(bus)); } if (rc->phases.exit) { rc->phases.exit(OBJECT(bus)); } } static void bus_transitional_reset(Object *obj) { BusClass *bc = BUS_GET_CLASS(obj); /* * This will call either @bus_phases_reset (for multi-phases transitioned * buses) or a bus's specific method for not-yet transitioned buses. * In both case, it does not reset children. */ if (bc->reset) { bc->reset(BUS(obj)); } } /** * bus_get_transitional_reset: * check if the bus's class is ready for multi-phase */ static ResettableTrFunction bus_get_transitional_reset(Object *obj) { BusClass *dc = BUS_GET_CLASS(obj); if (dc->reset != bus_phases_reset) { /* * dc->reset has been overridden by a subclass, * the bus is not ready for multi phase yet. */ return bus_transitional_reset; } return NULL; } 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; /* * @bus_phases_reset is put as the default reset method below, allowing * to do the multi-phase transition from base classes to leaf classes. It * allows a legacy-reset Bus class to extend a multi-phases-reset * Bus class for the following reason: * + If a base class B has been moved to multi-phase, then it does not * override this default reset method and may have defined phase methods. * + A child class C (extending class B) which uses * bus_class_set_parent_reset() (or similar means) to override the * reset method will still work as expected. @bus_phases_reset function * will be registered as the parent reset method and effectively call * parent reset phases. */ bc->reset = bus_phases_reset; rc->get_transitional_function = bus_get_transitional_reset; } 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)