]>
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 | ||
d637e1dc PM |
102 | static void qbus_init_internal(BusState *bus, DeviceState *parent, |
103 | const char *name) | |
a62c8911 AF |
104 | { |
105 | const char *typename = object_get_typename(OBJECT(bus)); | |
106 | BusClass *bc; | |
f73480c3 | 107 | int i, bus_id; |
a62c8911 AF |
108 | |
109 | bus->parent = parent; | |
110 | ||
111 | if (name) { | |
112 | bus->name = g_strdup(name); | |
113 | } else if (bus->parent && bus->parent->id) { | |
114 | /* parent device has id -> use it plus parent-bus-id for bus name */ | |
115 | bus_id = bus->parent->num_child_bus; | |
f73480c3 | 116 | bus->name = g_strdup_printf("%s.%d", bus->parent->id, bus_id); |
a62c8911 AF |
117 | } else { |
118 | /* no id -> use lowercase bus type plus global bus-id for bus name */ | |
119 | bc = BUS_GET_CLASS(bus); | |
120 | bus_id = bc->automatic_ids++; | |
f73480c3 MAL |
121 | bus->name = g_strdup_printf("%s.%d", typename, bus_id); |
122 | for (i = 0; bus->name[i]; i++) { | |
123 | bus->name[i] = qemu_tolower(bus->name[i]); | |
a62c8911 | 124 | } |
a62c8911 AF |
125 | } |
126 | ||
127 | if (bus->parent) { | |
128 | QLIST_INSERT_HEAD(&bus->parent->child_bus, bus, sibling); | |
129 | bus->parent->num_child_bus++; | |
d2623129 | 130 | object_property_add_child(OBJECT(bus->parent), bus->name, OBJECT(bus)); |
0d1e8d6f | 131 | object_unref(OBJECT(bus)); |
be1ba4d5 PM |
132 | } else { |
133 | /* The only bus without a parent is the main system bus */ | |
134 | assert(bus == sysbus_get_default()); | |
a62c8911 AF |
135 | } |
136 | } | |
137 | ||
138 | static void bus_unparent(Object *obj) | |
139 | { | |
140 | BusState *bus = BUS(obj); | |
141 | BusChild *kid; | |
142 | ||
be1ba4d5 PM |
143 | /* Only the main system bus has no parent, and that bus is never freed */ |
144 | assert(bus->parent); | |
145 | ||
a62c8911 AF |
146 | while ((kid = QTAILQ_FIRST(&bus->children)) != NULL) { |
147 | DeviceState *dev = kid->child; | |
148 | object_unparent(OBJECT(dev)); | |
149 | } | |
be1ba4d5 PM |
150 | QLIST_REMOVE(bus, sibling); |
151 | bus->parent->num_child_bus--; | |
152 | bus->parent = NULL; | |
a62c8911 AF |
153 | } |
154 | ||
d637e1dc PM |
155 | void qbus_init(void *bus, size_t size, const char *typename, |
156 | DeviceState *parent, const char *name) | |
a62c8911 AF |
157 | { |
158 | object_initialize(bus, size, typename); | |
d637e1dc | 159 | qbus_init_internal(bus, parent, name); |
a62c8911 AF |
160 | } |
161 | ||
9388d170 | 162 | BusState *qbus_new(const char *typename, DeviceState *parent, const char *name) |
a62c8911 AF |
163 | { |
164 | BusState *bus; | |
165 | ||
166 | bus = BUS(object_new(typename)); | |
d637e1dc | 167 | qbus_init_internal(bus, parent, name); |
a62c8911 AF |
168 | |
169 | return bus; | |
170 | } | |
171 | ||
9940b2cf MA |
172 | bool qbus_realize(BusState *bus, Error **errp) |
173 | { | |
f07ad48d | 174 | return object_property_set_bool(OBJECT(bus), "realized", true, errp); |
9940b2cf MA |
175 | } |
176 | ||
177 | void qbus_unrealize(BusState *bus) | |
178 | { | |
5325cc34 | 179 | object_property_set_bool(OBJECT(bus), "realized", false, &error_abort); |
9940b2cf MA |
180 | } |
181 | ||
a62c8911 AF |
182 | static bool bus_get_realized(Object *obj, Error **errp) |
183 | { | |
184 | BusState *bus = BUS(obj); | |
185 | ||
186 | return bus->realized; | |
187 | } | |
188 | ||
189 | static void bus_set_realized(Object *obj, bool value, Error **errp) | |
190 | { | |
191 | BusState *bus = BUS(obj); | |
192 | BusClass *bc = BUS_GET_CLASS(bus); | |
193 | BusChild *kid; | |
a62c8911 AF |
194 | |
195 | if (value && !bus->realized) { | |
196 | if (bc->realize) { | |
b69c3c21 | 197 | bc->realize(bus, errp); |
a62c8911 AF |
198 | } |
199 | ||
200 | /* TODO: recursive realization */ | |
201 | } else if (!value && bus->realized) { | |
2d24a646 ML |
202 | WITH_RCU_READ_LOCK_GUARD() { |
203 | QTAILQ_FOREACH_RCU(kid, &bus->children, sibling) { | |
204 | DeviceState *dev = kid->child; | |
205 | qdev_unrealize(dev); | |
206 | } | |
a62c8911 | 207 | } |
b69c3c21 MA |
208 | if (bc->unrealize) { |
209 | bc->unrealize(bus); | |
a62c8911 AF |
210 | } |
211 | } | |
212 | ||
a62c8911 AF |
213 | bus->realized = value; |
214 | } | |
215 | ||
216 | static void qbus_initfn(Object *obj) | |
217 | { | |
218 | BusState *bus = BUS(obj); | |
219 | ||
220 | QTAILQ_INIT(&bus->children); | |
221 | object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, | |
222 | TYPE_HOTPLUG_HANDLER, | |
223 | (Object **)&bus->hotplug_handler, | |
224 | object_property_allow_set_link, | |
d2623129 | 225 | 0); |
a62c8911 | 226 | object_property_add_bool(obj, "realized", |
d2623129 | 227 | bus_get_realized, bus_set_realized); |
a62c8911 AF |
228 | } |
229 | ||
230 | static char *default_bus_get_fw_dev_path(DeviceState *dev) | |
231 | { | |
232 | return g_strdup(object_get_typename(OBJECT(dev))); | |
233 | } | |
234 | ||
235 | static void bus_class_init(ObjectClass *class, void *data) | |
236 | { | |
237 | BusClass *bc = BUS_CLASS(class); | |
c11256aa | 238 | ResettableClass *rc = RESETTABLE_CLASS(class); |
a62c8911 AF |
239 | |
240 | class->unparent = bus_unparent; | |
241 | bc->get_fw_dev_path = default_bus_get_fw_dev_path; | |
c11256aa DH |
242 | |
243 | rc->get_state = bus_get_reset_state; | |
244 | rc->child_foreach = bus_reset_child_foreach; | |
a62c8911 AF |
245 | } |
246 | ||
247 | static void qbus_finalize(Object *obj) | |
248 | { | |
249 | BusState *bus = BUS(obj); | |
250 | ||
f73480c3 | 251 | g_free(bus->name); |
a62c8911 AF |
252 | } |
253 | ||
254 | static const TypeInfo bus_info = { | |
255 | .name = TYPE_BUS, | |
256 | .parent = TYPE_OBJECT, | |
257 | .instance_size = sizeof(BusState), | |
258 | .abstract = true, | |
259 | .class_size = sizeof(BusClass), | |
260 | .instance_init = qbus_initfn, | |
261 | .instance_finalize = qbus_finalize, | |
262 | .class_init = bus_class_init, | |
c11256aa DH |
263 | .interfaces = (InterfaceInfo[]) { |
264 | { TYPE_RESETTABLE_INTERFACE }, | |
265 | { } | |
266 | }, | |
a62c8911 AF |
267 | }; |
268 | ||
269 | static void bus_register_types(void) | |
270 | { | |
271 | type_register_static(&bus_info); | |
272 | } | |
273 | ||
274 | type_init(bus_register_types) |