blob: 479eb72b6740a6c2df91973eea180de7aaef634c [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);
Paul Brook02e2da42009-05-23 00:05:19 +0100104 LIST_INSERT_HEAD(&bus->children, dev, sibling);
Paul Brookaae94602009-05-14 22:35:06 +0100105 return dev;
106}
107
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200108DeviceState *qdev_device_add(const char *cmdline)
109{
110 DeviceInfo *info;
111 DeviceState *qdev;
112 BusState *bus;
113 char driver[32], path[128] = "";
114 char tag[32], value[256];
115 const char *params = NULL;
116 int n = 0;
117
118 if (1 != sscanf(cmdline, "%32[^,],%n", driver, &n)) {
119 fprintf(stderr, "device parse error: \"%s\"\n", cmdline);
120 return NULL;
121 }
122 if (strcmp(driver, "?") == 0) {
123 for (info = device_info_list; info != NULL; info = info->next) {
124 fprintf(stderr, "name \"%s\", bus %s\n", info->name, info->bus_info->name);
125 }
126 return NULL;
127 }
128 if (n) {
129 params = cmdline + n;
130 get_param_value(path, sizeof(path), "bus", params);
131 }
132 info = qdev_find_info(NULL, driver);
133 if (!info) {
134 fprintf(stderr, "Device \"%s\" not found. Try -device '?' for a list.\n",
135 driver);
136 return NULL;
137 }
138 if (info->no_user) {
139 fprintf(stderr, "device \"%s\" can't be added via command line\n",
140 info->name);
141 return NULL;
142 }
143
144 if (strlen(path)) {
145 bus = qbus_find(path);
146 if (!bus)
147 return NULL;
148 qdev = qdev_create(bus, driver);
149 } else {
150 bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
151 if (!bus)
152 return NULL;
153 qdev = qdev_create(bus, driver);
154 }
155
156 if (params) {
157 while (params[0]) {
158 if (2 != sscanf(params, "%31[^=]=%255[^,]%n", tag, value, &n)) {
159 fprintf(stderr, "parse error at \"%s\"\n", params);
160 break;
161 }
162 params += n;
163 if (params[0] == ',')
164 params++;
165 if (strcmp(tag, "bus") == 0)
166 continue;
167 if (strcmp(tag, "id") == 0) {
168 qdev->id = qemu_strdup(value);
169 continue;
170 }
171 if (-1 == qdev_prop_parse(qdev, tag, value)) {
172 fprintf(stderr, "can't set property \"%s\" to \"%s\" for \"%s\"\n",
173 tag, value, driver);
174 }
175 }
176 }
177
178 qdev_init(qdev);
179 return qdev;
180}
181
Paul Brookaae94602009-05-14 22:35:06 +0100182/* Initialize a device. Device properties should be set before calling
183 this function. IRQs and MMIO regions should be connected/mapped after
184 calling this function. */
185void qdev_init(DeviceState *dev)
186{
Gerd Hoffmann042f84d2009-06-30 14:12:09 +0200187 dev->info->init(dev, dev->info);
Paul Brook02e2da42009-05-23 00:05:19 +0100188}
189
190/* Unlink device from bus and free the structure. */
191void qdev_free(DeviceState *dev)
192{
193 LIST_REMOVE(dev, sibling);
Gerd Hoffmannccb63de2009-07-15 13:43:34 +0200194 qemu_free(dev->id);
195 qemu_free(dev);
Paul Brookaae94602009-05-14 22:35:06 +0100196}
197
Paul Brookaae94602009-05-14 22:35:06 +0100198/* Get a character (serial) device interface. */
199CharDriverState *qdev_init_chardev(DeviceState *dev)
200{
201 static int next_serial;
202 static int next_virtconsole;
203 /* FIXME: This is a nasty hack that needs to go away. */
Gerd Hoffmann042f84d2009-06-30 14:12:09 +0200204 if (strncmp(dev->info->name, "virtio", 6) == 0) {
Paul Brookaae94602009-05-14 22:35:06 +0100205 return virtcon_hds[next_virtconsole++];
206 } else {
207 return serial_hds[next_serial++];
208 }
209}
210
Paul Brook02e2da42009-05-23 00:05:19 +0100211BusState *qdev_get_parent_bus(DeviceState *dev)
Paul Brookaae94602009-05-14 22:35:06 +0100212{
Paul Brook02e2da42009-05-23 00:05:19 +0100213 return dev->parent_bus;
Paul Brookaae94602009-05-14 22:35:06 +0100214}
215
Paul Brookaae94602009-05-14 22:35:06 +0100216void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
217{
218 assert(dev->num_gpio_in == 0);
219 dev->num_gpio_in = n;
220 dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
221}
222
223void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
224{
225 assert(dev->num_gpio_out == 0);
226 dev->num_gpio_out = n;
227 dev->gpio_out = pins;
228}
229
230qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
231{
232 assert(n >= 0 && n < dev->num_gpio_in);
233 return dev->gpio_in[n];
234}
235
236void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
237{
238 assert(n >= 0 && n < dev->num_gpio_out);
239 dev->gpio_out[n] = pin;
240}
241
Paul Brook9d07d752009-05-14 22:35:07 +0100242VLANClientState *qdev_get_vlan_client(DeviceState *dev,
Mark McLoughlincda90462009-05-18 13:13:16 +0100243 NetCanReceive *can_receive,
244 NetReceive *receive,
245 NetReceiveIOV *receive_iov,
Paul Brook9d07d752009-05-14 22:35:07 +0100246 NetCleanup *cleanup,
247 void *opaque)
248{
249 NICInfo *nd = dev->nd;
250 assert(nd);
Mark McLoughlinae50b272009-07-01 16:46:38 +0100251 nd->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
252 receive, receive_iov, cleanup, opaque);
253 return nd->vc;
Paul Brook9d07d752009-05-14 22:35:07 +0100254}
255
256
257void qdev_get_macaddr(DeviceState *dev, uint8_t *macaddr)
258{
259 memcpy(macaddr, dev->nd->macaddr, 6);
260}
261
Paul Brookaae94602009-05-14 22:35:06 +0100262static int next_block_unit[IF_COUNT];
263
264/* Get a block device. This should only be used for single-drive devices
265 (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
266 appropriate bus. */
267BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type)
268{
269 int unit = next_block_unit[type]++;
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200270 DriveInfo *dinfo;
Paul Brookaae94602009-05-14 22:35:06 +0100271
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200272 dinfo = drive_get(type, 0, unit);
273 return dinfo ? dinfo->bdrv : NULL;
Paul Brookaae94602009-05-14 22:35:06 +0100274}
Paul Brook4d6ae672009-05-14 22:35:06 +0100275
Paul Brook02e2da42009-05-23 00:05:19 +0100276BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
Paul Brook4d6ae672009-05-14 22:35:06 +0100277{
Paul Brook02e2da42009-05-23 00:05:19 +0100278 BusState *bus;
Paul Brook4d6ae672009-05-14 22:35:06 +0100279
Paul Brook02e2da42009-05-23 00:05:19 +0100280 LIST_FOREACH(bus, &dev->child_bus, sibling) {
Paul Brook4d6ae672009-05-14 22:35:06 +0100281 if (strcmp(name, bus->name) == 0) {
Paul Brook02e2da42009-05-23 00:05:19 +0100282 return bus;
Paul Brook4d6ae672009-05-14 22:35:06 +0100283 }
284 }
285 return NULL;
286}
287
Paul Brook6f68ecb2009-05-14 22:35:07 +0100288static int next_scsi_bus;
289
290/* Create a scsi bus, and attach devices to it. */
291/* TODO: Actually create a scsi bus for hotplug to use. */
292void scsi_bus_new(DeviceState *host, SCSIAttachFn attach)
293{
294 int bus = next_scsi_bus++;
295 int unit;
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200296 DriveInfo *dinfo;
Paul Brook6f68ecb2009-05-14 22:35:07 +0100297
298 for (unit = 0; unit < MAX_SCSI_DEVS; unit++) {
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200299 dinfo = drive_get(IF_SCSI, bus, unit);
300 if (!dinfo) {
Paul Brook6f68ecb2009-05-14 22:35:07 +0100301 continue;
302 }
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200303 attach(host, dinfo->bdrv, unit);
Paul Brook6f68ecb2009-05-14 22:35:07 +0100304 }
305}
Paul Brook02e2da42009-05-23 00:05:19 +0100306
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200307static BusState *qbus_find_recursive(BusState *bus, const char *name,
308 const BusInfo *info)
309{
310 DeviceState *dev;
311 BusState *child, *ret;
312 int match = 1;
313
314 if (name && (strcmp(bus->name, name) != 0)) {
315 match = 0;
316 }
317 if (info && (bus->info != info)) {
318 match = 0;
319 }
320 if (match) {
321 return bus;
322 }
323
324 LIST_FOREACH(dev, &bus->children, sibling) {
325 LIST_FOREACH(child, &dev->child_bus, sibling) {
326 ret = qbus_find_recursive(child, name, info);
327 if (ret) {
328 return ret;
329 }
330 }
331 }
332 return NULL;
333}
334
335static void qbus_list_bus(DeviceState *dev, char *dest, int len)
336{
337 BusState *child;
338 const char *sep = " ";
339 int pos = 0;
340
341 pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
342 dev->id ? dev->id : dev->info->name);
343 LIST_FOREACH(child, &dev->child_bus, sibling) {
344 pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
345 sep = ", ";
346 }
347}
348
349static void qbus_list_dev(BusState *bus, char *dest, int len)
350{
351 DeviceState *dev;
352 const char *sep = " ";
353 int pos = 0;
354
355 pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
356 bus->name);
357 LIST_FOREACH(dev, &bus->children, sibling) {
358 pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
359 sep, dev->info->name);
360 if (dev->id)
361 pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id);
362 sep = ", ";
363 }
364}
365
366static BusState *qbus_find_bus(DeviceState *dev, char *elem)
367{
368 BusState *child;
369
370 LIST_FOREACH(child, &dev->child_bus, sibling) {
371 if (strcmp(child->name, elem) == 0) {
372 return child;
373 }
374 }
375 return NULL;
376}
377
378static DeviceState *qbus_find_dev(BusState *bus, char *elem)
379{
380 DeviceState *dev;
381
382 /*
383 * try to match in order:
384 * (1) instance id, if present
385 * (2) driver name
386 * (3) driver alias, if present
387 */
388 LIST_FOREACH(dev, &bus->children, sibling) {
389 if (dev->id && strcmp(dev->id, elem) == 0) {
390 return dev;
391 }
392 }
393 LIST_FOREACH(dev, &bus->children, sibling) {
394 if (strcmp(dev->info->name, elem) == 0) {
395 return dev;
396 }
397 }
398 LIST_FOREACH(dev, &bus->children, sibling) {
399 if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
400 return dev;
401 }
402 }
403 return NULL;
404}
405
406static BusState *qbus_find(const char *path)
407{
408 DeviceState *dev;
409 BusState *bus;
410 char elem[128], msg[256];
411 int pos, len;
412
413 /* find start element */
414 if (path[0] == '/') {
415 bus = main_system_bus;
416 pos = 0;
417 } else {
418 if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
419 fprintf(stderr, "path parse error (\"%s\")\n", path);
420 return NULL;
421 }
422 bus = qbus_find_recursive(main_system_bus, elem, NULL);
423 if (!bus) {
424 fprintf(stderr, "bus \"%s\" not found\n", elem);
425 return NULL;
426 }
427 pos = len;
428 }
429
430 for (;;) {
431 if (path[pos] == '\0') {
432 /* we are done */
433 return bus;
434 }
435
436 /* find device */
437 if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
438 fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
439 return NULL;
440 }
441 pos += len;
442 dev = qbus_find_dev(bus, elem);
443 if (!dev) {
444 qbus_list_dev(bus, msg, sizeof(msg));
445 fprintf(stderr, "device \"%s\" not found\n%s\n", elem, msg);
446 return NULL;
447 }
448 if (path[pos] == '\0') {
449 /* last specified element is a device. If it has exactly
450 * one child bus accept it nevertheless */
451 switch (dev->num_child_bus) {
452 case 0:
453 fprintf(stderr, "device has no child bus (%s)\n", path);
454 return NULL;
455 case 1:
456 return LIST_FIRST(&dev->child_bus);
457 default:
458 qbus_list_bus(dev, msg, sizeof(msg));
459 fprintf(stderr, "device has multiple child busses (%s)\n%s\n",
460 path, msg);
461 return NULL;
462 }
463 }
464
465 /* find bus */
466 if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
467 fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
468 return NULL;
469 }
470 pos += len;
471 bus = qbus_find_bus(dev, elem);
472 if (!bus) {
473 qbus_list_bus(dev, msg, sizeof(msg));
474 fprintf(stderr, "child bus \"%s\" not found\n%s\n", elem, msg);
475 return NULL;
476 }
477 }
478}
479
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200480BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
Paul Brook02e2da42009-05-23 00:05:19 +0100481{
482 BusState *bus;
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200483 char *buf;
484 int i,len;
Paul Brook02e2da42009-05-23 00:05:19 +0100485
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200486 bus = qemu_mallocz(info->size);
487 bus->info = info;
Paul Brook02e2da42009-05-23 00:05:19 +0100488 bus->parent = parent;
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200489
490 if (name) {
491 /* use supplied name */
492 bus->name = qemu_strdup(name);
493 } else if (parent && parent->id) {
494 /* parent device has id -> use it for bus name */
495 len = strlen(parent->id) + 16;
496 buf = qemu_malloc(len);
497 snprintf(buf, len, "%s.%d", parent->id, parent->num_child_bus);
498 bus->name = buf;
499 } else {
500 /* no id -> use lowercase bus type for bus name */
501 len = strlen(info->name) + 16;
502 buf = qemu_malloc(len);
503 len = snprintf(buf, len, "%s.%d", info->name,
504 parent ? parent->num_child_bus : 0);
505 for (i = 0; i < len; i++)
506 buf[i] = tolower(buf[i]);
507 bus->name = buf;
508 }
509
Paul Brook02e2da42009-05-23 00:05:19 +0100510 LIST_INIT(&bus->children);
511 if (parent) {
512 LIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200513 parent->num_child_bus++;
Paul Brook02e2da42009-05-23 00:05:19 +0100514 }
515 return bus;
516}
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100517
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100518#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
519static void qbus_print(Monitor *mon, BusState *bus, int indent);
520
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200521static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
522 const char *prefix, int indent)
523{
524 char buf[64];
525
526 if (!props)
527 return;
528 while (props->name) {
529 if (props->info->print) {
530 props->info->print(dev, props, buf, sizeof(buf));
531 qdev_printf("%s-prop: %s = %s\n", prefix, props->name, buf);
532 }
533 props++;
534 }
535}
536
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100537static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
538{
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100539 BusState *child;
Gerd Hoffmannccb63de2009-07-15 13:43:34 +0200540 qdev_printf("dev: %s, id \"%s\"\n", dev->info->name,
541 dev->id ? dev->id : "");
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100542 indent += 2;
543 if (dev->num_gpio_in) {
544 qdev_printf("gpio-in %d\n", dev->num_gpio_in);
545 }
546 if (dev->num_gpio_out) {
547 qdev_printf("gpio-out %d\n", dev->num_gpio_out);
548 }
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200549 qdev_print_props(mon, dev, dev->info->props, "dev", indent);
550 qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200551 if (dev->parent_bus->info->print_dev)
552 dev->parent_bus->info->print_dev(mon, dev, indent);
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100553 LIST_FOREACH(child, &dev->child_bus, sibling) {
554 qbus_print(mon, child, indent);
555 }
556}
557
558static void qbus_print(Monitor *mon, BusState *bus, int indent)
559{
560 struct DeviceState *dev;
561
562 qdev_printf("bus: %s\n", bus->name);
563 indent += 2;
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200564 qdev_printf("type %s\n", bus->info->name);
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100565 LIST_FOREACH(dev, &bus->children, sibling) {
566 qdev_print(mon, dev, indent);
567 }
568}
569#undef qdev_printf
570
571void do_info_qtree(Monitor *mon)
572{
573 if (main_system_bus)
574 qbus_print(mon, main_system_bus, 0);
575}