blob: 7b204f9517a9fce7c8a3faca96e5b11a6d5435c1 [file] [log] [blame]
Paul Brookaae94602009-05-14 22:35:06 +01001/*
2 * Dynamic device configuration and creation.
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
Blue Swirl8167ee82009-07-16 20:47:01 +000017 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Paul Brookaae94602009-05-14 22:35:06 +010018 */
19
20/* The theory here is that it should be possible to create a machine without
21 knowledge of specific devices. Historically board init routines have
22 passed a bunch of arguments to each device, requiring the board know
23 exactly which device it is dealing with. This file provides an abstract
24 API for device configuration and initialization. Devices will generally
25 inherit from a particular bus (e.g. PCI or I2C) rather than
26 this API directly. */
27
Paul Brook9d07d752009-05-14 22:35:07 +010028#include "net.h"
Paul Brookaae94602009-05-14 22:35:06 +010029#include "qdev.h"
30#include "sysemu.h"
Gerd Hoffmanncae49562009-06-05 15:53:17 +010031#include "monitor.h"
Paul Brookaae94602009-05-14 22:35:06 +010032
Paul Brook02e2da42009-05-23 00:05:19 +010033/* This is a nasty hack to allow passing a NULL bus to qdev_create. */
Blue Swirlb9aaf7f2009-06-09 18:38:51 +000034static BusState *main_system_bus;
Paul Brook4d6ae672009-05-14 22:35:06 +010035
Gerd Hoffmann042f84d2009-06-30 14:12:09 +020036static DeviceInfo *device_info_list;
Paul Brookaae94602009-05-14 22:35:06 +010037
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +020038static BusState *qbus_find_recursive(BusState *bus, const char *name,
39 const BusInfo *info);
40static BusState *qbus_find(const char *path);
41
Paul Brookaae94602009-05-14 22:35:06 +010042/* Register a new device type. */
Gerd Hoffmann074f2ff2009-06-10 09:41:42 +020043void qdev_register(DeviceInfo *info)
Paul Brookaae94602009-05-14 22:35:06 +010044{
Gerd Hoffmann074f2ff2009-06-10 09:41:42 +020045 assert(info->size >= sizeof(DeviceState));
Gerd Hoffmann042f84d2009-06-30 14:12:09 +020046 assert(!info->next);
Paul Brookaae94602009-05-14 22:35:06 +010047
Gerd Hoffmann042f84d2009-06-30 14:12:09 +020048 info->next = device_info_list;
49 device_info_list = info;
Paul Brookaae94602009-05-14 22:35:06 +010050}
51
Gerd Hoffmann81ebb982009-07-15 13:43:32 +020052static DeviceInfo *qdev_find_info(BusInfo *bus_info, const char *name)
53{
54 DeviceInfo *info;
55
Gerd Hoffmann3320e562009-07-15 13:43:33 +020056 /* first check device names */
Gerd Hoffmann81ebb982009-07-15 13:43:32 +020057 for (info = device_info_list; info != NULL; info = info->next) {
58 if (bus_info && info->bus_info != bus_info)
59 continue;
60 if (strcmp(info->name, name) != 0)
61 continue;
62 return info;
63 }
Gerd Hoffmann3320e562009-07-15 13:43:33 +020064
65 /* failing that check the aliases */
66 for (info = device_info_list; info != NULL; info = info->next) {
67 if (bus_info && info->bus_info != bus_info)
68 continue;
69 if (!info->alias)
70 continue;
71 if (strcmp(info->alias, name) != 0)
72 continue;
73 return info;
74 }
Gerd Hoffmann81ebb982009-07-15 13:43:32 +020075 return NULL;
76}
77
Paul Brookaae94602009-05-14 22:35:06 +010078/* Create a new device. This only initializes the device state structure
79 and allows properties to be set. qdev_init should be called to
80 initialize the actual device emulation. */
Paul Brook02e2da42009-05-23 00:05:19 +010081DeviceState *qdev_create(BusState *bus, const char *name)
Paul Brookaae94602009-05-14 22:35:06 +010082{
Gerd Hoffmann042f84d2009-06-30 14:12:09 +020083 DeviceInfo *info;
Paul Brookaae94602009-05-14 22:35:06 +010084 DeviceState *dev;
85
Gerd Hoffmann10c4c982009-06-30 14:12:08 +020086 if (!bus) {
87 if (!main_system_bus) {
88 main_system_bus = qbus_create(&system_bus_info, NULL, "main-system-bus");
Paul Brookaae94602009-05-14 22:35:06 +010089 }
Gerd Hoffmann10c4c982009-06-30 14:12:08 +020090 bus = main_system_bus;
91 }
92
Gerd Hoffmann81ebb982009-07-15 13:43:32 +020093 info = qdev_find_info(bus->info, name);
Gerd Hoffmann042f84d2009-06-30 14:12:09 +020094 if (!info) {
Gerd Hoffmann10c4c982009-06-30 14:12:08 +020095 hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name);
Paul Brookaae94602009-05-14 22:35:06 +010096 }
97
Gerd Hoffmann042f84d2009-06-30 14:12:09 +020098 dev = qemu_mallocz(info->size);
99 dev->info = info;
Paul Brook02e2da42009-05-23 00:05:19 +0100100 dev->parent_bus = bus;
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200101 qdev_prop_set_defaults(dev, dev->info->props);
102 qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
Gerd Hoffmannb6b61142009-07-15 13:48:21 +0200103 qdev_prop_set_compat(dev);
Blue Swirl72cf2d42009-09-12 07:36:22 +0000104 QLIST_INSERT_HEAD(&bus->children, dev, sibling);
Gerd Hoffmann131ec1b2009-09-25 21:42:34 +0200105 dev->state = DEV_STATE_CREATED;
Paul Brookaae94602009-05-14 22:35:06 +0100106 return dev;
107}
108
Gerd Hoffmann1b524b02009-07-29 13:12:23 +0200109static int qdev_print_devinfo(DeviceInfo *info, char *dest, int len)
110{
111 int pos = 0;
Gerd Hoffmann22f2e342009-08-03 11:26:48 +0200112 int ret;
Gerd Hoffmann1b524b02009-07-29 13:12:23 +0200113
Gerd Hoffmann22f2e342009-08-03 11:26:48 +0200114 ret = snprintf(dest+pos, len-pos, "name \"%s\", bus %s",
115 info->name, info->bus_info->name);
116 pos += MIN(len-pos,ret);
117 if (info->alias) {
118 ret = snprintf(dest+pos, len-pos, ", alias \"%s\"", info->alias);
119 pos += MIN(len-pos,ret);
120 }
121 if (info->desc) {
122 ret = snprintf(dest+pos, len-pos, ", desc \"%s\"", info->desc);
123 pos += MIN(len-pos,ret);
124 }
125 if (info->no_user) {
126 ret = snprintf(dest+pos, len-pos, ", no-user");
127 pos += MIN(len-pos,ret);
128 }
Gerd Hoffmann1b524b02009-07-29 13:12:23 +0200129 return pos;
130}
131
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200132static int set_property(const char *name, const char *value, void *opaque)
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200133{
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200134 DeviceState *dev = opaque;
135
136 if (strcmp(name, "driver") == 0)
137 return 0;
138 if (strcmp(name, "bus") == 0)
139 return 0;
140
Mark McLoughlin3df04ac2009-09-23 11:24:05 +0100141 if (qdev_prop_parse(dev, name, value) == -1) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200142 qemu_error("can't set property \"%s\" to \"%s\" for \"%s\"\n",
143 name, value, dev->info->name);
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200144 return -1;
145 }
146 return 0;
147}
148
149DeviceState *qdev_device_add(QemuOpts *opts)
150{
151 const char *driver, *path, *id;
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200152 DeviceInfo *info;
153 DeviceState *qdev;
154 BusState *bus;
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200155
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200156 driver = qemu_opt_get(opts, "driver");
157 if (!driver) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200158 qemu_error("-device: no driver specified\n");
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200159 return NULL;
160 }
161 if (strcmp(driver, "?") == 0) {
Gerd Hoffmann1b524b02009-07-29 13:12:23 +0200162 char msg[256];
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200163 for (info = device_info_list; info != NULL; info = info->next) {
Gerd Hoffmann1b524b02009-07-29 13:12:23 +0200164 qdev_print_devinfo(info, msg, sizeof(msg));
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200165 qemu_error("%s\n", msg);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200166 }
167 return NULL;
168 }
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200169
170 /* find driver */
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200171 info = qdev_find_info(NULL, driver);
172 if (!info) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200173 qemu_error("Device \"%s\" not found. Try -device '?' for a list.\n",
174 driver);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200175 return NULL;
176 }
177 if (info->no_user) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200178 qemu_error("device \"%s\" can't be added via command line\n",
179 info->name);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200180 return NULL;
181 }
182
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200183 /* find bus */
184 path = qemu_opt_get(opts, "bus");
185 if (path != NULL) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200186 bus = qbus_find(path);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200187 } else {
188 bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200189 }
Gerd Hoffmann75570082009-08-31 14:23:58 +0200190 if (!bus) {
191 qemu_error("Did not find %s bus for %s\n",
192 path ? path : info->bus_info->name, info->name);
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200193 return NULL;
Gerd Hoffmann75570082009-08-31 14:23:58 +0200194 }
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200195
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200196 /* create device, set properties */
197 qdev = qdev_create(bus, driver);
198 id = qemu_opts_id(opts);
199 if (id) {
200 qdev->id = id;
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200201 }
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200202 if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
203 qdev_free(qdev);
204 return NULL;
205 }
Gerd Hoffmann81a322d2009-08-14 10:36:05 +0200206 if (qdev_init(qdev) != 0) {
207 qdev_free(qdev);
208 return NULL;
209 }
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200210 return qdev;
211}
212
Paul Brookaae94602009-05-14 22:35:06 +0100213/* Initialize a device. Device properties should be set before calling
214 this function. IRQs and MMIO regions should be connected/mapped after
215 calling this function. */
Gerd Hoffmann81a322d2009-08-14 10:36:05 +0200216int qdev_init(DeviceState *dev)
Paul Brookaae94602009-05-14 22:35:06 +0100217{
Gerd Hoffmann959f7332009-09-01 09:56:12 +0200218 int rc;
219
Gerd Hoffmann131ec1b2009-09-25 21:42:34 +0200220 assert(dev->state == DEV_STATE_CREATED);
Gerd Hoffmann959f7332009-09-01 09:56:12 +0200221 rc = dev->info->init(dev, dev->info);
222 if (rc < 0)
223 return rc;
224 if (dev->info->reset)
225 qemu_register_reset(dev->info->reset, dev);
Gerd Hoffmann391a0792009-09-01 09:56:14 +0200226 if (dev->info->vmsd)
227 vmstate_register(-1, dev->info->vmsd, dev);
Gerd Hoffmann131ec1b2009-09-25 21:42:34 +0200228 dev->state = DEV_STATE_INITIALIZED;
Gerd Hoffmann959f7332009-09-01 09:56:12 +0200229 return 0;
Paul Brook02e2da42009-05-23 00:05:19 +0100230}
231
232/* Unlink device from bus and free the structure. */
233void qdev_free(DeviceState *dev)
234{
Gerd Hoffmann131ec1b2009-09-25 21:42:34 +0200235 BusState *bus;
236
237 if (dev->state == DEV_STATE_INITIALIZED) {
238 while (dev->num_child_bus) {
239 bus = QLIST_FIRST(&dev->child_bus);
240 qbus_free(bus);
241 }
Gerd Hoffmann391a0792009-09-01 09:56:14 +0200242#if 0 /* FIXME: need sane vmstate_unregister function */
Gerd Hoffmann131ec1b2009-09-25 21:42:34 +0200243 if (dev->info->vmsd)
244 vmstate_unregister(dev->info->vmsd, dev);
Gerd Hoffmann391a0792009-09-01 09:56:14 +0200245#endif
Gerd Hoffmann131ec1b2009-09-25 21:42:34 +0200246 if (dev->info->reset)
247 qemu_unregister_reset(dev->info->reset, dev);
248 }
Blue Swirl72cf2d42009-09-12 07:36:22 +0000249 QLIST_REMOVE(dev, sibling);
Gerd Hoffmannccb63de2009-07-15 13:43:34 +0200250 qemu_free(dev);
Paul Brookaae94602009-05-14 22:35:06 +0100251}
252
Paul Brookaae94602009-05-14 22:35:06 +0100253/* Get a character (serial) device interface. */
254CharDriverState *qdev_init_chardev(DeviceState *dev)
255{
256 static int next_serial;
257 static int next_virtconsole;
258 /* FIXME: This is a nasty hack that needs to go away. */
Gerd Hoffmann042f84d2009-06-30 14:12:09 +0200259 if (strncmp(dev->info->name, "virtio", 6) == 0) {
Paul Brookaae94602009-05-14 22:35:06 +0100260 return virtcon_hds[next_virtconsole++];
261 } else {
262 return serial_hds[next_serial++];
263 }
264}
265
Paul Brook02e2da42009-05-23 00:05:19 +0100266BusState *qdev_get_parent_bus(DeviceState *dev)
Paul Brookaae94602009-05-14 22:35:06 +0100267{
Paul Brook02e2da42009-05-23 00:05:19 +0100268 return dev->parent_bus;
Paul Brookaae94602009-05-14 22:35:06 +0100269}
270
Paul Brookaae94602009-05-14 22:35:06 +0100271void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
272{
273 assert(dev->num_gpio_in == 0);
274 dev->num_gpio_in = n;
275 dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
276}
277
278void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
279{
280 assert(dev->num_gpio_out == 0);
281 dev->num_gpio_out = n;
282 dev->gpio_out = pins;
283}
284
285qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
286{
287 assert(n >= 0 && n < dev->num_gpio_in);
288 return dev->gpio_in[n];
289}
290
291void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
292{
293 assert(n >= 0 && n < dev->num_gpio_out);
294 dev->gpio_out[n] = pin;
295}
296
Paul Brook9d07d752009-05-14 22:35:07 +0100297VLANClientState *qdev_get_vlan_client(DeviceState *dev,
Mark McLoughlincda90462009-05-18 13:13:16 +0100298 NetCanReceive *can_receive,
299 NetReceive *receive,
300 NetReceiveIOV *receive_iov,
Paul Brook9d07d752009-05-14 22:35:07 +0100301 NetCleanup *cleanup,
302 void *opaque)
303{
304 NICInfo *nd = dev->nd;
305 assert(nd);
Mark McLoughlinae50b272009-07-01 16:46:38 +0100306 nd->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
307 receive, receive_iov, cleanup, opaque);
308 return nd->vc;
Paul Brook9d07d752009-05-14 22:35:07 +0100309}
310
311
312void qdev_get_macaddr(DeviceState *dev, uint8_t *macaddr)
313{
314 memcpy(macaddr, dev->nd->macaddr, 6);
315}
316
Paul Brookaae94602009-05-14 22:35:06 +0100317static int next_block_unit[IF_COUNT];
318
319/* Get a block device. This should only be used for single-drive devices
320 (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
321 appropriate bus. */
322BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type)
323{
324 int unit = next_block_unit[type]++;
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200325 DriveInfo *dinfo;
Paul Brookaae94602009-05-14 22:35:06 +0100326
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200327 dinfo = drive_get(type, 0, unit);
328 return dinfo ? dinfo->bdrv : NULL;
Paul Brookaae94602009-05-14 22:35:06 +0100329}
Paul Brook4d6ae672009-05-14 22:35:06 +0100330
Paul Brook02e2da42009-05-23 00:05:19 +0100331BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
Paul Brook4d6ae672009-05-14 22:35:06 +0100332{
Paul Brook02e2da42009-05-23 00:05:19 +0100333 BusState *bus;
Paul Brook4d6ae672009-05-14 22:35:06 +0100334
Blue Swirl72cf2d42009-09-12 07:36:22 +0000335 QLIST_FOREACH(bus, &dev->child_bus, sibling) {
Paul Brook4d6ae672009-05-14 22:35:06 +0100336 if (strcmp(name, bus->name) == 0) {
Paul Brook02e2da42009-05-23 00:05:19 +0100337 return bus;
Paul Brook4d6ae672009-05-14 22:35:06 +0100338 }
339 }
340 return NULL;
341}
342
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200343static BusState *qbus_find_recursive(BusState *bus, const char *name,
344 const BusInfo *info)
345{
346 DeviceState *dev;
347 BusState *child, *ret;
348 int match = 1;
349
350 if (name && (strcmp(bus->name, name) != 0)) {
351 match = 0;
352 }
353 if (info && (bus->info != info)) {
354 match = 0;
355 }
356 if (match) {
357 return bus;
358 }
359
Blue Swirl72cf2d42009-09-12 07:36:22 +0000360 QLIST_FOREACH(dev, &bus->children, sibling) {
361 QLIST_FOREACH(child, &dev->child_bus, sibling) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200362 ret = qbus_find_recursive(child, name, info);
363 if (ret) {
364 return ret;
365 }
366 }
367 }
368 return NULL;
369}
370
371static void qbus_list_bus(DeviceState *dev, char *dest, int len)
372{
373 BusState *child;
374 const char *sep = " ";
375 int pos = 0;
376
377 pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
378 dev->id ? dev->id : dev->info->name);
Blue Swirl72cf2d42009-09-12 07:36:22 +0000379 QLIST_FOREACH(child, &dev->child_bus, sibling) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200380 pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
381 sep = ", ";
382 }
383}
384
385static void qbus_list_dev(BusState *bus, char *dest, int len)
386{
387 DeviceState *dev;
388 const char *sep = " ";
389 int pos = 0;
390
391 pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
392 bus->name);
Blue Swirl72cf2d42009-09-12 07:36:22 +0000393 QLIST_FOREACH(dev, &bus->children, sibling) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200394 pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
395 sep, dev->info->name);
396 if (dev->id)
397 pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id);
398 sep = ", ";
399 }
400}
401
402static BusState *qbus_find_bus(DeviceState *dev, char *elem)
403{
404 BusState *child;
405
Blue Swirl72cf2d42009-09-12 07:36:22 +0000406 QLIST_FOREACH(child, &dev->child_bus, sibling) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200407 if (strcmp(child->name, elem) == 0) {
408 return child;
409 }
410 }
411 return NULL;
412}
413
414static DeviceState *qbus_find_dev(BusState *bus, char *elem)
415{
416 DeviceState *dev;
417
418 /*
419 * try to match in order:
420 * (1) instance id, if present
421 * (2) driver name
422 * (3) driver alias, if present
423 */
Blue Swirl72cf2d42009-09-12 07:36:22 +0000424 QLIST_FOREACH(dev, &bus->children, sibling) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200425 if (dev->id && strcmp(dev->id, elem) == 0) {
426 return dev;
427 }
428 }
Blue Swirl72cf2d42009-09-12 07:36:22 +0000429 QLIST_FOREACH(dev, &bus->children, sibling) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200430 if (strcmp(dev->info->name, elem) == 0) {
431 return dev;
432 }
433 }
Blue Swirl72cf2d42009-09-12 07:36:22 +0000434 QLIST_FOREACH(dev, &bus->children, sibling) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200435 if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
436 return dev;
437 }
438 }
439 return NULL;
440}
441
442static BusState *qbus_find(const char *path)
443{
444 DeviceState *dev;
445 BusState *bus;
446 char elem[128], msg[256];
447 int pos, len;
448
449 /* find start element */
450 if (path[0] == '/') {
451 bus = main_system_bus;
452 pos = 0;
453 } else {
454 if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200455 qemu_error("path parse error (\"%s\")\n", path);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200456 return NULL;
457 }
458 bus = qbus_find_recursive(main_system_bus, elem, NULL);
459 if (!bus) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200460 qemu_error("bus \"%s\" not found\n", elem);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200461 return NULL;
462 }
463 pos = len;
464 }
465
466 for (;;) {
467 if (path[pos] == '\0') {
468 /* we are done */
469 return bus;
470 }
471
472 /* find device */
473 if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200474 qemu_error("path parse error (\"%s\" pos %d)\n", path, pos);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200475 return NULL;
476 }
477 pos += len;
478 dev = qbus_find_dev(bus, elem);
479 if (!dev) {
480 qbus_list_dev(bus, msg, sizeof(msg));
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200481 qemu_error("device \"%s\" not found\n%s\n", elem, msg);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200482 return NULL;
483 }
484 if (path[pos] == '\0') {
485 /* last specified element is a device. If it has exactly
486 * one child bus accept it nevertheless */
487 switch (dev->num_child_bus) {
488 case 0:
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200489 qemu_error("device has no child bus (%s)\n", path);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200490 return NULL;
491 case 1:
Blue Swirl72cf2d42009-09-12 07:36:22 +0000492 return QLIST_FIRST(&dev->child_bus);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200493 default:
494 qbus_list_bus(dev, msg, sizeof(msg));
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200495 qemu_error("device has multiple child busses (%s)\n%s\n",
496 path, msg);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200497 return NULL;
498 }
499 }
500
501 /* find bus */
502 if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200503 qemu_error("path parse error (\"%s\" pos %d)\n", path, pos);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200504 return NULL;
505 }
506 pos += len;
507 bus = qbus_find_bus(dev, elem);
508 if (!bus) {
509 qbus_list_bus(dev, msg, sizeof(msg));
Gerd Hoffmann286c2322009-08-14 10:36:08 +0200510 qemu_error("child bus \"%s\" not found\n%s\n", elem, msg);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200511 return NULL;
512 }
513 }
514}
515
Gerd Hoffmanncd739fb2009-09-16 22:25:27 +0200516void qbus_create_inplace(BusState *bus, BusInfo *info,
517 DeviceState *parent, const char *name)
Paul Brook02e2da42009-05-23 00:05:19 +0100518{
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200519 char *buf;
520 int i,len;
Paul Brook02e2da42009-05-23 00:05:19 +0100521
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200522 bus->info = info;
Paul Brook02e2da42009-05-23 00:05:19 +0100523 bus->parent = parent;
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200524
525 if (name) {
526 /* use supplied name */
527 bus->name = qemu_strdup(name);
528 } else if (parent && parent->id) {
529 /* parent device has id -> use it for bus name */
530 len = strlen(parent->id) + 16;
531 buf = qemu_malloc(len);
532 snprintf(buf, len, "%s.%d", parent->id, parent->num_child_bus);
533 bus->name = buf;
534 } else {
535 /* no id -> use lowercase bus type for bus name */
536 len = strlen(info->name) + 16;
537 buf = qemu_malloc(len);
538 len = snprintf(buf, len, "%s.%d", info->name,
539 parent ? parent->num_child_bus : 0);
540 for (i = 0; i < len; i++)
Christoph Eggerbb87ece2009-07-30 15:28:45 +0200541 buf[i] = qemu_tolower(buf[i]);
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200542 bus->name = buf;
543 }
544
Blue Swirl72cf2d42009-09-12 07:36:22 +0000545 QLIST_INIT(&bus->children);
Paul Brook02e2da42009-05-23 00:05:19 +0100546 if (parent) {
Blue Swirl72cf2d42009-09-12 07:36:22 +0000547 QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200548 parent->num_child_bus++;
Paul Brook02e2da42009-05-23 00:05:19 +0100549 }
Gerd Hoffmanncd739fb2009-09-16 22:25:27 +0200550
551}
552
553BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
554{
555 BusState *bus;
556
557 bus = qemu_mallocz(info->size);
558 bus->qdev_allocated = 1;
559 qbus_create_inplace(bus, info, parent, name);
Paul Brook02e2da42009-05-23 00:05:19 +0100560 return bus;
561}
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100562
Gerd Hoffmann131ec1b2009-09-25 21:42:34 +0200563void qbus_free(BusState *bus)
564{
565 DeviceState *dev;
566
567 while ((dev = QLIST_FIRST(&bus->children)) != NULL) {
568 qdev_free(dev);
569 }
570 if (bus->parent) {
571 QLIST_REMOVE(bus, sibling);
572 bus->parent->num_child_bus--;
573 }
574 if (bus->qdev_allocated) {
575 qemu_free(bus);
576 }
577}
578
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100579#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
580static void qbus_print(Monitor *mon, BusState *bus, int indent);
581
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200582static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
583 const char *prefix, int indent)
584{
585 char buf[64];
586
587 if (!props)
588 return;
589 while (props->name) {
590 if (props->info->print) {
591 props->info->print(dev, props, buf, sizeof(buf));
592 qdev_printf("%s-prop: %s = %s\n", prefix, props->name, buf);
593 }
594 props++;
595 }
596}
597
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100598static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
599{
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100600 BusState *child;
Gerd Hoffmannccb63de2009-07-15 13:43:34 +0200601 qdev_printf("dev: %s, id \"%s\"\n", dev->info->name,
602 dev->id ? dev->id : "");
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100603 indent += 2;
604 if (dev->num_gpio_in) {
605 qdev_printf("gpio-in %d\n", dev->num_gpio_in);
606 }
607 if (dev->num_gpio_out) {
608 qdev_printf("gpio-out %d\n", dev->num_gpio_out);
609 }
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200610 qdev_print_props(mon, dev, dev->info->props, "dev", indent);
611 qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200612 if (dev->parent_bus->info->print_dev)
613 dev->parent_bus->info->print_dev(mon, dev, indent);
Blue Swirl72cf2d42009-09-12 07:36:22 +0000614 QLIST_FOREACH(child, &dev->child_bus, sibling) {
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100615 qbus_print(mon, child, indent);
616 }
617}
618
619static void qbus_print(Monitor *mon, BusState *bus, int indent)
620{
621 struct DeviceState *dev;
622
623 qdev_printf("bus: %s\n", bus->name);
624 indent += 2;
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200625 qdev_printf("type %s\n", bus->info->name);
Blue Swirl72cf2d42009-09-12 07:36:22 +0000626 QLIST_FOREACH(dev, &bus->children, sibling) {
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100627 qdev_print(mon, dev, indent);
628 }
629}
630#undef qdev_printf
631
632void do_info_qtree(Monitor *mon)
633{
634 if (main_system_bus)
635 qbus_print(mon, main_system_bus, 0);
636}
Gerd Hoffmann9316d302009-07-29 13:12:24 +0200637
Gerd Hoffmannf6c64e02009-08-03 15:03:09 +0200638void do_info_qdm(Monitor *mon)
Gerd Hoffmann9316d302009-07-29 13:12:24 +0200639{
640 DeviceInfo *info;
641 char msg[256];
642
643 for (info = device_info_list; info != NULL; info = info->next) {
644 qdev_print_devinfo(info, msg, sizeof(msg));
645 monitor_printf(mon, "%s\n", msg);
646 }
647}