]>
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 | |
9 | * version 2 of the License, or (at your option) any later version. | |
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 | ||
52 | QTAILQ_FOREACH(kid, &bus->children, sibling) { | |
53 | err = qdev_walk_children(kid->child, | |
54 | pre_devfn, pre_busfn, | |
55 | post_devfn, post_busfn, opaque); | |
56 | if (err < 0) { | |
57 | return err; | |
58 | } | |
59 | } | |
60 | ||
61 | if (post_busfn) { | |
62 | err = post_busfn(bus, opaque); | |
63 | if (err) { | |
64 | return err; | |
65 | } | |
66 | } | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
abb89dbf DH |
71 | void bus_cold_reset(BusState *bus) |
72 | { | |
73 | resettable_reset(OBJECT(bus), RESET_TYPE_COLD); | |
74 | } | |
75 | ||
c11256aa DH |
76 | bool bus_is_in_reset(BusState *bus) |
77 | { | |
78 | return resettable_is_in_reset(OBJECT(bus)); | |
79 | } | |
80 | ||
81 | static ResettableState *bus_get_reset_state(Object *obj) | |
82 | { | |
83 | BusState *bus = BUS(obj); | |
84 | return &bus->reset; | |
85 | } | |
86 | ||
87 | static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb, | |
88 | void *opaque, ResetType type) | |
89 | { | |
90 | BusState *bus = BUS(obj); | |
91 | BusChild *kid; | |
92 | ||
93 | QTAILQ_FOREACH(kid, &bus->children, sibling) { | |
94 | cb(OBJECT(kid->child), opaque, type); | |
95 | } | |
96 | } | |
97 | ||
30884d1b | 98 | static void qbus_init(BusState *bus, DeviceState *parent, const char *name) |
a62c8911 AF |
99 | { |
100 | const char *typename = object_get_typename(OBJECT(bus)); | |
101 | BusClass *bc; | |
f73480c3 | 102 | int i, bus_id; |
a62c8911 AF |
103 | |
104 | bus->parent = parent; | |
105 | ||
106 | if (name) { | |
107 | bus->name = g_strdup(name); | |
108 | } else if (bus->parent && bus->parent->id) { | |
109 | /* parent device has id -> use it plus parent-bus-id for bus name */ | |
110 | bus_id = bus->parent->num_child_bus; | |
f73480c3 | 111 | bus->name = g_strdup_printf("%s.%d", bus->parent->id, bus_id); |
a62c8911 AF |
112 | } else { |
113 | /* no id -> use lowercase bus type plus global bus-id for bus name */ | |
114 | bc = BUS_GET_CLASS(bus); | |
115 | bus_id = bc->automatic_ids++; | |
f73480c3 MAL |
116 | bus->name = g_strdup_printf("%s.%d", typename, bus_id); |
117 | for (i = 0; bus->name[i]; i++) { | |
118 | bus->name[i] = qemu_tolower(bus->name[i]); | |
a62c8911 | 119 | } |
a62c8911 AF |
120 | } |
121 | ||
122 | if (bus->parent) { | |
123 | QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); | |
124 | bus->parent->num_child_bus++; | |
d2623129 | 125 | object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus)); |
0d1e8d6f | 126 | object_unref(OBJECT(bus)); |
be1ba4d5 PM |
127 | } else { |
128 | /* The only bus without a parent is the main system bus */ | |
129 | assert(bus == sysbus_get_default()); | |
a62c8911 AF |
130 | } |
131 | } | |
132 | ||
133 | static void bus_unparent(Object *obj) | |
134 | { | |
135 | BusState *bus = BUS(obj); | |
136 | BusChild *kid; | |
137 | ||
be1ba4d5 PM |
138 | /* Only the main system bus has no parent, and that bus is never freed */ |
139 | assert(bus->parent); | |
140 | ||
a62c8911 AF |
141 | while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { |
142 | DeviceState *dev = kid->child; | |
143 | object_unparent(OBJECT(dev)); | |
144 | } | |
be1ba4d5 PM |
145 | QLIST_REMOVE(bus, sibling); |
146 | bus->parent->num_child_bus--; | |
147 | bus->parent = NULL; | |
a62c8911 AF |
148 | } |
149 | ||
150 | void qbus_create_inplace(void *bus, size_t size, const char *typename, | |
151 | DeviceState *parent, const char *name) | |
152 | { | |
153 | object_initialize(bus, size, typename); | |
30884d1b | 154 | qbus_init(bus, parent, name); |
a62c8911 AF |
155 | } |
156 | ||
157 | BusState *qbus_create(const char *typename, DeviceState *parent, const char *name) | |
158 | { | |
159 | BusState *bus; | |
160 | ||
161 | bus = BUS(object_new(typename)); | |
30884d1b | 162 | qbus_init(bus, parent, name); |
a62c8911 AF |
163 | |
164 | return bus; | |
165 | } | |
166 | ||
9940b2cf MA |
167 | bool qbus_realize(BusState *bus, Error **errp) |
168 | { | |
f07ad48d | 169 | return object_property_set_bool(OBJECT(bus), "realized", true, errp); |
9940b2cf MA |
170 | } |
171 | ||
172 | void qbus_unrealize(BusState *bus) | |
173 | { | |
5325cc34 | 174 | object_property_set_bool(OBJECT(bus), "realized", false, &error_abort); |
9940b2cf MA |
175 | } |
176 | ||
a62c8911 AF |
177 | static bool bus_get_realized(Object *obj, Error **errp) |
178 | { | |
179 | BusState *bus = BUS(obj); | |
180 | ||
181 | return bus->realized; | |
182 | } | |
183 | ||
184 | static void bus_set_realized(Object *obj, bool value, Error **errp) | |
185 | { | |
186 | BusState *bus = BUS(obj); | |
187 | BusClass *bc = BUS_GET_CLASS(bus); | |
188 | BusChild *kid; | |
a62c8911 AF |
189 | |
190 | if (value && !bus->realized) { | |
191 | if (bc->realize) { | |
b69c3c21 | 192 | bc->realize(bus, errp); |
a62c8911 AF |
193 | } |
194 | ||
195 | /* TODO: recursive realization */ | |
196 | } else if (!value && bus->realized) { | |
197 | QTAILQ_FOREACH(kid, &bus->children, sibling) { | |
198 | DeviceState *dev = kid->child; | |
981c3dcd | 199 | qdev_unrealize(dev); |
a62c8911 | 200 | } |
b69c3c21 MA |
201 | if (bc->unrealize) { |
202 | bc->unrealize(bus); | |
a62c8911 AF |
203 | } |
204 | } | |
205 | ||
a62c8911 AF |
206 | bus->realized = value; |
207 | } | |
208 | ||
209 | static void qbus_initfn(Object *obj) | |
210 | { | |
211 | BusState *bus = BUS(obj); | |
212 | ||
213 | QTAILQ_INIT(&bus->children); | |
214 | object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, | |
215 | TYPE_HOTPLUG_HANDLER, | |
216 | (Object **)&bus->hotplug_handler, | |
217 | object_property_allow_set_link, | |
d2623129 | 218 | 0); |
a62c8911 | 219 | object_property_add_bool(obj, "realized", |
d2623129 | 220 | bus_get_realized, bus_set_realized); |
a62c8911 AF |
221 | } |
222 | ||
223 | static char *default_bus_get_fw_dev_path(DeviceState *dev) | |
224 | { | |
225 | return g_strdup(object_get_typename(OBJECT(dev))); | |
226 | } | |
227 | ||
c11256aa DH |
228 | /** |
229 | * bus_phases_reset: | |
230 | * Transition reset method for buses to allow moving | |
231 | * smoothly from legacy reset method to multi-phases | |
232 | */ | |
233 | static void bus_phases_reset(BusState *bus) | |
234 | { | |
235 | ResettableClass *rc = RESETTABLE_GET_CLASS(bus); | |
236 | ||
237 | if (rc->phases.enter) { | |
238 | rc->phases.enter(OBJECT(bus), RESET_TYPE_COLD); | |
239 | } | |
240 | if (rc->phases.hold) { | |
241 | rc->phases.hold(OBJECT(bus)); | |
242 | } | |
243 | if (rc->phases.exit) { | |
244 | rc->phases.exit(OBJECT(bus)); | |
245 | } | |
246 | } | |
247 | ||
248 | static void bus_transitional_reset(Object *obj) | |
249 | { | |
250 | BusClass *bc = BUS_GET_CLASS(obj); | |
251 | ||
252 | /* | |
253 | * This will call either @bus_phases_reset (for multi-phases transitioned | |
254 | * buses) or a bus's specific method for not-yet transitioned buses. | |
255 | * In both case, it does not reset children. | |
256 | */ | |
257 | if (bc->reset) { | |
258 | bc->reset(BUS(obj)); | |
259 | } | |
260 | } | |
261 | ||
262 | /** | |
263 | * bus_get_transitional_reset: | |
264 | * check if the bus's class is ready for multi-phase | |
265 | */ | |
266 | static ResettableTrFunction bus_get_transitional_reset(Object *obj) | |
267 | { | |
268 | BusClass *dc = BUS_GET_CLASS(obj); | |
269 | if (dc->reset != bus_phases_reset) { | |
270 | /* | |
271 | * dc->reset has been overridden by a subclass, | |
272 | * the bus is not ready for multi phase yet. | |
273 | */ | |
274 | return bus_transitional_reset; | |
275 | } | |
276 | return NULL; | |
277 | } | |
278 | ||
a62c8911 AF |
279 | static void bus_class_init(ObjectClass *class, void *data) |
280 | { | |
281 | BusClass *bc = BUS_CLASS(class); | |
c11256aa | 282 | ResettableClass *rc = RESETTABLE_CLASS(class); |
a62c8911 AF |
283 | |
284 | class->unparent = bus_unparent; | |
285 | bc->get_fw_dev_path = default_bus_get_fw_dev_path; | |
c11256aa DH |
286 | |
287 | rc->get_state = bus_get_reset_state; | |
288 | rc->child_foreach = bus_reset_child_foreach; | |
289 | ||
290 | /* | |
291 | * @bus_phases_reset is put as the default reset method below, allowing | |
292 | * to do the multi-phase transition from base classes to leaf classes. It | |
293 | * allows a legacy-reset Bus class to extend a multi-phases-reset | |
294 | * Bus class for the following reason: | |
295 | * + If a base class B has been moved to multi-phase, then it does not | |
296 | * override this default reset method and may have defined phase methods. | |
297 | * + A child class C (extending class B) which uses | |
298 | * bus_class_set_parent_reset() (or similar means) to override the | |
299 | * reset method will still work as expected. @bus_phases_reset function | |
300 | * will be registered as the parent reset method and effectively call | |
301 | * parent reset phases. | |
302 | */ | |
303 | bc->reset = bus_phases_reset; | |
304 | rc->get_transitional_function = bus_get_transitional_reset; | |
a62c8911 AF |
305 | } |
306 | ||
307 | static void qbus_finalize(Object *obj) | |
308 | { | |
309 | BusState *bus = BUS(obj); | |
310 | ||
f73480c3 | 311 | g_free(bus->name); |
a62c8911 AF |
312 | } |
313 | ||
314 | static const TypeInfo bus_info = { | |
315 | .name = TYPE_BUS, | |
316 | .parent = TYPE_OBJECT, | |
317 | .instance_size = sizeof(BusState), | |
318 | .abstract = true, | |
319 | .class_size = sizeof(BusClass), | |
320 | .instance_init = qbus_initfn, | |
321 | .instance_finalize = qbus_finalize, | |
322 | .class_init = bus_class_init, | |
c11256aa DH |
323 | .interfaces = (InterfaceInfo[]) { |
324 | { TYPE_RESETTABLE_INTERFACE }, | |
325 | { } | |
326 | }, | |
a62c8911 AF |
327 | }; |
328 | ||
329 | static void bus_register_types(void) | |
330 | { | |
331 | type_register_static(&bus_info); | |
332 | } | |
333 | ||
334 | type_init(bus_register_types) |