/* * 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_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)