char name[32];
BusChild *kid = g_malloc0(sizeof(*kid));
- if (qdev_hotplug) {
- assert(qbus_is_hotpluggable(bus));
- }
-
kid->index = bus->max_index++;
kid->child = child;
object_ref(OBJECT(kid->child));
bus_add_child(bus, dev);
}
+static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler,
+ Error **errp)
+{
+
+ object_property_set_link(OBJECT(bus), OBJECT(handler),
+ QDEV_HOTPLUG_HANDLER_PROPERTY, errp);
+}
+
+void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp)
+{
+ qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp);
+}
+
+void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp)
+{
+ qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp);
+}
+
/* Create a new device. This only initializes the device state structure
and allows properties to be set. qdev_init should be called to
initialize the actual device emulation. */
return 0;
}
+static QTAILQ_HEAD(device_listeners, DeviceListener) device_listeners
+ = QTAILQ_HEAD_INITIALIZER(device_listeners);
+
+enum ListenerDirection { Forward, Reverse };
+
+#define DEVICE_LISTENER_CALL(_callback, _direction, _args...) \
+ do { \
+ DeviceListener *_listener; \
+ \
+ switch (_direction) { \
+ case Forward: \
+ QTAILQ_FOREACH(_listener, &device_listeners, link) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
+ } \
+ break; \
+ case Reverse: \
+ QTAILQ_FOREACH_REVERSE(_listener, &device_listeners, \
+ device_listeners, link) { \
+ if (_listener->_callback) { \
+ _listener->_callback(_listener, ##_args); \
+ } \
+ } \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } while (0)
+
+static int device_listener_add(DeviceState *dev, void *opaque)
+{
+ DEVICE_LISTENER_CALL(realize, Forward, dev);
+
+ return 0;
+}
+
+void device_listener_register(DeviceListener *listener)
+{
+ QTAILQ_INSERT_TAIL(&device_listeners, listener, link);
+
+ qbus_walk_children(sysbus_get_default(), NULL, NULL, device_listener_add,
+ NULL, NULL);
+}
+
+void device_listener_unregister(DeviceListener *listener)
+{
+ QTAILQ_REMOVE(&device_listeners, listener, link);
+}
+
static void device_realize(DeviceState *dev, Error **errp)
{
DeviceClass *dc = DEVICE_GET_CLASS(dev);
dev->alias_required_for_version = required_for_version;
}
+static HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
+{
+ HotplugHandler *hotplug_ctrl = NULL;
+
+ if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
+ hotplug_ctrl = dev->parent_bus->hotplug_handler;
+ } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) {
+ MachineState *machine = MACHINE(qdev_get_machine());
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+
+ if (mc->get_hotplug_handler) {
+ hotplug_ctrl = mc->get_hotplug_handler(machine, dev);
+ }
+ }
+ return hotplug_ctrl;
+}
+
void qdev_unplug(DeviceState *dev, Error **errp)
{
DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ HotplugHandler *hotplug_ctrl;
+ HotplugHandlerClass *hdc;
if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) {
error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name);
qdev_hot_removed = true;
- if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
- hotplug_handler_unplug(dev->parent_bus->hotplug_handler, dev, errp);
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ /* hotpluggable device MUST have HotplugHandler, if it doesn't
+ * then something is very wrong with it */
+ g_assert(hotplug_ctrl);
+
+ /* If device supports async unplug just request it to be done,
+ * otherwise just remove it synchronously */
+ hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl);
+ if (hdc->unplug_request) {
+ hotplug_handler_unplug_request(hotplug_ctrl, dev, errp);
} else {
- assert(dc->unplug != NULL);
- if (dc->unplug(dev) < 0) { /* legacy handler */
- error_set(errp, QERR_UNDEFINED_ERROR);
- }
+ hotplug_handler_unplug(hotplug_ctrl, dev, errp);
}
}
}
/* can be used as ->unplug() callback for the simple cases */
-int qdev_simple_unplug_cb(DeviceState *dev)
+void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
{
/* just zap it */
object_unparent(OBJECT(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
char *propname = g_strdup_printf("%s[*]", name ? name : "unnamed-gpio-out");
assert(gpio_list->num_in == 0 || !name);
- assert(gpio_list->num_out == 0);
- gpio_list->num_out = n;
- gpio_list->out = pins;
+ gpio_list->num_out += n;
for (i = 0; i < n; ++i) {
memset(&pins[i], 0, sizeof(*pins));
void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
qemu_irq pin)
{
- NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
+ char *propname = g_strdup_printf("%s[%d]",
+ name ? name : "unnamed-gpio-out", n);
+ if (pin) {
+ /* We need a name for object_property_set_link to work. If the
+ * object has a parent, object_property_add_child will come back
+ * with an error without doing anything. If it has none, it will
+ * never fail. So we can just call it with a NULL Error pointer.
+ */
+ object_property_add_child(qdev_get_machine(), "non-qdev-gpio[*]",
+ OBJECT(pin), NULL);
+ }
+ object_property_set_link(OBJECT(dev), OBJECT(pin), propname, &error_abort);
+ g_free(propname);
+}
+
+qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
+{
+ char *propname = g_strdup_printf("%s[%d]",
+ name ? name : "unnamed-gpio-out", n);
- assert(n >= 0 && n < gpio_list->num_out);
- gpio_list->out[n] = pin;
+ qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
+ NULL);
+
+ return ret;
+}
+
+/* disconnect a GPIO ouput, returning the disconnected input (if any) */
+
+static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
+ const char *name, int n)
+{
+ char *propname = g_strdup_printf("%s[%d]",
+ name ? name : "unnamed-gpio-out", n);
+
+ qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
+ NULL);
+ if (ret) {
+ object_property_set_link(OBJECT(dev), NULL, propname, NULL);
+ }
+ g_free(propname);
+ return ret;
+}
+
+qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
+ const char *name, int n)
+{
+ qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n);
+ qdev_connect_gpio_out_named(dev, name, n, icpt);
+ return disconnected;
}
void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
qdev_connect_gpio_out_named(dev, NULL, n, pin);
}
+void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
+ const char *name)
+{
+ int i;
+ NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name);
+
+ for (i = 0; i < ngl->num_in; i++) {
+ const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in";
+ char *propname = g_strdup_printf("%s[%d]", nm, i);
+
+ object_property_add_alias(OBJECT(container), propname,
+ OBJECT(dev), propname,
+ &error_abort);
+ }
+ for (i = 0; i < ngl->num_out; i++) {
+ const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out";
+ char *propname = g_strdup_printf("%s[%d]", nm, i);
+
+ object_property_add_alias(OBJECT(container), propname,
+ OBJECT(dev), propname,
+ &error_abort);
+ }
+ QLIST_REMOVE(ngl, node);
+ QLIST_INSERT_HEAD(&container->gpios, ngl, node);
+}
+
BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
{
BusState *bus;
return d;
}
+char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev)
+{
+ Object *obj = OBJECT(dev);
+
+ return fw_path_provider_try_get_dev_path(obj, bus, dev);
+}
+
static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size)
{
int l = 0;
error_propagate(errp, local_err);
return;
}
+
+ object_property_set_description(obj, prop->name,
+ prop->info->description,
+ &error_abort);
+
if (prop->qtype == QTYPE_NONE) {
return;
}
} while (class != object_class_by_name(TYPE_DEVICE));
}
+static int qdev_add_hotpluggable_device(Object *obj, void *opaque)
+{
+ GSList **list = opaque;
+ DeviceState *dev = DEVICE(obj);
+
+ if (dev->realized && object_property_get_bool(obj, "hotpluggable", NULL)) {
+ *list = g_slist_append(*list, dev);
+ }
+
+ return 0;
+}
+
+GSList *qdev_build_hotpluggable_device_list(Object *peripheral)
+{
+ GSList *list = NULL;
+
+ object_child_foreach(peripheral, qdev_add_hotpluggable_device, &list);
+
+ return list;
+}
+
static bool device_get_realized(Object *obj, Error **errp)
{
DeviceState *dev = DEVICE(obj);
{
DeviceState *dev = DEVICE(obj);
DeviceClass *dc = DEVICE_GET_CLASS(dev);
+ HotplugHandler *hotplug_ctrl;
BusState *bus;
Error *local_err = NULL;
goto fail;
}
- if (dev->parent_bus && dev->parent_bus->hotplug_handler) {
- hotplug_handler_plug(dev->parent_bus->hotplug_handler,
- dev, &local_err);
- } else if (object_dynamic_cast(qdev_get_machine(), TYPE_MACHINE)) {
- HotplugHandler *hotplug_ctrl;
- MachineState *machine = MACHINE(qdev_get_machine());
- MachineClass *mc = MACHINE_GET_CLASS(machine);
-
- if (mc->get_hotplug_handler) {
- hotplug_ctrl = mc->get_hotplug_handler(machine, dev);
- if (hotplug_ctrl) {
- hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
- }
- }
+ DEVICE_LISTENER_CALL(realize, Forward, dev);
+
+ hotplug_ctrl = qdev_get_hotplug_handler(dev);
+ if (hotplug_ctrl) {
+ hotplug_handler_plug(hotplug_ctrl, dev, &local_err);
}
if (local_err != NULL) {
dc->unrealize(dev, local_errp);
}
dev->pending_deleted_event = true;
+ DEVICE_LISTENER_CALL(unrealize, Reverse, dev);
}
if (local_err != NULL) {
NamedGPIOList *ngl, *next;
DeviceState *dev = DEVICE(obj);
- if (dev->opts) {
- qemu_opts_del(dev->opts);
- }
+ qemu_opts_del(dev->opts);
QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) {
QLIST_REMOVE(ngl, node);