diff options
author | Paul Brook <paul@codesourcery.com> | 2009-05-14 22:35:06 +0100 |
---|---|---|
committer | Paul Brook <paul@codesourcery.com> | 2009-05-14 22:35:06 +0100 |
commit | aae9460e244c7abe70b72ff374b3aa102bb09691 (patch) | |
tree | aeed206df23f689a13c332ac9831e382a684dd57 /hw/qdev.c | |
parent | 019d6b8ff0d495ded6977f24a4e8fd1c7fec09e0 (diff) |
Basic qdev infrastructure.
Signed-off-by: Paul Brook <paul@codesourcery.com>
Diffstat (limited to 'hw/qdev.c')
-rw-r--r-- | hw/qdev.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/hw/qdev.c b/hw/qdev.c new file mode 100644 index 0000000000..eaa30f430b --- /dev/null +++ b/hw/qdev.c @@ -0,0 +1,237 @@ +/* + * Dynamic device configuration and creation. + * + * Copyright (c) 2009 CodeSourcery + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + */ + +/* The theory here is that it should be possible to create a machine without + knowledge of specific devices. Historically board init routines have + passed a bunch of arguments to each device, requiring the board know + exactly which device it is dealing with. This file provides an abstract + API for device configuration and initialization. Devices will generally + inherit from a particular bus (e.g. PCI or I2C) rather than + this API directly. */ + +#include "qdev.h" +#include "sysemu.h" + +struct DeviceProperty { + const char *name; + union { + int i; + void *ptr; + } value; + DeviceProperty *next; +}; + +struct DeviceType { + const char *name; + qdev_initfn init; + void *opaque; + int size; + DeviceType *next; +}; + +static DeviceType *device_type_list; + +/* Register a new device type. */ +DeviceType *qdev_register(const char *name, int size, qdev_initfn init, + void *opaque) +{ + DeviceType *t; + + assert(size >= sizeof(DeviceState)); + + t = qemu_mallocz(sizeof(DeviceType)); + t->next = device_type_list; + device_type_list = t; + t->name = qemu_strdup(name); + t->size = size; + t->init = init; + t->opaque = opaque; + + return t; +} + +/* Create a new device. This only initializes the device state structure + and allows properties to be set. qdev_init should be called to + initialize the actual device emulation. */ +DeviceState *qdev_create(void *bus, const char *name) +{ + DeviceType *t; + DeviceState *dev; + + for (t = device_type_list; t; t = t->next) { + if (strcmp(t->name, name) == 0) { + break; + } + } + if (!t) { + fprintf(stderr, "Unknown device '%s'\n", name); + exit(1); + } + + dev = qemu_mallocz(t->size); + dev->name = name; + dev->type = t; + dev->bus = bus; + return dev; +} + +/* Initialize a device. Device properties should be set before calling + this function. IRQs and MMIO regions should be connected/mapped after + calling this function. */ +void qdev_init(DeviceState *dev) +{ + dev->type->init(dev, dev->type->opaque); +} + +static DeviceProperty *create_prop(DeviceState *dev, const char *name) +{ + DeviceProperty *prop; + + /* TODO: Check for duplicate properties. */ + prop = qemu_mallocz(sizeof(*prop)); + prop->name = qemu_strdup(name); + prop->next = dev->props; + dev->props = prop; + + return prop; +} + +void qdev_set_prop_int(DeviceState *dev, const char *name, int value) +{ + DeviceProperty *prop; + + prop = create_prop(dev, name); + prop->value.i = value; +} + +void qdev_set_prop_ptr(DeviceState *dev, const char *name, void *value) +{ + DeviceProperty *prop; + + prop = create_prop(dev, name); + prop->value.ptr = value; +} + + +qemu_irq qdev_get_irq_sink(DeviceState *dev, int n) +{ + assert(n >= 0 && n < dev->num_irq_sink); + return dev->irq_sink[n]; +} + +/* Register device IRQ sinks. */ +void qdev_init_irq_sink(DeviceState *dev, qemu_irq_handler handler, int nirq) +{ + dev->num_irq_sink = nirq; + dev->irq_sink = qemu_allocate_irqs(handler, dev, nirq); +} + +/* Get a character (serial) device interface. */ +CharDriverState *qdev_init_chardev(DeviceState *dev) +{ + static int next_serial; + static int next_virtconsole; + /* FIXME: This is a nasty hack that needs to go away. */ + if (strncmp(dev->name, "virtio", 6) == 0) { + return virtcon_hds[next_virtconsole++]; + } else { + return serial_hds[next_serial++]; + } +} + +void *qdev_get_bus(DeviceState *dev) +{ + return dev->bus; +} + +static DeviceProperty *find_prop(DeviceState *dev, const char *name) +{ + DeviceProperty *prop; + + for (prop = dev->props; prop; prop = prop->next) { + if (strcmp(prop->name, name) == 0) { + return prop; + } + } + return NULL; +} + +uint64_t qdev_get_prop_int(DeviceState *dev, const char *name, uint64_t def) +{ + DeviceProperty *prop; + + prop = find_prop(dev, name); + if (!prop) + return def; + + return prop->value.i; +} + +void *qdev_get_prop_ptr(DeviceState *dev, const char *name) +{ + DeviceProperty *prop; + + prop = find_prop(dev, name); + assert(prop); + return prop->value.ptr; +} + +void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n) +{ + assert(dev->num_gpio_in == 0); + dev->num_gpio_in = n; + dev->gpio_in = qemu_allocate_irqs(handler, dev, n); +} + +void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n) +{ + assert(dev->num_gpio_out == 0); + dev->num_gpio_out = n; + dev->gpio_out = pins; +} + +qemu_irq qdev_get_gpio_in(DeviceState *dev, int n) +{ + assert(n >= 0 && n < dev->num_gpio_in); + return dev->gpio_in[n]; +} + +void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin) +{ + assert(n >= 0 && n < dev->num_gpio_out); + dev->gpio_out[n] = pin; +} + +static int next_block_unit[IF_COUNT]; + +/* Get a block device. This should only be used for single-drive devices + (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the + appropriate bus. */ +BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type) +{ + int unit = next_block_unit[type]++; + int index; + + index = drive_get_index(type, 0, unit); + if (index == -1) { + return NULL; + } + return drives_table[index].bdrv; +} |