blob: b048020455d8287f95fee4cba588fdc0d8efa39d [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 Hoffmann1b524b02009-07-29 13:12:23 +0200108static int qdev_print_devinfo(DeviceInfo *info, char *dest, int len)
109{
110 int pos = 0;
111
112 pos += snprintf(dest+pos, len-pos, "name \"%s\", bus %s",
113 info->name, info->bus_info->name);
114 if (info->alias)
115 pos += snprintf(dest+pos, len-pos, ", alias \"%s\"", info->alias);
116 if (info->desc)
117 pos += snprintf(dest+pos, len-pos, ", desc \"%s\"", info->desc);
118 if (info->no_user)
119 pos += snprintf(dest+pos, len-pos, ", no-user");
120 return pos;
121}
122
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200123static int set_property(const char *name, const char *value, void *opaque)
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200124{
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200125 DeviceState *dev = opaque;
126
127 if (strcmp(name, "driver") == 0)
128 return 0;
129 if (strcmp(name, "bus") == 0)
130 return 0;
131
132 if (-1 == qdev_prop_parse(dev, name, value)) {
133 fprintf(stderr, "can't set property \"%s\" to \"%s\" for \"%s\"\n",
134 name, value, dev->info->name);
135 return -1;
136 }
137 return 0;
138}
139
140DeviceState *qdev_device_add(QemuOpts *opts)
141{
142 const char *driver, *path, *id;
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200143 DeviceInfo *info;
144 DeviceState *qdev;
145 BusState *bus;
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200146
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200147 driver = qemu_opt_get(opts, "driver");
148 if (!driver) {
149 fprintf(stderr, "-device: no driver specified\n");
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200150 return NULL;
151 }
152 if (strcmp(driver, "?") == 0) {
Gerd Hoffmann1b524b02009-07-29 13:12:23 +0200153 char msg[256];
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200154 for (info = device_info_list; info != NULL; info = info->next) {
Gerd Hoffmann1b524b02009-07-29 13:12:23 +0200155 qdev_print_devinfo(info, msg, sizeof(msg));
156 fprintf(stderr, "%s\n", msg);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200157 }
158 return NULL;
159 }
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200160
161 /* find driver */
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200162 info = qdev_find_info(NULL, driver);
163 if (!info) {
164 fprintf(stderr, "Device \"%s\" not found. Try -device '?' for a list.\n",
165 driver);
166 return NULL;
167 }
168 if (info->no_user) {
169 fprintf(stderr, "device \"%s\" can't be added via command line\n",
170 info->name);
171 return NULL;
172 }
173
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200174 /* find bus */
175 path = qemu_opt_get(opts, "bus");
176 if (path != NULL) {
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200177 bus = qbus_find(path);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200178 } else {
179 bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info);
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200180 }
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200181 if (!bus)
182 return NULL;
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200183
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200184 /* create device, set properties */
185 qdev = qdev_create(bus, driver);
186 id = qemu_opts_id(opts);
187 if (id) {
188 qdev->id = id;
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200189 }
Gerd Hoffmannf31d07d2009-07-31 12:25:37 +0200190 if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) {
191 qdev_free(qdev);
192 return NULL;
193 }
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200194 qdev_init(qdev);
195 return qdev;
196}
197
Paul Brookaae94602009-05-14 22:35:06 +0100198/* Initialize a device. Device properties should be set before calling
199 this function. IRQs and MMIO regions should be connected/mapped after
200 calling this function. */
201void qdev_init(DeviceState *dev)
202{
Gerd Hoffmann042f84d2009-06-30 14:12:09 +0200203 dev->info->init(dev, dev->info);
Paul Brook02e2da42009-05-23 00:05:19 +0100204}
205
206/* Unlink device from bus and free the structure. */
207void qdev_free(DeviceState *dev)
208{
209 LIST_REMOVE(dev, sibling);
Gerd Hoffmannccb63de2009-07-15 13:43:34 +0200210 qemu_free(dev);
Paul Brookaae94602009-05-14 22:35:06 +0100211}
212
Paul Brookaae94602009-05-14 22:35:06 +0100213/* Get a character (serial) device interface. */
214CharDriverState *qdev_init_chardev(DeviceState *dev)
215{
216 static int next_serial;
217 static int next_virtconsole;
218 /* FIXME: This is a nasty hack that needs to go away. */
Gerd Hoffmann042f84d2009-06-30 14:12:09 +0200219 if (strncmp(dev->info->name, "virtio", 6) == 0) {
Paul Brookaae94602009-05-14 22:35:06 +0100220 return virtcon_hds[next_virtconsole++];
221 } else {
222 return serial_hds[next_serial++];
223 }
224}
225
Paul Brook02e2da42009-05-23 00:05:19 +0100226BusState *qdev_get_parent_bus(DeviceState *dev)
Paul Brookaae94602009-05-14 22:35:06 +0100227{
Paul Brook02e2da42009-05-23 00:05:19 +0100228 return dev->parent_bus;
Paul Brookaae94602009-05-14 22:35:06 +0100229}
230
Paul Brookaae94602009-05-14 22:35:06 +0100231void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
232{
233 assert(dev->num_gpio_in == 0);
234 dev->num_gpio_in = n;
235 dev->gpio_in = qemu_allocate_irqs(handler, dev, n);
236}
237
238void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
239{
240 assert(dev->num_gpio_out == 0);
241 dev->num_gpio_out = n;
242 dev->gpio_out = pins;
243}
244
245qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
246{
247 assert(n >= 0 && n < dev->num_gpio_in);
248 return dev->gpio_in[n];
249}
250
251void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
252{
253 assert(n >= 0 && n < dev->num_gpio_out);
254 dev->gpio_out[n] = pin;
255}
256
Paul Brook9d07d752009-05-14 22:35:07 +0100257VLANClientState *qdev_get_vlan_client(DeviceState *dev,
Mark McLoughlincda90462009-05-18 13:13:16 +0100258 NetCanReceive *can_receive,
259 NetReceive *receive,
260 NetReceiveIOV *receive_iov,
Paul Brook9d07d752009-05-14 22:35:07 +0100261 NetCleanup *cleanup,
262 void *opaque)
263{
264 NICInfo *nd = dev->nd;
265 assert(nd);
Mark McLoughlinae50b272009-07-01 16:46:38 +0100266 nd->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name, can_receive,
267 receive, receive_iov, cleanup, opaque);
268 return nd->vc;
Paul Brook9d07d752009-05-14 22:35:07 +0100269}
270
271
272void qdev_get_macaddr(DeviceState *dev, uint8_t *macaddr)
273{
274 memcpy(macaddr, dev->nd->macaddr, 6);
275}
276
Paul Brookaae94602009-05-14 22:35:06 +0100277static int next_block_unit[IF_COUNT];
278
279/* Get a block device. This should only be used for single-drive devices
280 (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
281 appropriate bus. */
282BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type)
283{
284 int unit = next_block_unit[type]++;
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200285 DriveInfo *dinfo;
Paul Brookaae94602009-05-14 22:35:06 +0100286
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200287 dinfo = drive_get(type, 0, unit);
288 return dinfo ? dinfo->bdrv : NULL;
Paul Brookaae94602009-05-14 22:35:06 +0100289}
Paul Brook4d6ae672009-05-14 22:35:06 +0100290
Paul Brook02e2da42009-05-23 00:05:19 +0100291BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
Paul Brook4d6ae672009-05-14 22:35:06 +0100292{
Paul Brook02e2da42009-05-23 00:05:19 +0100293 BusState *bus;
Paul Brook4d6ae672009-05-14 22:35:06 +0100294
Paul Brook02e2da42009-05-23 00:05:19 +0100295 LIST_FOREACH(bus, &dev->child_bus, sibling) {
Paul Brook4d6ae672009-05-14 22:35:06 +0100296 if (strcmp(name, bus->name) == 0) {
Paul Brook02e2da42009-05-23 00:05:19 +0100297 return bus;
Paul Brook4d6ae672009-05-14 22:35:06 +0100298 }
299 }
300 return NULL;
301}
302
Paul Brook6f68ecb2009-05-14 22:35:07 +0100303static int next_scsi_bus;
304
305/* Create a scsi bus, and attach devices to it. */
306/* TODO: Actually create a scsi bus for hotplug to use. */
307void scsi_bus_new(DeviceState *host, SCSIAttachFn attach)
308{
309 int bus = next_scsi_bus++;
310 int unit;
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200311 DriveInfo *dinfo;
Paul Brook6f68ecb2009-05-14 22:35:07 +0100312
313 for (unit = 0; unit < MAX_SCSI_DEVS; unit++) {
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200314 dinfo = drive_get(IF_SCSI, bus, unit);
315 if (!dinfo) {
Paul Brook6f68ecb2009-05-14 22:35:07 +0100316 continue;
317 }
Gerd Hoffmann751c6a12009-07-22 16:42:57 +0200318 attach(host, dinfo->bdrv, unit);
Paul Brook6f68ecb2009-05-14 22:35:07 +0100319 }
320}
Paul Brook02e2da42009-05-23 00:05:19 +0100321
Gerd Hoffmann8ffb1bc2009-07-15 13:59:25 +0200322static BusState *qbus_find_recursive(BusState *bus, const char *name,
323 const BusInfo *info)
324{
325 DeviceState *dev;
326 BusState *child, *ret;
327 int match = 1;
328
329 if (name && (strcmp(bus->name, name) != 0)) {
330 match = 0;
331 }
332 if (info && (bus->info != info)) {
333 match = 0;
334 }
335 if (match) {
336 return bus;
337 }
338
339 LIST_FOREACH(dev, &bus->children, sibling) {
340 LIST_FOREACH(child, &dev->child_bus, sibling) {
341 ret = qbus_find_recursive(child, name, info);
342 if (ret) {
343 return ret;
344 }
345 }
346 }
347 return NULL;
348}
349
350static void qbus_list_bus(DeviceState *dev, char *dest, int len)
351{
352 BusState *child;
353 const char *sep = " ";
354 int pos = 0;
355
356 pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":",
357 dev->id ? dev->id : dev->info->name);
358 LIST_FOREACH(child, &dev->child_bus, sibling) {
359 pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name);
360 sep = ", ";
361 }
362}
363
364static void qbus_list_dev(BusState *bus, char *dest, int len)
365{
366 DeviceState *dev;
367 const char *sep = " ";
368 int pos = 0;
369
370 pos += snprintf(dest+pos, len-pos, "devices at \"%s\":",
371 bus->name);
372 LIST_FOREACH(dev, &bus->children, sibling) {
373 pos += snprintf(dest+pos, len-pos, "%s\"%s\"",
374 sep, dev->info->name);
375 if (dev->id)
376 pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id);
377 sep = ", ";
378 }
379}
380
381static BusState *qbus_find_bus(DeviceState *dev, char *elem)
382{
383 BusState *child;
384
385 LIST_FOREACH(child, &dev->child_bus, sibling) {
386 if (strcmp(child->name, elem) == 0) {
387 return child;
388 }
389 }
390 return NULL;
391}
392
393static DeviceState *qbus_find_dev(BusState *bus, char *elem)
394{
395 DeviceState *dev;
396
397 /*
398 * try to match in order:
399 * (1) instance id, if present
400 * (2) driver name
401 * (3) driver alias, if present
402 */
403 LIST_FOREACH(dev, &bus->children, sibling) {
404 if (dev->id && strcmp(dev->id, elem) == 0) {
405 return dev;
406 }
407 }
408 LIST_FOREACH(dev, &bus->children, sibling) {
409 if (strcmp(dev->info->name, elem) == 0) {
410 return dev;
411 }
412 }
413 LIST_FOREACH(dev, &bus->children, sibling) {
414 if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
415 return dev;
416 }
417 }
418 return NULL;
419}
420
421static BusState *qbus_find(const char *path)
422{
423 DeviceState *dev;
424 BusState *bus;
425 char elem[128], msg[256];
426 int pos, len;
427
428 /* find start element */
429 if (path[0] == '/') {
430 bus = main_system_bus;
431 pos = 0;
432 } else {
433 if (sscanf(path, "%127[^/]%n", elem, &len) != 1) {
434 fprintf(stderr, "path parse error (\"%s\")\n", path);
435 return NULL;
436 }
437 bus = qbus_find_recursive(main_system_bus, elem, NULL);
438 if (!bus) {
439 fprintf(stderr, "bus \"%s\" not found\n", elem);
440 return NULL;
441 }
442 pos = len;
443 }
444
445 for (;;) {
446 if (path[pos] == '\0') {
447 /* we are done */
448 return bus;
449 }
450
451 /* find device */
452 if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
453 fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
454 return NULL;
455 }
456 pos += len;
457 dev = qbus_find_dev(bus, elem);
458 if (!dev) {
459 qbus_list_dev(bus, msg, sizeof(msg));
460 fprintf(stderr, "device \"%s\" not found\n%s\n", elem, msg);
461 return NULL;
462 }
463 if (path[pos] == '\0') {
464 /* last specified element is a device. If it has exactly
465 * one child bus accept it nevertheless */
466 switch (dev->num_child_bus) {
467 case 0:
468 fprintf(stderr, "device has no child bus (%s)\n", path);
469 return NULL;
470 case 1:
471 return LIST_FIRST(&dev->child_bus);
472 default:
473 qbus_list_bus(dev, msg, sizeof(msg));
474 fprintf(stderr, "device has multiple child busses (%s)\n%s\n",
475 path, msg);
476 return NULL;
477 }
478 }
479
480 /* find bus */
481 if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) {
482 fprintf(stderr, "path parse error (\"%s\" pos %d)\n", path, pos);
483 return NULL;
484 }
485 pos += len;
486 bus = qbus_find_bus(dev, elem);
487 if (!bus) {
488 qbus_list_bus(dev, msg, sizeof(msg));
489 fprintf(stderr, "child bus \"%s\" not found\n%s\n", elem, msg);
490 return NULL;
491 }
492 }
493}
494
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200495BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name)
Paul Brook02e2da42009-05-23 00:05:19 +0100496{
497 BusState *bus;
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200498 char *buf;
499 int i,len;
Paul Brook02e2da42009-05-23 00:05:19 +0100500
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200501 bus = qemu_mallocz(info->size);
502 bus->info = info;
Paul Brook02e2da42009-05-23 00:05:19 +0100503 bus->parent = parent;
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200504
505 if (name) {
506 /* use supplied name */
507 bus->name = qemu_strdup(name);
508 } else if (parent && parent->id) {
509 /* parent device has id -> use it for bus name */
510 len = strlen(parent->id) + 16;
511 buf = qemu_malloc(len);
512 snprintf(buf, len, "%s.%d", parent->id, parent->num_child_bus);
513 bus->name = buf;
514 } else {
515 /* no id -> use lowercase bus type for bus name */
516 len = strlen(info->name) + 16;
517 buf = qemu_malloc(len);
518 len = snprintf(buf, len, "%s.%d", info->name,
519 parent ? parent->num_child_bus : 0);
520 for (i = 0; i < len; i++)
Christoph Eggerbb87ece2009-07-30 15:28:45 +0200521 buf[i] = qemu_tolower(buf[i]);
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200522 bus->name = buf;
523 }
524
Paul Brook02e2da42009-05-23 00:05:19 +0100525 LIST_INIT(&bus->children);
526 if (parent) {
527 LIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
Gerd Hoffmannd271de92009-07-15 13:59:24 +0200528 parent->num_child_bus++;
Paul Brook02e2da42009-05-23 00:05:19 +0100529 }
530 return bus;
531}
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100532
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100533#define qdev_printf(fmt, ...) monitor_printf(mon, "%*s" fmt, indent, "", ## __VA_ARGS__)
534static void qbus_print(Monitor *mon, BusState *bus, int indent);
535
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200536static void qdev_print_props(Monitor *mon, DeviceState *dev, Property *props,
537 const char *prefix, int indent)
538{
539 char buf[64];
540
541 if (!props)
542 return;
543 while (props->name) {
544 if (props->info->print) {
545 props->info->print(dev, props, buf, sizeof(buf));
546 qdev_printf("%s-prop: %s = %s\n", prefix, props->name, buf);
547 }
548 props++;
549 }
550}
551
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100552static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
553{
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100554 BusState *child;
Gerd Hoffmannccb63de2009-07-15 13:43:34 +0200555 qdev_printf("dev: %s, id \"%s\"\n", dev->info->name,
556 dev->id ? dev->id : "");
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100557 indent += 2;
558 if (dev->num_gpio_in) {
559 qdev_printf("gpio-in %d\n", dev->num_gpio_in);
560 }
561 if (dev->num_gpio_out) {
562 qdev_printf("gpio-out %d\n", dev->num_gpio_out);
563 }
Gerd Hoffmannee6847d2009-07-15 13:43:31 +0200564 qdev_print_props(mon, dev, dev->info->props, "dev", indent);
565 qdev_print_props(mon, dev, dev->parent_bus->info->props, "bus", indent);
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200566 if (dev->parent_bus->info->print_dev)
567 dev->parent_bus->info->print_dev(mon, dev, indent);
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100568 LIST_FOREACH(child, &dev->child_bus, sibling) {
569 qbus_print(mon, child, indent);
570 }
571}
572
573static void qbus_print(Monitor *mon, BusState *bus, int indent)
574{
575 struct DeviceState *dev;
576
577 qdev_printf("bus: %s\n", bus->name);
578 indent += 2;
Gerd Hoffmann10c4c982009-06-30 14:12:08 +0200579 qdev_printf("type %s\n", bus->info->name);
Gerd Hoffmanncae49562009-06-05 15:53:17 +0100580 LIST_FOREACH(dev, &bus->children, sibling) {
581 qdev_print(mon, dev, indent);
582 }
583}
584#undef qdev_printf
585
586void do_info_qtree(Monitor *mon)
587{
588 if (main_system_bus)
589 qbus_print(mon, main_system_bus, 0);
590}
Gerd Hoffmann9316d302009-07-29 13:12:24 +0200591
592void do_info_qdrv(Monitor *mon)
593{
594 DeviceInfo *info;
595 char msg[256];
596
597 for (info = device_info_list; info != NULL; info = info->next) {
598 qdev_print_devinfo(info, msg, sizeof(msg));
599 monitor_printf(mon, "%s\n", msg);
600 }
601}