diff options
Diffstat (limited to 'hw/qdev-properties.c')
-rw-r--r-- | hw/qdev-properties.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index 0307a7830b..247ca6c5b4 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -779,6 +779,110 @@ PropertyInfo qdev_prop_pci_host_devaddr = { .set = set_pci_host_devaddr, }; +/* --- support for array properties --- */ + +/* Used as an opaque for the object properties we add for each + * array element. Note that the struct Property must be first + * in the struct so that a pointer to this works as the opaque + * for the underlying element's property hooks as well as for + * our own release callback. + */ +typedef struct { + struct Property prop; + char *propname; + ObjectPropertyRelease *release; +} ArrayElementProperty; + +/* object property release callback for array element properties: + * we call the underlying element's property release hook, and + * then free the memory we allocated when we added the property. + */ +static void array_element_release(Object *obj, const char *name, void *opaque) +{ + ArrayElementProperty *p = opaque; + if (p->release) { + p->release(obj, name, opaque); + } + g_free(p->propname); + g_free(p); +} + +static void set_prop_arraylen(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + /* Setter for the property which defines the length of a + * variable-sized property array. As well as actually setting the + * array-length field in the device struct, we have to create the + * array itself and dynamically add the corresponding properties. + */ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + uint32_t *alenptr = qdev_get_prop_ptr(dev, prop); + void **arrayptr = (void *)dev + prop->arrayoffset; + void *eltptr; + const char *arrayname; + int i; + + if (dev->realized) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + if (*alenptr) { + error_setg(errp, "array size property %s may not be set more than once", + name); + return; + } + visit_type_uint32(v, alenptr, name, errp); + if (error_is_set(errp)) { + return; + } + if (!*alenptr) { + return; + } + + /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix; + * strip it off so we can get the name of the array itself. + */ + assert(strncmp(name, PROP_ARRAY_LEN_PREFIX, + strlen(PROP_ARRAY_LEN_PREFIX)) == 0); + arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX); + + /* Note that it is the responsibility of the individual device's deinit + * to free the array proper. + */ + *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize); + for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) { + char *propname = g_strdup_printf("%s[%d]", arrayname, i); + ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1); + arrayprop->release = prop->arrayinfo->release; + arrayprop->propname = propname; + arrayprop->prop.info = prop->arrayinfo; + arrayprop->prop.name = propname; + /* This ugly piece of pointer arithmetic sets up the offset so + * that when the underlying get/set hooks call qdev_get_prop_ptr + * they get the right answer despite the array element not actually + * being inside the device struct. + */ + arrayprop->prop.offset = eltptr - (void *)dev; + assert(qdev_get_prop_ptr(dev, &arrayprop->prop) == eltptr); + object_property_add(obj, propname, + arrayprop->prop.info->name, + arrayprop->prop.info->get, + arrayprop->prop.info->set, + array_element_release, + arrayprop, errp); + if (error_is_set(errp)) { + return; + } + } +} + +PropertyInfo qdev_prop_arraylen = { + .name = "uint32", + .get = get_uint32, + .set = set_prop_arraylen, +}; + /* --- public helpers --- */ static Property *qdev_prop_walk(Property *props, const char *name) |