diff options
Diffstat (limited to 'hw')
| -rw-r--r-- | hw/cirrus_vga.c | 8 | ||||
| -rw-r--r-- | hw/container.c | 20 | ||||
| -rw-r--r-- | hw/mc146818rtc.c | 27 | ||||
| -rw-r--r-- | hw/pc.c | 26 | ||||
| -rw-r--r-- | hw/pc.h | 14 | ||||
| -rw-r--r-- | hw/pc_piix.c | 58 | ||||
| -rw-r--r-- | hw/piix_pci.c | 3 | ||||
| -rw-r--r-- | hw/qdev.c | 555 | ||||
| -rw-r--r-- | hw/qdev.h | 281 | ||||
| -rw-r--r-- | hw/usb-bus.c | 12 | ||||
| -rw-r--r-- | hw/vga-pci.c | 5 | ||||
| -rw-r--r-- | hw/vmware_vga.h | 6 |
12 files changed, 980 insertions, 35 deletions
diff --git a/hw/cirrus_vga.c b/hw/cirrus_vga.c index 1b216e8813..1388a203eb 100644 --- a/hw/cirrus_vga.c +++ b/hw/cirrus_vga.c @@ -2899,7 +2899,7 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci, * ***************************************/ -void isa_cirrus_vga_init(MemoryRegion *system_memory) +DeviceState *isa_cirrus_vga_init(MemoryRegion *system_memory) { CirrusVGAState *s; @@ -2913,6 +2913,8 @@ void isa_cirrus_vga_init(MemoryRegion *system_memory) vmstate_register(NULL, 0, &vmstate_cirrus_vga, s); rom_add_vga(VGABIOS_CIRRUS_FILENAME); /* XXX ISA-LFB support */ + /* FIXME not qdev yet */ + return NULL; } /*************************************** @@ -2955,9 +2957,9 @@ static int pci_cirrus_vga_initfn(PCIDevice *dev) return 0; } -void pci_cirrus_vga_init(PCIBus *bus) +DeviceState *pci_cirrus_vga_init(PCIBus *bus) { - pci_create_simple(bus, -1, "cirrus-vga"); + return &pci_create_simple(bus, -1, "cirrus-vga")->qdev; } static PCIDeviceInfo cirrus_vga_info = { diff --git a/hw/container.c b/hw/container.c new file mode 100644 index 0000000000..9cbf3992c4 --- /dev/null +++ b/hw/container.c @@ -0,0 +1,20 @@ +#include "sysbus.h" + +static int container_initfn(SysBusDevice *dev) +{ + return 0; +} + +static SysBusDeviceInfo container_info = { + .init = container_initfn, + .qdev.name = "container", + .qdev.size = sizeof(SysBusDevice), + .qdev.no_user = 1, +}; + +static void container_init(void) +{ + sysbus_register_withprop(&container_info); +} + +device_init(container_init); diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index 2aaca2ff41..0c23cb0dba 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -614,6 +614,29 @@ static const MemoryRegionOps cmos_ops = { .old_portio = cmos_portio }; +// FIXME add int32 visitor +static void visit_type_int32(Visitor *v, int *value, const char *name, Error **errp) +{ + int64_t val = *value; + visit_type_int(v, &val, name, errp); +} + +static void rtc_get_date(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + ISADevice *isa = DO_UPCAST(ISADevice, qdev, dev); + RTCState *s = DO_UPCAST(RTCState, dev, isa); + + visit_start_struct(v, NULL, "struct tm", name, 0, errp); + visit_type_int32(v, &s->current_tm.tm_year, "tm_year", errp); + visit_type_int32(v, &s->current_tm.tm_mon, "tm_mon", errp); + visit_type_int32(v, &s->current_tm.tm_mday, "tm_mday", errp); + visit_type_int32(v, &s->current_tm.tm_hour, "tm_hour", errp); + visit_type_int32(v, &s->current_tm.tm_min, "tm_min", errp); + visit_type_int32(v, &s->current_tm.tm_sec, "tm_sec", errp); + visit_end_struct(v, errp); +} + static int rtc_initfn(ISADevice *dev) { RTCState *s = DO_UPCAST(RTCState, dev, dev); @@ -647,6 +670,10 @@ static int rtc_initfn(ISADevice *dev) qdev_set_legacy_instance_id(&dev->qdev, base, 2); qemu_register_reset(rtc_reset, s); + + qdev_property_add(&s->dev.qdev, "date", "struct tm", + rtc_get_date, NULL, NULL, s, NULL); + return 0; } diff --git a/hw/pc.c b/hw/pc.c index b6dcba2067..03466ec8d1 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -1069,38 +1069,44 @@ qemu_irq *pc_allocate_cpu_irq(void) return qemu_allocate_irqs(pic_irq_request, NULL, 1); } -void pc_vga_init(PCIBus *pci_bus) +DeviceState *pc_vga_init(PCIBus *pci_bus) { + DeviceState *dev = NULL; + if (cirrus_vga_enabled) { if (pci_bus) { - pci_cirrus_vga_init(pci_bus); + dev = pci_cirrus_vga_init(pci_bus); } else { - isa_cirrus_vga_init(get_system_memory()); + dev = isa_cirrus_vga_init(get_system_memory()); } } else if (vmsvga_enabled) { if (pci_bus) { - if (!pci_vmsvga_init(pci_bus)) { + dev = pci_vmsvga_init(pci_bus); + if (!dev) { fprintf(stderr, "Warning: vmware_vga not available," " using standard VGA instead\n"); - pci_vga_init(pci_bus); + dev = pci_vga_init(pci_bus); } } else { fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__); } #ifdef CONFIG_SPICE } else if (qxl_enabled) { - if (pci_bus) - pci_create_simple(pci_bus, -1, "qxl-vga"); - else + if (pci_bus) { + dev = &pci_create_simple(pci_bus, -1, "qxl-vga")->qdev; + } else { fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__); + } #endif } else if (std_vga_enabled) { if (pci_bus) { - pci_vga_init(pci_bus); + dev = pci_vga_init(pci_bus); } else { - isa_vga_init(); + dev = isa_vga_init(); } } + + return dev; } static void cpu_request_exit(void *opaque, int irq, int level) diff --git a/hw/pc.h b/hw/pc.h index b7b7e40ce7..b2000e7873 100644 --- a/hw/pc.h +++ b/hw/pc.h @@ -140,7 +140,7 @@ void pc_memory_init(MemoryRegion *system_memory, MemoryRegion *rom_memory, MemoryRegion **ram_memory); qemu_irq *pc_allocate_cpu_irq(void); -void pc_vga_init(PCIBus *pci_bus); +DeviceState *pc_vga_init(PCIBus *pci_bus); void pc_basic_device_init(qemu_irq *gsi, ISADevice **rtc_state, ISADevice **floppy, @@ -205,27 +205,27 @@ enum vga_retrace_method { extern enum vga_retrace_method vga_retrace_method; -static inline int isa_vga_init(void) +static inline DeviceState *isa_vga_init(void) { ISADevice *dev; dev = isa_try_create("isa-vga"); if (!dev) { fprintf(stderr, "Warning: isa-vga not available\n"); - return 0; + return NULL; } qdev_init_nofail(&dev->qdev); - return 1; + return &dev->qdev; } -int pci_vga_init(PCIBus *bus); +DeviceState *pci_vga_init(PCIBus *bus); int isa_vga_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift, MemoryRegion *address_space); /* cirrus_vga.c */ -void pci_cirrus_vga_init(PCIBus *bus); -void isa_cirrus_vga_init(MemoryRegion *address_space); +DeviceState *pci_cirrus_vga_init(PCIBus *bus); +DeviceState *isa_cirrus_vga_init(MemoryRegion *address_space); /* ne2000.c */ static inline bool isa_ne2000_init(int base, int irq, NICInfo *nd) diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 970f43c99c..b9bb09d1e0 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -99,6 +99,7 @@ static void pc_init1(MemoryRegion *system_memory, MemoryRegion *ram_memory; MemoryRegion *pci_memory; MemoryRegion *rom_memory; + DeviceState *dev; pc_cpus_init(cpu_model); @@ -168,7 +169,10 @@ static void pc_init1(MemoryRegion *system_memory, pc_register_ferr_irq(gsi[13]); - pc_vga_init(pci_enabled? pci_bus: NULL); + dev = pc_vga_init(pci_enabled? pci_bus: NULL); + if (dev) { + qdev_property_add_child(qdev_get_root(), "vga", dev, NULL); + } if (xen_enabled()) { pci_create_simple(pci_bus, -1, "xen-platform"); @@ -205,6 +209,17 @@ static void pc_init1(MemoryRegion *system_memory, } } + /* FIXME there's some major spaghetti here. Somehow we create the devices + * on the PIIX before we actually create it. We create the PIIX3 deep in + * the recess of the i440fx creation too and then lose the DeviceState. + * + * For now, let's "fix" this by making judicious use of paths. This is not + * generally the right way to do this. + */ + + qdev_property_add_child(qdev_resolve_path("/i440fx/piix3", NULL), + "rtc", (DeviceState *)rtc_state, NULL); + audio_init(gsi, pci_enabled ? pci_bus : NULL); pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, @@ -306,6 +321,14 @@ static QEMUMachine pc_machine_v1_0 = { .is_default = 1, }; +static QEMUMachine pc_machine_v0_15 = { + .name = "pc-0.15", + .desc = "Standard PC", + .init = pc_init_pci, + .max_cpus = 255, + .is_default = 1, +}; + static QEMUMachine pc_machine_v0_14 = { .name = "pc-0.14", .desc = "Standard PC", @@ -320,6 +343,22 @@ static QEMUMachine pc_machine_v0_14 = { .driver = "qxl-vga", .property = "revision", .value = stringify(2), + },{ + .driver = "virtio-blk-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-serial-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-net-pci", + .property = "event_idx", + .value = "off", + },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", }, { /* end of list */ } }, @@ -360,6 +399,10 @@ static QEMUMachine pc_machine_v0_13 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -407,6 +450,10 @@ static QEMUMachine pc_machine_v0_12 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -462,6 +509,10 @@ static QEMUMachine pc_machine_v0_11 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -529,6 +580,10 @@ static QEMUMachine pc_machine_v0_10 = { .property = "event_idx", .value = "off", },{ + .driver = "virtio-balloon-pci", + .property = "event_idx", + .value = "off", + },{ .driver = "AC97", .property = "use_broken_id", .value = stringify(1), @@ -557,6 +612,7 @@ static QEMUMachine xenfv_machine = { static void pc_machine_init(void) { qemu_register_machine(&pc_machine_v1_0); + qemu_register_machine(&pc_machine_v0_15); qemu_register_machine(&pc_machine_v0_14); qemu_register_machine(&pc_machine_v0_13); qemu_register_machine(&pc_machine_v0_12); diff --git a/hw/piix_pci.c b/hw/piix_pci.c index d183443b2f..d785d4ba07 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -288,6 +288,7 @@ static PCIBus *i440fx_common_init(const char *device_name, address_space_io, 0); s->bus = b; qdev_init_nofail(dev); + qdev_property_add_child(qdev_get_root(), "i440fx", dev, NULL); d = pci_create_simple(b, 0, device_name); *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d); @@ -323,6 +324,8 @@ static PCIBus *i440fx_common_init(const char *device_name, pci_create_simple_multifunction(b, -1, true, "PIIX3")); pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, PIIX_NUM_PIRQS); + + qdev_property_add_child(dev, "piix3", &piix3->dev.qdev, NULL); } piix3->pic = pic; diff --git a/hw/qdev.c b/hw/qdev.c index 106407f226..83913c72f2 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -83,6 +83,7 @@ static DeviceInfo *qdev_find_info(BusInfo *bus_info, const char *name) static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info) { DeviceState *dev; + Property *prop; assert(bus->info == info->bus_info); dev = g_malloc0(info->size); @@ -98,7 +99,19 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info) qdev_hot_added = true; } dev->instance_id_alias = -1; + QTAILQ_INIT(&dev->properties); dev->state = DEV_STATE_CREATED; + + for (prop = dev->info->props; prop && prop->name; prop++) { + qdev_property_add_legacy(dev, prop, NULL); + } + + for (prop = dev->info->bus_info->props; prop && prop->name; prop++) { + qdev_property_add_legacy(dev, prop, NULL); + } + + qdev_property_add_str(dev, "type", qdev_get_type, NULL, NULL); + return dev; } @@ -216,6 +229,32 @@ int qdev_device_help(QemuOpts *opts) return 1; } +static DeviceState *qdev_get_peripheral(void) +{ + static DeviceState *dev; + + if (dev == NULL) { + dev = qdev_create(NULL, "container"); + qdev_property_add_child(qdev_get_root(), "peripheral", dev, NULL); + qdev_init_nofail(dev); + } + + return dev; +} + +static DeviceState *qdev_get_peripheral_anon(void) +{ + static DeviceState *dev; + + if (dev == NULL) { + dev = qdev_create(NULL, "container"); + qdev_property_add_child(qdev_get_root(), "peripheral-anon", dev, NULL); + qdev_init_nofail(dev); + } + + return dev; +} + DeviceState *qdev_device_add(QemuOpts *opts) { const char *driver, *path, *id; @@ -267,7 +306,14 @@ DeviceState *qdev_device_add(QemuOpts *opts) id = qemu_opts_id(opts); if (id) { qdev->id = id; - } + qdev_property_add_child(qdev_get_peripheral(), qdev->id, qdev, NULL); + } else { + static int anon_count; + gchar *name = g_strdup_printf("device[%d]", anon_count++); + qdev_property_add_child(qdev_get_peripheral_anon(), name, + qdev, NULL); + g_free(name); + } if (qemu_opt_foreach(opts, set_property, qdev, 1) != 0) { qdev_free(qdev); return NULL; @@ -323,6 +369,11 @@ int qdev_unplug(DeviceState *dev) } assert(dev->info->unplug != NULL); + if (dev->ref != 0) { + qerror_report(QERR_DEVICE_IN_USE, dev->id?:""); + return -1; + } + qdev_hot_removed = true; return dev->info->unplug(dev); @@ -390,12 +441,31 @@ void qdev_init_nofail(DeviceState *dev) } } +static void qdev_property_del_all(DeviceState *dev) +{ + while (!QTAILQ_EMPTY(&dev->properties)) { + DeviceProperty *prop = QTAILQ_FIRST(&dev->properties); + + QTAILQ_REMOVE(&dev->properties, prop, node); + + if (prop->release) { + prop->release(dev, prop->name, prop->opaque); + } + + g_free(prop->name); + g_free(prop->type); + g_free(prop); + } +} + /* Unlink device from bus and free the structure. */ void qdev_free(DeviceState *dev) { BusState *bus; Property *prop; + qdev_property_del_all(dev); + if (dev->state == DEV_STATE_INITIALIZED) { while (dev->num_child_bus) { bus = QLIST_FIRST(&dev->child_bus); @@ -962,3 +1032,486 @@ char* qdev_get_fw_dev_path(DeviceState *dev) return strdup(path); } + +char *qdev_get_type(DeviceState *dev, Error **errp) +{ + return g_strdup(dev->info->name); +} + +void qdev_ref(DeviceState *dev) +{ + dev->ref++; +} + +void qdev_unref(DeviceState *dev) +{ + g_assert(dev->ref > 0); + dev->ref--; +} + +void qdev_property_add(DeviceState *dev, const char *name, const char *type, + DevicePropertyAccessor *get, DevicePropertyAccessor *set, + DevicePropertyRelease *release, + void *opaque, Error **errp) +{ + DeviceProperty *prop = g_malloc0(sizeof(*prop)); + + prop->name = g_strdup(name); + prop->type = g_strdup(type); + + prop->get = get; + prop->set = set; + prop->release = release; + prop->opaque = opaque; + + QTAILQ_INSERT_TAIL(&dev->properties, prop, node); +} + +static DeviceProperty *qdev_property_find(DeviceState *dev, const char *name) +{ + DeviceProperty *prop; + + QTAILQ_FOREACH(prop, &dev->properties, node) { + if (strcmp(prop->name, name) == 0) { + return prop; + } + } + + return NULL; +} + +void qdev_property_get(DeviceState *dev, Visitor *v, const char *name, + Error **errp) +{ + DeviceProperty *prop = qdev_property_find(dev, name); + + if (prop == NULL) { + error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name); + return; + } + + if (!prop->get) { + error_set(errp, QERR_PERMISSION_DENIED); + } else { + prop->get(dev, v, prop->opaque, name, errp); + } +} + +void qdev_property_set(DeviceState *dev, Visitor *v, const char *name, + Error **errp) +{ + DeviceProperty *prop = qdev_property_find(dev, name); + + if (prop == NULL) { + error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name); + return; + } + + if (!prop->set) { + error_set(errp, QERR_PERMISSION_DENIED); + } else { + prop->set(dev, prop->opaque, v, name, errp); + } +} + +const char *qdev_property_get_type(DeviceState *dev, const char *name, Error **errp) +{ + DeviceProperty *prop = qdev_property_find(dev, name); + + if (prop == NULL) { + error_set(errp, QERR_PROPERTY_NOT_FOUND, dev->id?:"", name); + return NULL; + } + + return prop->type; +} + +/** + * Legacy property handling + */ + +static void qdev_get_legacy_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + Property *prop = opaque; + + if (prop->info->print) { + char buffer[1024]; + char *ptr = buffer; + + prop->info->print(dev, prop, buffer, sizeof(buffer)); + visit_type_str(v, &ptr, name, errp); + } else { + error_set(errp, QERR_PERMISSION_DENIED); + } +} + +static void qdev_set_legacy_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + Property *prop = opaque; + + if (dev->state != DEV_STATE_CREATED) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + if (prop->info->parse) { + Error *local_err = NULL; + char *ptr = NULL; + + visit_type_str(v, &ptr, name, &local_err); + if (!local_err) { + int ret; + ret = prop->info->parse(dev, prop, ptr); + if (ret != 0) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + name, prop->info->name); + } + g_free(ptr); + } else { + error_propagate(errp, local_err); + } + } else { + error_set(errp, QERR_PERMISSION_DENIED); + } +} + +/** + * @qdev_add_legacy_property - adds a legacy property + * + * Do not use this is new code! Properties added through this interface will + * be given types in the "legacy<>" type namespace. + * + * Legacy properties are always processed as strings. The format of the string + * depends on the property type. + */ +void qdev_property_add_legacy(DeviceState *dev, Property *prop, + Error **errp) +{ + gchar *type; + + type = g_strdup_printf("legacy<%s>", prop->info->name); + + qdev_property_add(dev, prop->name, type, + qdev_get_legacy_property, + qdev_set_legacy_property, + NULL, + prop, errp); + + g_free(type); +} + +DeviceState *qdev_get_root(void) +{ + static DeviceState *qdev_root; + + if (!qdev_root) { + qdev_root = qdev_create(NULL, "container"); + qdev_init_nofail(qdev_root); + } + + return qdev_root; +} + +static void qdev_get_child_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *child = opaque; + gchar *path; + + path = qdev_get_canonical_path(child); + visit_type_str(v, &path, name, errp); + g_free(path); +} + +void qdev_property_add_child(DeviceState *dev, const char *name, + DeviceState *child, Error **errp) +{ + gchar *type; + + type = g_strdup_printf("child<%s>", child->info->name); + + qdev_property_add(dev, name, type, qdev_get_child_property, + NULL, NULL, child, errp); + + qdev_ref(child); + g_assert(child->parent == NULL); + child->parent = dev; + + g_free(type); +} + +static void qdev_get_link_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState **child = opaque; + gchar *path; + + if (*child) { + path = qdev_get_canonical_path(*child); + visit_type_str(v, &path, name, errp); + g_free(path); + } else { + path = (gchar *)""; + visit_type_str(v, &path, name, errp); + } +} + +static void qdev_set_link_property(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState **child = opaque; + bool ambiguous = false; + const char *type; + char *path; + + type = qdev_property_get_type(dev, name, NULL); + + visit_type_str(v, &path, name, errp); + + if (*child) { + qdev_unref(*child); + } + + if (strcmp(path, "") != 0) { + DeviceState *target; + + target = qdev_resolve_path(path, &ambiguous); + if (target) { + gchar *target_type; + + target_type = g_strdup_printf("link<%s>", target->info->name); + if (strcmp(target_type, type) == 0) { + *child = target; + qdev_ref(target); + } else { + error_set(errp, QERR_INVALID_PARAMETER_TYPE, name, type); + } + + g_free(target_type); + } else { + error_set(errp, QERR_DEVICE_NOT_FOUND, path); + } + } else { + *child = NULL; + } + + g_free(path); +} + +void qdev_property_add_link(DeviceState *dev, const char *name, + const char *type, DeviceState **child, + Error **errp) +{ + gchar *full_type; + + full_type = g_strdup_printf("link<%s>", type); + + qdev_property_add(dev, name, full_type, + qdev_get_link_property, + qdev_set_link_property, + NULL, child, errp); + + g_free(full_type); +} + +gchar *qdev_get_canonical_path(DeviceState *dev) +{ + DeviceState *root = qdev_get_root(); + char *newpath = NULL, *path = NULL; + + while (dev != root) { + DeviceProperty *prop = NULL; + + g_assert(dev->parent != NULL); + + QTAILQ_FOREACH(prop, &dev->parent->properties, node) { + if (!strstart(prop->type, "child<", NULL)) { + continue; + } + + if (prop->opaque == dev) { + if (path) { + newpath = g_strdup_printf("%s/%s", prop->name, path); + g_free(path); + path = newpath; + } else { + path = g_strdup(prop->name); + } + break; + } + } + + g_assert(prop != NULL); + + dev = dev->parent; + } + + newpath = g_strdup_printf("/%s", path); + g_free(path); + + return newpath; +} + +static DeviceState *qdev_resolve_abs_path(DeviceState *parent, + gchar **parts, + int index) +{ + DeviceProperty *prop; + DeviceState *child; + + if (parts[index] == NULL) { + return parent; + } + + if (strcmp(parts[index], "") == 0) { + return qdev_resolve_abs_path(parent, parts, index + 1); + } + + prop = qdev_property_find(parent, parts[index]); + if (prop == NULL) { + return NULL; + } + + child = NULL; + if (strstart(prop->type, "link<", NULL)) { + DeviceState **pchild = prop->opaque; + if (*pchild) { + child = *pchild; + } + } else if (strstart(prop->type, "child<", NULL)) { + child = prop->opaque; + } + + if (!child) { + return NULL; + } + + return qdev_resolve_abs_path(child, parts, index + 1); +} + +static DeviceState *qdev_resolve_partial_path(DeviceState *parent, + gchar **parts, + bool *ambiguous) +{ + DeviceState *dev; + DeviceProperty *prop; + + dev = qdev_resolve_abs_path(parent, parts, 0); + + QTAILQ_FOREACH(prop, &parent->properties, node) { + DeviceState *found; + + if (!strstart(prop->type, "child<", NULL)) { + continue; + } + + found = qdev_resolve_partial_path(prop->opaque, parts, ambiguous); + if (found) { + if (dev) { + if (ambiguous) { + *ambiguous = true; + } + return NULL; + } + dev = found; + } + + if (ambiguous && *ambiguous) { + return NULL; + } + } + + return dev; +} + +DeviceState *qdev_resolve_path(const char *path, bool *ambiguous) +{ + bool partial_path = true; + DeviceState *dev; + gchar **parts; + + parts = g_strsplit(path, "/", 0); + if (parts == NULL || parts[0] == NULL) { + g_strfreev(parts); + return qdev_get_root(); + } + + if (strcmp(parts[0], "") == 0) { + partial_path = false; + } + + if (partial_path) { + if (ambiguous) { + *ambiguous = false; + } + dev = qdev_resolve_partial_path(qdev_get_root(), parts, ambiguous); + } else { + dev = qdev_resolve_abs_path(qdev_get_root(), parts, 1); + } + + g_strfreev(parts); + + return dev; +} + +typedef struct StringProperty +{ + char *(*get)(DeviceState *, Error **); + void (*set)(DeviceState *, const char *, Error **); +} StringProperty; + +static void qdev_property_get_str(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + StringProperty *prop = opaque; + char *value; + + value = prop->get(dev, errp); + if (value) { + visit_type_str(v, &value, name, errp); + g_free(value); + } +} + +static void qdev_property_set_str(DeviceState *dev, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + StringProperty *prop = opaque; + char *value; + Error *local_err = NULL; + + visit_type_str(v, &value, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + prop->set(dev, value, errp); + g_free(value); +} + +static void qdev_property_release_str(DeviceState *dev, const char *name, + void *opaque) +{ + StringProperty *prop = opaque; + g_free(prop); +} + +void qdev_property_add_str(DeviceState *dev, const char *name, + char *(*get)(DeviceState *, Error **), + void (*set)(DeviceState *, const char *, Error **), + Error **errp) +{ + StringProperty *prop = g_malloc0(sizeof(*prop)); + + prop->get = get; + prop->set = set; + + qdev_property_add(dev, name, "string", + get ? qdev_property_get_str : NULL, + set ? qdev_property_set_str : NULL, + qdev_property_release_str, + prop, errp); +} diff --git a/hw/qdev.h b/hw/qdev.h index 36a4198c89..6e184278f4 100644 --- a/hw/qdev.h +++ b/hw/qdev.h @@ -5,6 +5,7 @@ #include "qemu-queue.h" #include "qemu-char.h" #include "qemu-option.h" +#include "qapi/qapi-visit-core.h" typedef struct Property Property; @@ -27,6 +28,44 @@ enum { DEV_NVECTORS_UNSPECIFIED = -1, }; +/** + * @DevicePropertyAccessor - called when trying to get/set a property + * + * @dev the device that owns the property + * @v the visitor that contains the property data + * @opaque the device property opaque + * @name the name of the property + * @errp a pointer to an Error that is filled if getting/setting fails. + */ +typedef void (DevicePropertyAccessor)(DeviceState *dev, + Visitor *v, + void *opaque, + const char *name, + Error **errp); + +/** + * @DevicePropertyRelease - called when a property is removed from a device + * + * @dev the device that owns the property + * @name the name of the property + * @opaque the opaque registered with the property + */ +typedef void (DevicePropertyRelease)(DeviceState *dev, + const char *name, + void *opaque); + +typedef struct DeviceProperty +{ + gchar *name; + gchar *type; + DevicePropertyAccessor *get; + DevicePropertyAccessor *set; + DevicePropertyRelease *release; + void *opaque; + + QTAILQ_ENTRY(DeviceProperty) node; +} DeviceProperty; + /* This structure should not be accessed directly. We declare it here so that it can be embedded in individual device state structures. */ struct DeviceState { @@ -45,6 +84,18 @@ struct DeviceState { QTAILQ_ENTRY(DeviceState) sibling; int instance_id_alias; int alias_required_for_version; + + /** + * This tracks the number of references between devices. See @qdev_ref for + * more information. + */ + uint32_t ref; + + QTAILQ_HEAD(, DeviceProperty) properties; + + /* Do not, under any circumstance, use this parent link below anywhere + * outside of qdev.c. You have been warned. */ + DeviceState *parent; }; typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent); @@ -329,4 +380,234 @@ char *qdev_get_fw_dev_path(DeviceState *dev); /* This is a nasty hack to allow passing a NULL bus to qdev_create. */ extern struct BusInfo system_bus_info; +/** + * @qdev_ref + * + * Increase the reference count of a device. A device cannot be freed as long + * as its reference count is greater than zero. + * + * @dev - the device + */ +void qdev_ref(DeviceState *dev); + +/** + * @qdef_unref + * + * Decrease the reference count of a device. A device cannot be freed as long + * as its reference count is greater than zero. + * + * @dev - the device + */ +void qdev_unref(DeviceState *dev); + +/** + * @qdev_property_add - add a new property to a device + * + * @dev - the device to add a property to + * + * @name - the name of the property. This can contain any character except for + * a forward slash. In general, you should use hyphens '-' instead of + * underscores '_' when naming properties. + * + * @type - the type name of the property. This namespace is pretty loosely + * defined. Sub namespaces are constructed by using a prefix and then + * to angle brackets. For instance, the type 'virtio-net-pci' in the + * 'link' namespace would be 'link<virtio-net-pci>'. + * + * @get - the getter to be called to read a property. If this is NULL, then + * the property cannot be read. + * + * @set - the setter to be called to write a property. If this is NULL, + * then the property cannot be written. + * + * @release - called when the property is removed from the device. This is + * meant to allow a property to free its opaque upon device + * destruction. This may be NULL. + * + * @opaque - an opaque pointer to pass to the callbacks for the property + * + * @errp - returns an error if this function fails + */ +void qdev_property_add(DeviceState *dev, const char *name, const char *type, + DevicePropertyAccessor *get, DevicePropertyAccessor *set, + DevicePropertyRelease *release, + void *opaque, Error **errp); + +/** + * @qdev_property_get - reads a property from a device + * + * @dev - the device + * + * @v - the visitor that will receive the property value. This should be an + * Output visitor and the data will be written with @name as the name. + * + * @name - the name of the property + * + * @errp - returns an error if this function fails + */ +void qdev_property_get(DeviceState *dev, Visitor *v, const char *name, + Error **errp); + +/** + * @qdev_property_set - writes a property to a device + * + * @dev - the device + * + * @v - the visitor that will be used to write the property value. This should + * be an Input visitor and the data will be first read with @name as the + * name and then written as the property value. + * + * @name - the name of the property + * + * @errp - returns an error if this function fails + */ +void qdev_property_set(DeviceState *dev, Visitor *v, const char *name, + Error **errp); + +/** + * @qdev_property_get_type - returns the type of a property + * + * @dev - the device + * + * @name - the name of the property + * + * @errp - returns an error if this function fails + * + * Returns: + * The type name of the property. + */ +const char *qdev_property_get_type(DeviceState *dev, const char *name, + Error **errp); + +/** + * @qdev_property_add_legacy - add a legacy @Property to a device + * + * DO NOT USE THIS IN NEW CODE! + */ +void qdev_property_add_legacy(DeviceState *dev, Property *prop, Error **errp); + +/** + * @qdev_get_root - returns the root device of the composition tree + * + * Returns: + * The root of the composition tree. + */ +DeviceState *qdev_get_root(void); + +/** + * @qdev_get_canonical_path - returns the canonical path for a device. This + * is the path within the composition tree starting from the root. + * + * Returns: + * The canonical path in the composition tree. + */ +gchar *qdev_get_canonical_path(DeviceState *dev); + +/** + * @qdev_resolve_path - resolves a path returning a device + * + * There are two types of supported paths--absolute paths and partial paths. + * + * Absolute paths are derived from the root device and can follow child<> or + * link<> properties. Since they can follow link<> properties, they can be + * arbitrarily long. Absolute paths look like absolute filenames and are + * prefixed with a leading slash. + * + * Partial paths look like relative filenames. They do not begin with a + * prefix. The matching rules for partial paths are subtle but designed to make + * specifying devices easy. At each level of the composition tree, the partial + * path is matched as an absolute path. The first match is not returned. At + * least two matches are searched for. A successful result is only returned if + * only one match is founded. If more than one match is found, a flag is + * return to indicate that the match was ambiguous. + * + * @path - the path to resolve + * + * @ambiguous - returns true if the path resolution failed because of an + * ambiguous match + * + * Returns: + * The matched device or NULL on path lookup failure. + */ +DeviceState *qdev_resolve_path(const char *path, bool *ambiguous); + +/** + * @qdev_property_add_child - Add a child property to a device + * + * Child properties form the composition tree. All devices need to be a child + * of another device. Devices can only be a child of one device. + * + * There is no way for a child to determine what its parent is. It is not + * a bidirectional relationship. This is by design. + * + * @dev - the device to add a property to + * + * @name - the name of the property + * + * @child - the child device + * + * @errp - if an error occurs, a pointer to an area to store the area + */ +void qdev_property_add_child(DeviceState *dev, const char *name, + DeviceState *child, Error **errp); + +/** + * @qdev_property_add_link - Add a link property to a device + * + * Links establish relationships between devices. Links are unidirectional + * although two links can be combined to form a bidirectional relationship + * between devices. + * + * Links form the graph in the device model. + * + * @dev - the device to add a property to + * + * @name - the name of the property + * + * @type - the qdev type of the link + * + * @child - a pointer to where the link device reference is stored + * + * @errp - if an error occurs, a pointer to an area to store the area + */ +void qdev_property_add_link(DeviceState *dev, const char *name, + const char *type, DeviceState **child, + Error **errp); + +/** + * @qdev_property_add_str + * + * Add a string property using getters/setters. This function will add a + * property of type 'string'. + * + * @dev - the device to add a property to + * + * @name - the name of the property + * + * @get - the getter or NULL if the property is write-only. This function must + * return a string to be freed by @g_free(). + * + * @set - the setter or NULL if the property is read-only + * + * @errp - if an error occurs, a pointer to an area to store the error + */ +void qdev_property_add_str(DeviceState *dev, const char *name, + char *(*get)(DeviceState *, Error **), + void (*set)(DeviceState *, const char *, Error **), + Error **errp); + +/** + * @qdev_get_type + * + * Returns the string representation of the type of this object. + * + * @dev - the device + * + * @errp - if an error occurs, a pointer to an area to store the error + * + * Returns: a string representing the type. This must be freed by the caller + * with g_free(). + */ +char *qdev_get_type(DeviceState *dev, Error **errp); + #endif diff --git a/hw/usb-bus.c b/hw/usb-bus.c index 8cafb76fff..8203390929 100644 --- a/hw/usb-bus.c +++ b/hw/usb-bus.c @@ -77,23 +77,21 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base) QLIST_INIT(&dev->strings); rc = usb_claim_port(dev); if (rc != 0) { - goto err; + return rc; } rc = dev->info->init(dev); if (rc != 0) { - goto err; + usb_release_port(dev); + return rc; } if (dev->auto_attach) { rc = usb_device_attach(dev); if (rc != 0) { - goto err; + usb_qdev_exit(qdev); + return rc; } } return 0; - -err: - usb_qdev_exit(qdev); - return rc; } static int usb_qdev_exit(DeviceState *qdev) diff --git a/hw/vga-pci.c b/hw/vga-pci.c index 14bfadbfcf..a75dbf3974 100644 --- a/hw/vga-pci.c +++ b/hw/vga-pci.c @@ -70,10 +70,9 @@ static int pci_vga_initfn(PCIDevice *dev) return 0; } -int pci_vga_init(PCIBus *bus) +DeviceState *pci_vga_init(PCIBus *bus) { - pci_create_simple(bus, -1, "VGA"); - return 0; + return &pci_create_simple(bus, -1, "VGA")->qdev; } static PCIDeviceInfo vga_info = { diff --git a/hw/vmware_vga.h b/hw/vmware_vga.h index 5132573a56..db11cbfac8 100644 --- a/hw/vmware_vga.h +++ b/hw/vmware_vga.h @@ -4,15 +4,15 @@ #include "qemu-common.h" /* vmware_vga.c */ -static inline bool pci_vmsvga_init(PCIBus *bus) +static inline DeviceState *pci_vmsvga_init(PCIBus *bus) { PCIDevice *dev; dev = pci_try_create(bus, -1, "vmware-svga"); if (!dev || qdev_init(&dev->qdev) < 0) { - return false; + return NULL; } else { - return true; + return &dev->qdev; } } |