diff mbox

[06/10] qdev: Implement (variable length) array properties

Message ID 1359985476-29380-7-git-send-email-peter.maydell@linaro.org
State Superseded
Headers show

Commit Message

Peter Maydell Feb. 4, 2013, 1:44 p.m. UTC
Add support for declaring array properties for qdev devices.
These work by defining an initial static property 'len-arrayname'
which the user of the device should set to the desired size
of the array. When this property is set, memory is allocated
for the array elements, and dynamic properties "arrayname[0]",
"arrayname[1]"... are created so the user of the device can
then set the values of the individual array elements.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/qdev-core.h       |    3 ++
 hw/qdev-properties.c |  104 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/qdev-properties.h |   39 +++++++++++++++++++
 3 files changed, 146 insertions(+)

Comments

Peter Maydell Feb. 11, 2013, 2:38 p.m. UTC | #1
On 4 February 2013 13:44, Peter Maydell <peter.maydell@linaro.org> wrote:
> Add support for declaring array properties for qdev devices.
> These work by defining an initial static property 'len-arrayname'
> which the user of the device should set to the desired size
> of the array. When this property is set, memory is allocated
> for the array elements, and dynamic properties "arrayname[0]",
> "arrayname[1]"... are created so the user of the device can
> then set the values of the individual array elements.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Just a ping to try to elicit opinions on the array properties
here. Patchwork URL http://patchwork.ozlabs.org/patch/217964/
for those I just added to the CC list.

thanks
-- PMM
Peter Maydell Feb. 18, 2013, 10:30 a.m. UTC | #2
On 11 February 2013 14:38, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 4 February 2013 13:44, Peter Maydell <peter.maydell@linaro.org> wrote:
>> Add support for declaring array properties for qdev devices.
>> These work by defining an initial static property 'len-arrayname'
>> which the user of the device should set to the desired size
>> of the array. When this property is set, memory is allocated
>> for the array elements, and dynamic properties "arrayname[0]",
>> "arrayname[1]"... are created so the user of the device can
>> then set the values of the individual array elements.
>>
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
> Just a ping to try to elicit opinions on the array properties
> here. Patchwork URL http://patchwork.ozlabs.org/patch/217964/
> for those I just added to the CC list.

Another ping for review for qdev property arrays; if I don't see any
review I shall assume it is entirely uncontroversial and stick
it into arm-devs.next with the patches that need it :-)

thanks
-- PMM
diff mbox

Patch

diff --git a/hw/qdev-core.h b/hw/qdev-core.h
index 2486f36..547fbc7 100644
--- a/hw/qdev-core.h
+++ b/hw/qdev-core.h
@@ -175,6 +175,9 @@  struct Property {
     uint8_t      bitnr;
     uint8_t      qtype;
     int64_t      defval;
+    int          arrayoffset;
+    PropertyInfo *arrayinfo;
+    int          arrayfieldsize;
 };
 
 struct PropertyInfo {
diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c
index a8a31f5..5f03942 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)
diff --git a/hw/qdev-properties.h b/hw/qdev-properties.h
index 20c67f3..af2f086 100644
--- a/hw/qdev-properties.h
+++ b/hw/qdev-properties.h
@@ -26,6 +26,7 @@  extern PropertyInfo qdev_prop_vlan;
 extern PropertyInfo qdev_prop_pci_devfn;
 extern PropertyInfo qdev_prop_blocksize;
 extern PropertyInfo qdev_prop_pci_host_devaddr;
+extern PropertyInfo qdev_prop_arraylen;
 
 #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \
         .name      = (_name),                                    \
@@ -51,6 +52,44 @@  extern PropertyInfo qdev_prop_pci_host_devaddr;
         .defval    = (bool)_defval,                              \
         }
 
+#define PROP_ARRAY_LEN_PREFIX "len-"
+
+/**
+ * DEFINE_PROP_ARRAY:
+ * @_name: name of the array
+ * @_state: name of the device state structure type
+ * @_field: uint32_t field in @_state to hold the array length
+ * @_arrayfield: field in @_state (of type '@_arraytype *') which
+ *               will point to the array
+ * @_arrayprop: PropertyInfo defining what property the array elements have
+ * @_arraytype: C type of the array elements
+ *
+ * Define device properties for a variable-length array _name.  A
+ * static property "len-arrayname" is defined. When the device creator
+ * sets this property to the desired length of array, further dynamic
+ * properties "arrayname[0]", "arrayname[1]", ...  are defined so the
+ * device creator can set the array element values. Setting the
+ * "len-arrayname" property more than once is an error.
+ *
+ * When the array length is set, the @_field member of the device
+ * struct is set to the array length, and @_arrayfield is set to point
+ * to (zero-initialised) memory allocated for the array.  For a zero
+ * length array, @_field will be set to 0 and @_arrayfield to NULL.
+ * It is the responsibility of the device deinit code to free the
+ * @_arrayfield memory.
+ */
+#define DEFINE_PROP_ARRAY(_name, _state, _field,                        \
+                          _arrayfield, _arrayprop, _arraytype) {        \
+        .name = (PROP_ARRAY_LEN_PREFIX _name),                          \
+        .info = &(qdev_prop_arraylen),                                  \
+        .offset = offsetof(_state, _field)                              \
+            + type_check(uint32_t, typeof_field(_state, _field)),       \
+        .qtype = QTYPE_QINT,                                            \
+        .arrayinfo = &(_arrayprop),                                     \
+        .arrayfieldsize = sizeof(_arraytype),                           \
+        .arrayoffset = offsetof(_state, _arrayfield),                   \
+        }
+
 #define DEFINE_PROP_UINT8(_n, _s, _f, _d)                       \
     DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint8, uint8_t)
 #define DEFINE_PROP_UINT16(_n, _s, _f, _d)                      \