[PATCH v3 57/74] hw/gpio/pca955x: convert pin/led property to QAPI-aware enum

Marc-André Lureau <[email protected]>
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
Replaces the runtime-parsed visitor-based QOM property accessors with
compile-time per-pin/per-LED accessor functions using QEMU_REPEAT.

Signed-off-by: Marc-André Lureau <[email protected]>
---
 hw/gpio/pca9552.c | 205 +++++++++++++++++++++---------------------------------
 hw/gpio/pca9554.c | 136 ++++++++++++++----------------------
 2 files changed, 131 insertions(+), 210 deletions(-)

diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c
index 16741c22dea3..b743a30d80ff 100644
--- a/hw/gpio/pca9552.c
+++ b/hw/gpio/pca9552.c
@@ -25,6 +25,7 @@
 #include "qapi/error.h"
 #include "qapi/qapi-types-machine.h"
 #include "qapi/qapi-visit-machine.h"
+#include "qapi/qapi-type-infos-machine.h"
 #include "trace.h"
 #include "qom/object.h"
 
@@ -354,33 +355,6 @@ static int pca955x_event(I2CSlave *i2c, enum i2c_event event)
     return 0;
 }
 
-static void pca955x_get_led(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(obj);
-    PCA955xState *s = PCA955X(obj);
-    int led, rc, reg;
-    Pca9552LedState state;
-
-    rc = sscanf(name, "led%2d", &led);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (led < 0 || led >= k->pin_count) {
-        error_setg(errp, "%s: invalid led %s", __func__, name);
-        return;
-    }
-    /*
-     * Get the LSx register as the qom interface should expose the device
-     * state, not the modeled 'input line' behaviour which would come from
-     * reading the INPUTx reg
-     */
-    reg = PCA9552_LS0 + led / 4;
-    state = (pca955x_read(s, reg) >> ((led % 4) * 2)) & 0x3;
-    visit_type_Pca9552LedState(v, name, &state, errp);
-}
-
 /*
  * Return an LED selector register value based on an existing one, with
  * the appropriate 2-bit state value set for the given LED number (0-3).
@@ -391,97 +365,8 @@ static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
                 ((state & 0x3) << (led_num << 1));
 }
 
-static void pca955x_set_led(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(obj);
-    PCA955xState *s = PCA955X(obj);
-    int led, rc, reg, val;
-    Pca9552LedState state;
-
-    if (!visit_type_Pca9552LedState(v, name, &state, errp)) {
-        return;
-    }
-    rc = sscanf(name, "led%2d", &led);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (led < 0 || led >= k->pin_count) {
-        error_setg(errp, "%s: invalid led %s", __func__, name);
-        return;
-    }
-
-    reg = PCA9552_LS0 + led / 4;
-    val = pca955x_read(s, reg);
-    val = pca955x_ledsel(val, led % 4, state);
-    pca955x_write(s, reg, val);
-}
-
 static void pca955x_set_ext_state(PCA955xState *s, int pin, int level);
 
-static void pca955x_get_pin(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(obj);
-    PCA955xState *s = PCA955X(obj);
-    int pin, rc;
-    uint8_t input_reg;
-    Pca9552PinState state;
-
-    rc = sscanf(name, "pin%2d", &pin);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (pin < 0 || pin >= k->pin_count) {
-        error_setg(errp, "%s invalid pin %s", __func__, name);
-        return;
-    }
-
-    /*
-     * Report the raw pin logic level; polarity inversion is a read-time
-     * transform applied to the INPUT register, not to the pin state itself.
-     */
-    input_reg = PCA9535_INPUT0 + (pin / 8);
-    state = (s->regs[input_reg] >> (pin % 8)) & 0x1;
-    visit_type_Pca9552PinState(v, name, &state, errp);
-}
-
-static void pca955x_set_pin(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA955xClass *k = PCA955X_GET_CLASS(obj);
-    PCA955xState *s = PCA955X(obj);
-    int pin, rc;
-    Pca9552PinState state;
-    uint8_t config_reg;
-
-    if (!visit_type_Pca9552PinState(v, name, &state, errp)) {
-        return;
-    }
-    rc = sscanf(name, "pin%2d", &pin);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (pin < 0 || pin >= k->pin_count) {
-        error_setg(errp, "%s invalid pin %s", __func__, name);
-        return;
-    }
-
-    /* Only input-configured pins can be driven by an external device. */
-    config_reg = PCA9535_CONFIG0 + (pin / 8);
-    if (!((s->regs[config_reg] >> (pin % 8)) & 0x1)) {
-        qemu_log_mask(LOG_UNIMP,
-                      "%s: pin %d is configured as output, ignoring set\n",
-                      s->description, pin);
-        return;
-    }
-
-    pca955x_set_ext_state(s, pin, state != PCA9552_PIN_STATE_LOW);
-}
-
 static const VMStateDescription pca9552_vmstate = {
     .name = "PCA9552",
     .version_id = 0,
@@ -536,26 +421,92 @@ static void pca9535_reset_hold(Object *obj, ResetType type)
     s->len = 0;
 }
 
