]>
Commit | Line | Data |
---|---|---|
a62c8911 AF |
1 | /* |
2 | * Dynamic device configuration and creation -- buses. | |
3 | * | |
4 | * Copyright (c) 2009 CodeSourcery | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
61f3c91a | 9 | * version 2.1 of the License, or (at your option) any later version. |
a62c8911 AF |
10 | * |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "qemu/osdep.h" | |
a27bd6c7 | 21 | #include "hw/qdev-properties.h" |
856dfd8a | 22 | #include "qemu/ctype.h" |
0b8fa32f | 23 | #include "qemu/module.h" |
a62c8911 AF |
24 | #include "qapi/error.h" |
25 | ||
9bc6bfdf | 26 | void qbus_set_hotplug_handler(BusState *bus, Object *handler) |
a62c8911 | 27 | { |
5325cc34 MA |
28 | object_property_set_link(OBJECT(bus), QDEV_HOTPLUG_HANDLER_PROPERTY, |
29 | handler, &error_abort); | |
a62c8911 AF |
30 | } |
31 | ||
cd7c8660 | 32 | void qbus_set_bus_hotplug_handler(BusState *bus) |
a62c8911 | 33 | { |
9bc6bfdf | 34 | qbus_set_hotplug_handler(bus, OBJECT(bus)); |
a62c8911 AF |
35 | } |
36 | ||
37 | int qbus_walk_children(BusState *bus, | |
38 | qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn, | |
39 | qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn, | |
40 | void *opaque) | |
41 | { | |
42 | BusChild *kid; | |
43 | int err; | |
44 | ||
45 | if (pre_busfn) { | |
46 | err = pre_busfn(bus, opaque); | |
47 | if (err) { | |
48 | return err; | |
49 | } | |
50 | } | |
51 | ||
2d24a646 ML |
52 | WITH_RCU_READ_LOCK_GUARD() { |
53 | QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { | |
54 | err = qdev_walk_children(kid->child, | |
55 | pre_devfn, pre_busfn, | |
56 | post_devfn, post_busfn, opaque); | |
57 | if (err < 0) { | |
58 | return err; | |
59 | } | |
a62c8911 AF |
60 | } |
61 | } | |
62 | ||
63 | if (post_busfn) { | |
64 | err = post_busfn(bus, opaque); | |
65 | if (err) { | |
66 | return err; | |
67 | } | |
68 | } | |
69 | ||
70 | return 0; | |
71 | } | |
72 | ||
abb89dbf DH |
73 | void bus_cold_reset(BusState *bus) |
74 | { | |
75 | resettable_reset(OBJECT(bus), RESET_TYPE_COLD); | |
76 | } | |
77 | ||
c11256aa DH |
78 | bool bus_is_in_reset(BusState *bus) |
79 | { | |
80 | return resettable_is_in_reset(OBJECT(bus)); | |
81 | } | |
82 | ||
83 | static ResettableState *bus_get_reset_state(Object *obj) | |
84 | { | |
85 | BusState *bus = BUS(obj); | |
86 | return &bus->reset; | |
87 | } | |
88 | ||
89 | static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb, | |
90 | void *opaque, ResetType type) | |
91 | { | |
92 | BusState *bus = BUS(obj); | |
93 | BusChild *kid; | |
94 | ||
2d24a646 ML |
95 | WITH_RCU_READ_LOCK_GUARD() { |
96 | QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { | |
97 | cb(OBJECT(kid->child), opaque, type); | |
98 | } | |
c11256aa DH |
99 | } |
100 | } | |
101 | ||
30884d1b | 102 | static void qbus_init(BusState *bus, DeviceState *parent, const char *name) |
a62c8911 AF |
103 | { |
104 | const char *typename = object_get_typename(OBJECT(bus)); | |
105 | BusClass *bc; | |
f73480c3 | 106 | int i, bus_id; |
a62c8911 AF |
107 | |
108 | bus->parent = parent; | |
109 | ||
110 | if (name) { | |
111 | bus->name = g_strdup(name); | |
112 | } else if (bus->parent && bus->parent->id) { | |
113 | /* parent device has id -> use it plus parent-bus-id for bus name */ | |
114 | bus_id = bus->parent->num_child_bus; | |
f73480c3 | 115 | bus->name = g_strdup_printf("%s.%d", bus->parent->id, bus_id); |
a62c8911 AF |
116 | } else { |
117 | /* no id -> use lowercase bus type plus global bus-id for bus name */ | |
118 | bc = BUS_GET_CLASS(bus); | |
119 | bus_id = bc->automatic_ids++; | |
f73480c3 MAL |
120 | bus->name = g_strdup_printf("%s.%d", typename, bus_id); |
121 | for (i = 0; bus->name[i]; i++) { | |
122 | bus->name[i] = qemu_tolower(bus->name[i]); | |
a62c8911 | 123 | } |
a62c8911 AF |
124 | } |
125 | ||
126 | if (bus->parent) { | |
127 | QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); | |
128 | bus->parent->num_child_bus++; | |
d2623129 | 129 | object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus)); |
0d1e8d6f | 130 | object_unref(OBJECT(bus)); |
be1ba4d5 PM |
131 | } else { |
132 | /* The only bus without a parent is the main system bus */ | |
133 | assert(bus == sysbus_get_default()); | |
a62c8911 AF |
134 | } |
135 | } | |
136 | ||
137 | static void bus_unparent(Object *obj) | |
138 | { | |
139 | BusState *bus = BUS(obj); | |
140 | BusChild *kid; | |
141 | ||
be1ba4d5 PM |
142 | /* Only the main system bus has no parent, and that bus is never freed */ |
143 | assert(bus->parent); | |
144 | ||
a62c8911 AF |
145 | while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { |
146 | DeviceState *dev = kid->child; | |
147 | object_unparent(OBJECT(dev)); | |
148 | } | |
be1ba4d5 PM |
149 | QLIST_REMOVE(bus, sibling); |
150 | bus->parent->num_child_bus--; | |
151 | bus->parent = NULL; | |
a62c8911 AF |
152 | } |
153 | ||
154 | void qbus_create_inplace(void *bus, size_t size, const char *typename, | |
155 | DeviceState *parent, const char *name) | |
156 | { | |
157 | object_initialize(bus, size, typename); | |
30884d1b | 158 | qbus_init(bus, parent, name); |
a62c8911 AF |
159 | } |
160 | ||
161 | BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) | |
162 | { | |
163 | BusState *bus; | |
164 | ||
165 | bus = BUS(object_new(typename)); | |
30884d1b | 166 | qbus_init(bus, parent, name); |
a62c8911 AF |
167 | |
168 | return bus; | |
169 | } | |
170 | ||
9940b2cf MA |
171 | bool qbus_realize(BusState *bus, Error **errp) |
172 | { | |
f07ad48d | 173 | return object_property_set_bool(OBJECT(bus), "realized", true, errp); |
9940b2cf MA |
174 | } |
175 | ||
176 | void qbus_unrealize(BusState *bus) | |
177 | { | |
5325cc34 | 178 | object_property_set_bool(OBJECT(bus), "realized", false, &error_abort); |
9940b2cf MA |
179 | } |
180 | ||
a62c8911 AF |
181 | static bool bus_get_realized(Object *obj, Error **errp) |
182 | { | |
183 | BusState *bus = BUS(obj); | |
184 | ||
185 | return bus->realized; | |
186 | } | |
187 | ||
188 | static void bus_set_realized(Object *obj, bool value, Error **errp) | |
189 | { | |
190 | BusState *bus = BUS(obj); | |
191 | BusClass *bc = BUS_GET_CLASS(bus); | |
192 | BusChild *kid; | |
a62c8911 AF |
193 | |
194 | if (value && !bus->realized) { | |
195 | if (bc->realize) { | |
b69c3c21 | 196 | bc->realize(bus, errp); |
a62c8911 AF |
197 | } |
198 | ||
199 | /* TODO: recursive realization */ | |
200 | } else if (!value && bus->realized) { | |
2d24a646 ML |
201 | WITH_RCU_READ_LOCK_GUARD() { |
202 | QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { | |
203 | DeviceState *dev = kid->child; | |
204 | qdev_unrealize(dev); | |
205 | } | |
a62c8911 | 206 | } |
b69c3c21 MA |
207 | if (bc->unrealize) { |
208 | bc->unrealize(bus); | |
a62c8911 AF |
209 | } |
210 | } | |
211 | ||
a62c8911 AF |
212 | bus->realized = value; |
213 | } | |
214 | ||
215 | static void qbus_initfn(Object *obj) | |
216 | { | |
217 | BusState *bus = BUS(obj); | |
218 | ||
219 | QTAILQ_INIT(&bus->children); | |
220 | object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, | |
221 | TYPE_HOTPLUG_HANDLER, | |
222 | (Object **)&bus->hotplug_handler, | |
223 | object_property_allow_set_link, | |
d2623129 | 224 | 0); |
a62c8911 | 225 | object_property_add_bool(obj, "realized", |
d2623129 | 226 | bus_get_realized, bus_set_realized); |
a62c8911 AF |
227 | } |
228 | ||
229 | static char *default_bus_get_fw_dev_path(DeviceState *dev) | |
230 | { | |
231 | return g_strdup(object_get_typename(OBJECT(dev))); | |
232 | } | |
233 | ||
c11256aa DH |
234 | /** |
235 | * bus_phases_reset: | |
236 | * Transition reset method for buses to allow moving | |
237 | * smoothly from legacy reset method to multi-phases | |
238 | */ | |
239 | static void bus_phases_reset(BusState *bus) | |
240 | { | |
241 | ResettableClass *rc = RESETTABLE_GET_CLASS(bus); | |
242 | ||
243 | if (rc->phases.enter) { | |
244 | rc->phases.enter(OBJECT(bus), RESET_TYPE_COLD); | |
245 | } | |
246 | if (rc->phases.hold) { | |
247 | rc->phases.hold(OBJECT(bus)); | |
248 | } | |
249 | if (rc->phases.exit) { | |
250 | rc->phases.exit(OBJECT(bus)); | |
251 | } | |
252 | } | |
253 | ||
254 | static void bus_transitional_reset(Object *obj) | |
255 | { | |
256 | BusClass *bc = BUS_GET_CLASS(obj); | |
257 | ||
258 | /* | |
259 | * This will call either @bus_phases_reset (for multi-phases transitioned | |
260 | * buses) or a bus's specific method for not-yet transitioned buses. | |
261 | * In both case, it does not reset children. | |
262 | */ | |
263 | if (bc->reset) { | |
264 | bc->reset(BUS(obj)); | |
265 | } | |
266 | } | |
267 | ||
268 | /** | |
269 | * bus_get_transitional_reset: | |
270 | * check if the bus's class is ready for multi-phase | |
271 | */ | |
272 | static ResettableTrFunction bus_get_transitional_reset(Object *obj) | |
273 | { | |
274 | BusClass *dc = BUS_GET_CLASS(obj); | |
275 | if (dc->reset != bus_phases_reset) { | |
276 | /* | |
277 | * dc->reset has been overridden by a subclass, | |
278 | * the bus is not ready for multi phase yet. | |
279 | */ | |
280 | return bus_transitional_reset; | |
281 | } | |
282 | return NULL; | |
283 | } | |
284 | ||
a62c8911 AF |
285 | static void bus_class_init(ObjectClass *class, void *data) |
286 | { | |
287 | BusClass *bc = BUS_CLASS(class); | |
c11256aa | 288 | ResettableClass *rc = RESETTABLE_CLASS(class); |
a62c8911 AF |
289 | |
290 | class->unparent = bus_unparent; | |
291 | bc->get_fw_dev_path = default_bus_get_fw_dev_path; | |
c11256aa DH |
292 | |
293 | rc->get_state = bus_get_reset_state; | |
294 | rc->child_foreach = bus_reset_child_foreach; | |
295 | ||
296 | /* | |
297 | * @bus_phases_reset is put as the default reset method below, allowing | |
298 | * to do the multi-phase transition from base classes to leaf classes. It | |
299 | * allows a legacy-reset Bus class to extend a multi-phases-reset | |
300 | * Bus class for the following reason: | |
301 | * + If a base class B has been moved to multi-phase, then it does not | |
302 | * override this default reset method and may have defined phase methods. | |
303 | * + A child class C (extending class B) which uses | |
304 | * bus_class_set_parent_reset() (or similar means) to override the | |
305 | * reset method will still work as expected. @bus_phases_reset function | |
306 | * will be registered as the parent reset method and effectively call | |
307 | * parent reset phases. | |
308 | */ | |
309 | bc->reset = bus_phases_reset; | |
310 | rc->get_transitional_function = bus_get_transitional_reset; | |
a62c8911 AF |
311 | } |
312 | ||
313 | static void qbus_finalize(Object *obj) | |
314 | { | |
315 | BusState *bus = BUS(obj); | |
316 | ||
f73480c3 | 317 | g_free(bus->name); |
a62c8911 AF |
318 | } |
319 | ||
320 | static const TypeInfo bus_info = { | |
321 | .name = TYPE_BUS, | |
322 | .parent = TYPE_OBJECT, | |
323 | .instance_size = sizeof(BusState), | |
324 | .abstract = true, | |
325 | .class_size = sizeof(BusClass), | |
326 | .instance_init = qbus_initfn, | |
327 | .instance_finalize = qbus_finalize, | |
328 | .class_init = bus_class_init, | |
c11256aa DH |
329 | .interfaces = (InterfaceInfo[]) { |
330 | { TYPE_RESETTABLE_INTERFACE }, | |
331 | { } | |
332 | }, | |
a62c8911 AF |
333 | }; |
334 | ||
335 | static void bus_register_types(void) | |
336 | { | |
337 | type_register_static(&bus_info); | |
338 | } | |
339 | ||
340 | type_init(bus_register_types) |