+/*
+ * Get the LSx register as the qom interface should expose the device
+ * state, not the modeled 'input line' behaviour which would come from
+ * reading the INPUTx reg
+ */
+#define DEFINE_LED_ACCESSORS(n)                                              \
+static int prop_get_led##n(Object *obj, Error **errp)                        \
+{                                                                            \
+    PCA955xState *s = PCA955X(obj);                                          \
+    uint8_t reg = PCA9552_LS0 + (n) / 4;                                     \
+    return (pca955x_read(s, reg) >> (((n) % 4) * 2)) & 0x3;                  \
+}                                                                            \
+static void prop_set_led##n(Object *obj, int val, Error **errp)              \
+{                                                                            \
+    PCA955xState *s = PCA955X(obj);                                          \
+    uint8_t reg = PCA9552_LS0 + (n) / 4;                                     \
+    uint8_t old = pca955x_read(s, reg);                                      \
+    pca955x_write(s, reg, pca955x_ledsel(old, (n) % 4, val));                \
+}
+
+QEMU_REPEAT(PCA955X_PIN_COUNT_MAX, DEFINE_LED_ACCESSORS)
+
+#define LED_ENUM_PROP(n) {                                              \
+    .name = "led" #n,                                                   \
+    .default_value = -1,                                                \
+    .qapi_type = &Pca9552LedState_type_info,                            \
+    .get = prop_get_led##n,                                             \
+    .set = prop_set_led##n,                                             \
+},
+
+static const QapiEnumProp led_enum_props[] = {
+    QEMU_REPEAT(PCA955X_PIN_COUNT_MAX, LED_ENUM_PROP)
+};
+
+/*
+ * Report the raw pin logic level; polarity inversion is a read-time
+ * transform applied to the INPUT register, not to the pin state
+ * itself.
+ */
+#define DEFINE_PIN_ACCESSORS(n)                                              \
+static int prop_get_pin##n(Object *obj, Error **errp)                        \
+{                                                                            \
+    PCA955xState *s = PCA955X(obj);                                          \
+    uint8_t input_reg = PCA9535_INPUT0 + (n) / 8;                            \
+    return (s->regs[input_reg] >> ((n) % 8)) & 0x1;                          \
+}                                                                            \
+static void prop_set_pin##n(Object *obj, int val, Error **errp)              \
+{                                                                            \
+    PCA955xState *s = PCA955X(obj);                                          \
+    uint8_t config_reg = PCA9535_CONFIG0 + (n) / 8;                          \
+    /* Only input-configured pins can be driven by an external device */     \
+    if (!((s->regs[config_reg] >> ((n) % 8)) & 0x1)) {                       \
+        qemu_log_mask(LOG_UNIMP,                                             \
+                      "%s: pin %d configured as output, ignoring set\n",     \
+                      s->description, (n));                                  \
+        return;                                                              \
+    }                                                                        \
+    pca955x_set_ext_state(s, (n), val != PCA9552_PIN_STATE_LOW);             \
+}
+
+QEMU_REPEAT(PCA955X_PIN_COUNT_MAX, DEFINE_PIN_ACCESSORS)
+
+#define PIN_ENUM_PROP(n) {                                              \
+    .name = "pin" #n,                                                   \
+    .default_value = -1,                                                \
+    .qapi_type = &Pca9552PinState_type_info,                            \
+    .get = prop_get_pin##n,                                             \
+    .set = prop_set_pin##n,                                             \
+},
+
+static const QapiEnumProp pin_enum_props[] = {
+    QEMU_REPEAT(PCA955X_PIN_COUNT_MAX, PIN_ENUM_PROP)
+};
+
 static void pca955x_initfn(Object *obj)
 {
     PCA955xClass *k = PCA955X_GET_CLASS(obj);
 
     assert(k->pin_count <= PCA955X_PIN_COUNT_MAX);
+    /* use array/list instead of individual QAPI enum properties */
     for (int ix = 0; ix < k->pin_count; ix++) {
-        char *name;
-
         if (k->has_led_support) {
-            /* LED variant: expose the LED selector state as led%d. */
-            name = g_strdup_printf("led%d", ix);
-            object_property_add(obj, name, "Pca9552LedState",
-                                pca955x_get_led, pca955x_set_led, NULL, NULL);
+            object_property_add_qapi_enum(obj, &led_enum_props[ix]);
         } else {
-            /* GPIO variant: expose the pin logic level as pin%d. */
-            name = g_strdup_printf("pin%d", ix);
-            object_property_add(obj, name, "Pca9552PinState",
-                                pca955x_get_pin, pca955x_set_pin, NULL, NULL);
+            object_property_add_qapi_enum(obj, &pin_enum_props[ix]);
         }
-        g_free(name);
     }
 }
 
diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c
index e4eddc829ef7..f2a5b94a36d8 100644
--- a/hw/gpio/pca9554.c
+++ b/hw/gpio/pca9554.c
@@ -18,7 +18,7 @@
 #include "qapi/error.h"
 #include "qapi/qapi-types-machine.h"
 #include "qapi/qapi-visit-machine.h"
-#include "qapi/visitor.h"
+#include "qapi/qapi-type-infos-machine.h"
 #include "trace.h"
 #include "qom/object.h"
 
@@ -151,83 +151,60 @@ static void pca9554_set_ext_state(PCA9554State *s, int pin, int level)
     }
 }
 
-static void pca9554_get_pin(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA9554State *s = PCA9554(obj);
-    int pin, rc;
-    Pca9554PinState state;
-
-    rc = sscanf(name, "pin%2d", &pin);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (pin < 0 || pin >= PCA9554_GET_CLASS(s)->pin_count) {
-        error_setg(errp, "%s invalid pin %s", __func__, name);
-        return;
-    }
-
-    /*
-     * Report the physical pin level. The input register is kept in sync by
-     * pca9554_update_pin_input(): output pins mirror the OUTPUT register and
-     * input pins reflect the externally driven (or pulled-up) level, so it
-     * holds the wire level regardless of the configured direction.
-     */
-    state = (s->regs[PCA9554_INPUT] >> pin) & 0x1;
-    visit_type_Pca9554PinState(v, name, &state, errp);
+/*
+ * Report the physical pin level. The input register is kept in sync
+ * by pca9554_update_pin_input(): output pins mirror the OUTPUT
+ * register and input pins reflect the externally driven (or
+ * pulled-up) level, so it holds the wire level regardless of the
+ * configured direction.
+ */
+#define DEFINE_PIN_ACCESSORS(n)                                              \
+static int prop_get_pin##n(Object *obj, Error **errp)                        \
+{                                                                            \
+    PCA9554State *s = PCA9554(obj);                                          \
+    return (s->regs[PCA9554_INPUT] >> (n)) & 0x1;                            \
+}                                                                            \
+static void prop_set_pin##n(Object *obj, int val, Error **errp)              \
+{                                                                            \
+    PCA9554State *s = PCA9554(obj);                                          \
+    if (s->hw_dir) {                                                         \
+        if (!((s->regs[PCA9554_CONFIG] >> (n)) & 0x1)) {                     \
+            qemu_log_mask(LOG_UNIMP,                                         \
+                          "%s: pin %d configured as output,"                 \
+                          " ignoring set\n",                                 \
+                          s->description, (n));                              \
+            return;                                                          \
+        }                                                                    \
+        pca9554_set_ext_state(s, (n), val != PCA9554_PIN_STATE_LOW);         \
+    } else {                                                                 \
+        /* Legacy behavior: force output mode and drive */                   \
+        uint8_t mask = 0x1 << (n);                                           \
+        int v = pca9554_read(s, PCA9554_OUTPUT);                             \
+        if (val == PCA9554_PIN_STATE_LOW) {                                  \
+            v &= ~mask;                                                      \
+        } else {                                                             \
+            v |= mask;                                                       \
+        }                                                                    \
+        pca9554_write(s, PCA9554_OUTPUT, v);                                 \
+        v = pca9554_read(s, PCA9554_CONFIG);                                 \
+        v &= ~mask;                                                          \
+        pca9554_write(s, PCA9554_CONFIG, v);                                 \
+    }                                                                        \
 }
 
-static void pca9554_set_pin(Object *obj, Visitor *v, const char *name,
-                            void *opaque, Error **errp)
-{
-    PCA9554State *s = PCA9554(obj);
-    int pin, rc, val;
-    uint8_t mask;
-    Pca9554PinState state;
+QEMU_REPEAT(PCA9554_PIN_COUNT, DEFINE_PIN_ACCESSORS)
 
-    if (!visit_type_Pca9554PinState(v, name, &state, errp)) {
-        return;
-    }
-    rc = sscanf(name, "pin%2d", &pin);
-    if (rc != 1) {
-        error_setg(errp, "%s: error reading %s", __func__, name);
-        return;
-    }
-    if (pin < 0 || pin >= PCA9554_GET_CLASS(s)->pin_count) {
-        error_setg(errp, "%s invalid pin %s", __func__, name);
-        return;
-    }
+#define PIN_ENUM_PROP(n) {                                              \
+    .name = "pin" #n,                                                   \
+    .default_value = -1,                                                \
+    .qapi_type = &Pca9554PinState_type_info,                            \
+    .get = prop_get_pin##n,                                             \
+    .set = prop_set_pin##n,                                             \
+},
 
-    if (s->hw_dir) {
-        /* Warn and ignore if the guest has configured this pin as output */
-        if (!((s->regs[PCA9554_CONFIG] >> pin) & 0x1)) {
-            qemu_log_mask(LOG_UNIMP,
-                          "%s: pin %d is configured as output, "
-                          "ignoring external set\n",
-                          s->description, pin);
-            return;
-        }
-        /* Drive the external input level */
-        pca9554_set_ext_state(s, pin, state != PCA9554_PIN_STATE_LOW);
-    } else {
-        /* Legacy behavior: force output mode and drive */
-        /* First, modify the output register bit */
-        val = pca9554_read(s, PCA9554_OUTPUT);
-        mask = 0x1 << pin;
-        if (state == PCA9554_PIN_STATE_LOW) {
-            val &= ~(mask);
-        } else {
-            val |= mask;
-        }
-        pca9554_write(s, PCA9554_OUTPUT, val);
-
-        /* Then, clear the config register bit for output mode */
-        val = pca9554_read(s, PCA9554_CONFIG);
-        val &= ~mask;
-        pca9554_write(s, PCA9554_CONFIG, val);
-    }
-}
+static const QapiEnumProp pin_enum_props[] = {
+    QEMU_REPEAT(PCA9554_PIN_COUNT, PIN_ENUM_PROP)
+};
 
 static const VMStateDescription pca9554_vmstate = {
     .name = "PCA9554",
@@ -264,16 +241,9 @@ static void pca9554_reset(DeviceState *dev)
 static void pca9554_initfn(Object *obj)
 {
     PCA9554Class *pc = PCA9554_GET_CLASS(obj);
-    int pin;
 
-    for (pin = 0; pin < pc->pin_count; pin++) {
-        char *name;
-
-        name = g_strdup_printf("pin%d", pin);
-        object_property_add(obj, name, "Pca9554PinState",
-                            pca9554_get_pin, pca9554_set_pin,
-                            NULL, NULL);
-        g_free(name);
+    for (int pin = 0; pin < pc->pin_count; pin++) {
+        object_property_add_qapi_enum(obj, &pin_enum_props[pin]);
     }
 }
 

-- 
2.55.0.543.g5ebe2ebe4ea8
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.