Reviewed-by: Glenn Miles <[email protected]>

Thanks,

Glenn

On Tue, 2026-10-20 at 14:46 +0400, Marc-André Lureau wrote:
> Replace hand-rolled string lookup tables with QAPI enums
> Pca9552LedState and Pca955{2,4}PinState for the led%d and pin%d QOM
> properties. This fixes the incorrect property type ("bool" for LEDs,
> "str" for pins) and lets QAPI handle string-to-enum conversion in the
> visitor, removing the manual string matching in the setters.
> 
> Add QEMU_BUILD_BUG_ON guards to ensure the QAPI-generated enum values
> stay in sync with the hardware register encoding.
> 
> I kept Pca9552PinState & @Pca9554PinState separate types, because I
> don't know if they could diverge. We could eventually merge those.
> 
> Signed-off-by: Markus Armbruster <[email protected]>
> Signed-off-by: Marc-André Lureau <[email protected]>
> ---
>  hw/gpio/pca9552.c | 82 
> +++++++++++++++++++++----------------------------------
>  hw/gpio/pca9554.c | 39 ++++++++++----------------
>  qapi/machine.json | 46 +++++++++++++++++++++++++++++++
>  3 files changed, 92 insertions(+), 75 deletions(-)
> 
> diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c
> index 719149b7174b..16741c22dea3 100644
> --- a/hw/gpio/pca9552.c
> +++ b/hw/gpio/pca9552.c
> @@ -23,7 +23,8 @@
>  #include "hw/core/irq.h"
>  #include "migration/vmstate.h"
>  #include "qapi/error.h"
> -#include "qapi/visitor.h"
> +#include "qapi/qapi-types-machine.h"
> +#include "qapi/qapi-visit-machine.h"
>  #include "trace.h"
>  #include "qom/object.h"
>  
> @@ -59,16 +60,15 @@ struct PCA955xClass {
>  /*
>   * Note:  The LED_ON and LED_OFF configuration values for the PCA955X
>   *        chips are the reverse of the PCA953X family of chips.
> + *
> + * The QAPI enums must match the hardware register values.
>   */
> -#define PCA9552_LED_ON   0x0
> -#define PCA9552_LED_OFF  0x1
> -#define PCA9552_LED_PWM0 0x2
> -#define PCA9552_LED_PWM1 0x3
> -#define PCA9552_PIN_LOW  0x0
> -#define PCA9552_PIN_HIZ  0x1
> -
> -static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
> -static const char *pin_state[] = {"low", "high"};
> +QEMU_BUILD_BUG_ON(PCA9552_LED_STATE_ON != 0x0);
> +QEMU_BUILD_BUG_ON(PCA9552_LED_STATE_OFF != 0x1);
> +QEMU_BUILD_BUG_ON(PCA9552_LED_STATE_PWM0 != 0x2);
> +QEMU_BUILD_BUG_ON(PCA9552_LED_STATE_PWM1 != 0x3);
> +QEMU_BUILD_BUG_ON(PCA9552_PIN_STATE_LOW != 0x0);
> +QEMU_BUILD_BUG_ON(PCA9552_PIN_STATE_HIGH != 0x1);
>  
>  static uint8_t pca955x_pin_get_config(PCA955xState *s, int pin)
>  {
> @@ -142,24 +142,24 @@ static void pca955x_update_pin_input(PCA955xState *s)
>              uint8_t config = pca955x_pin_get_config(s, i);
>  
>              switch (config) {
> -            case PCA9552_LED_ON:
> +            case PCA9552_LED_STATE_ON:
>                  /* Pin is set to 0V to turn on LED */
>                  s->regs[input_reg] &= ~bit_mask;
>                  break;
> -            case PCA9552_LED_OFF:
> +            case PCA9552_LED_STATE_OFF:
>                  /*
>                   * Pin is set to Hi-Z to turn off LED and
>                   * pullup sets it to a logical 1 unless
>                   * external device drives it low.
>                   */
> -                if (s->ext_state[i] == PCA9552_PIN_LOW) {
> +                if (s->ext_state[i] == PCA9552_PIN_STATE_LOW) {
>                      s->regs[input_reg] &= ~bit_mask;
>                  } else {
>                      s->regs[input_reg] |=  bit_mask;
>                  }
>                  break;
> -            case PCA9552_LED_PWM0:
> -            case PCA9552_LED_PWM1:
> +            case PCA9552_LED_STATE_PWM0:
> +            case PCA9552_LED_STATE_PWM1:
>                  /* TODO */
>              default:
>                  break;
> @@ -176,7 +176,7 @@ static void pca955x_update_pin_input(PCA955xState *s)
>               */
>              if (s->regs[config_reg] & bit_mask) {
>                  /* Input mode - reflect external state */
> -                if (s->ext_state[i] == PCA9552_PIN_LOW) {
> +                if (s->ext_state[i] == PCA9552_PIN_STATE_LOW) {
>                      s->regs[input_reg] &= ~bit_mask;
>                  } else {
>                      s->regs[input_reg] |= bit_mask;
> @@ -360,7 +360,7 @@ static void pca955x_get_led(Object *obj, Visitor *v, 
> const char *name,
>      PCA955xClass *k = PCA955X_GET_CLASS(obj);
>      PCA955xState *s = PCA955X(obj);
>      int led, rc, reg;
> -    uint8_t state;
> +    Pca9552LedState state;
>  
>      rc = sscanf(name, "led%2d", &led);
>      if (rc != 1) {
> @@ -378,7 +378,7 @@ static void pca955x_get_led(Object *obj, Visitor *v, 
> const char *name,
>       */
>      reg = PCA9552_LS0 + led / 4;
>      state = (pca955x_read(s, reg) >> ((led % 4) * 2)) & 0x3;
> -    visit_type_str(v, name, (char **)&led_state[state], errp);
> +    visit_type_Pca9552LedState(v, name, &state, errp);
>  }
>  
>  /*
> @@ -397,10 +397,9 @@ static void pca955x_set_led(Object *obj, Visitor *v, 
> const char *name,
>      PCA955xClass *k = PCA955X_GET_CLASS(obj);
>      PCA955xState *s = PCA955X(obj);
>      int led, rc, reg, val;
> -    uint8_t state;
> -    g_autofree char *state_str = NULL;
> +    Pca9552LedState state;
>  
> -    if (!visit_type_str(v, name, &state_str, errp)) {
> +    if (!visit_type_Pca9552LedState(v, name, &state, errp)) {
>          return;
>      }
>      rc = sscanf(name, "led%2d", &led);
> @@ -413,16 +412,6 @@ static void pca955x_set_led(Object *obj, Visitor *v, 
> const char *name,
>          return;
>      }
>  
> -    for (state = 0; state < ARRAY_SIZE(led_state); state++) {
> -        if (!strcmp(state_str, led_state[state])) {
> -            break;
> -        }
> -    }
> -    if (state >= ARRAY_SIZE(led_state)) {
> -        error_setg(errp, "%s invalid led state %s", __func__, state_str);
> -        return;
> -    }
> -
>      reg = PCA9552_LS0 + led / 4;
>      val = pca955x_read(s, reg);
>      val = pca955x_ledsel(val, led % 4, state);
> @@ -437,7 +426,8 @@ static void pca955x_get_pin(Object *obj, Visitor *v, 
> const char *name,
>      PCA955xClass *k = PCA955X_GET_CLASS(obj);
>      PCA955xState *s = PCA955X(obj);
>      int pin, rc;
> -    uint8_t input_reg, state;
> +    uint8_t input_reg;
> +    Pca9552PinState state;
>  
>      rc = sscanf(name, "pin%2d", &pin);
>      if (rc != 1) {
> @@ -455,7 +445,7 @@ static void pca955x_get_pin(Object *obj, Visitor *v, 
> const char *name,
>       */
>      input_reg = PCA9535_INPUT0 + (pin / 8);
>      state = (s->regs[input_reg] >> (pin % 8)) & 0x1;
> -    visit_type_str(v, name, (char **)&pin_state[state], errp);
> +    visit_type_Pca9552PinState(v, name, &state, errp);
>  }
>  
>  static void pca955x_set_pin(Object *obj, Visitor *v, const char *name,
> @@ -464,10 +454,10 @@ static void pca955x_set_pin(Object *obj, Visitor *v, 
> const char *name,
>      PCA955xClass *k = PCA955X_GET_CLASS(obj);
>      PCA955xState *s = PCA955X(obj);
>      int pin, rc;
> -    uint8_t state, config_reg;
> -    g_autofree char *state_str = NULL;
> +    Pca9552PinState state;
> +    uint8_t config_reg;
>  
> -    if (!visit_type_str(v, name, &state_str, errp)) {
> +    if (!visit_type_Pca9552PinState(v, name, &state, errp)) {
>          return;
>      }
>      rc = sscanf(name, "pin%2d", &pin);
> @@ -480,16 +470,6 @@ static void pca955x_set_pin(Object *obj, Visitor *v, 
> const char *name,
>          return;
>      }
>  
> -    for (state = 0; state < ARRAY_SIZE(pin_state); state++) {
> -        if (!strcmp(state_str, pin_state[state])) {
> -            break;
> -        }
> -    }
> -    if (state >= ARRAY_SIZE(pin_state)) {
> -        error_setg(errp, "%s invalid pin state %s", __func__, state_str);
> -        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)) {
> @@ -499,7 +479,7 @@ static void pca955x_set_pin(Object *obj, Visitor *v, 
> const char *name,
>          return;
>      }
>  
> -    pca955x_set_ext_state(s, pin, state != PCA9552_PIN_LOW);
> +    pca955x_set_ext_state(s, pin, state != PCA9552_PIN_STATE_LOW);
>  }
>  
>  static const VMStateDescription pca9552_vmstate = {
> @@ -529,7 +509,7 @@ static void pca9552_reset_hold(Object *obj, ResetType 
> type)
>      s->regs[PCA9552_LS2] = 0x55;
>      s->regs[PCA9552_LS3] = 0x55;
>  
> -    memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
> +    memset(s->ext_state, PCA9552_PIN_STATE_HIGH, PCA955X_PIN_COUNT_MAX);
>      pca955x_update_pin_input(s);
>  
>      s->pointer = 0xFF;
> @@ -549,7 +529,7 @@ static void pca9535_reset_hold(Object *obj, ResetType 
> type)
>      s->regs[PCA9535_CONFIG0] = 0xFF;  /* All pins as inputs */
>      s->regs[PCA9535_CONFIG1] = 0xFF;  /* All pins as inputs */
>  
> -    memset(s->ext_state, PCA9552_PIN_HIZ, PCA955X_PIN_COUNT_MAX);
> +    memset(s->ext_state, PCA9552_PIN_STATE_HIGH, PCA955X_PIN_COUNT_MAX);
>      pca955x_update_pin_input(s);
>  
>      s->pointer = 0xFF;
> @@ -567,12 +547,12 @@ static void pca955x_initfn(Object *obj)
>          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, "bool",
> +            object_property_add(obj, name, "Pca9552LedState",
>                                  pca955x_get_led, pca955x_set_led, NULL, 
> NULL);
>          } else {
>              /* GPIO variant: expose the pin logic level as pin%d. */
>              name = g_strdup_printf("pin%d", ix);
> -            object_property_add(obj, name, "str",
> +            object_property_add(obj, name, "Pca9552PinState",
>                                  pca955x_get_pin, pca955x_set_pin, NULL, 
> NULL);
>          }
>          g_free(name);
> diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c
> index 904698cdce85..e4eddc829ef7 100644
> --- a/hw/gpio/pca9554.c
> +++ b/hw/gpio/pca9554.c
> @@ -16,6 +16,8 @@
>  #include "hw/core/irq.h"
>  #include "migration/vmstate.h"
>  #include "qapi/error.h"
> +#include "qapi/qapi-types-machine.h"
> +#include "qapi/qapi-visit-machine.h"
>  #include "qapi/visitor.h"
>  #include "trace.h"
>  #include "qom/object.h"
> @@ -32,10 +34,8 @@ typedef struct PCA9554Class PCA9554Class;
>  DECLARE_CLASS_CHECKERS(PCA9554Class, PCA9554,
>                         TYPE_PCA9554)
>  
> -#define PCA9554_PIN_LOW  0x0
> -#define PCA9554_PIN_HIZ  0x1
> -
> -static const char *pin_state[] = {"low", "high"};
> +QEMU_BUILD_BUG_ON(PCA9554_PIN_STATE_LOW != 0x0);
> +QEMU_BUILD_BUG_ON(PCA9554_PIN_STATE_HIGH != 0x1);
>  
>  static void pca9554_update_pin_input(PCA9554State *s)
>  {
> @@ -54,7 +54,7 @@ static void pca9554_update_pin_input(PCA9554State *s)
>               * Input: the pin is Hi-Z with a pull-up, so it reads high
>               * unless an external device drives it low.
>               */
> -            if (s->ext_state[i] == PCA9554_PIN_LOW) {
> +            if (s->ext_state[i] == PCA9554_PIN_STATE_LOW) {
>                  s->regs[PCA9554_INPUT] &= ~bit_mask;
>              } else {
>                  s->regs[PCA9554_INPUT] |= bit_mask;
> @@ -156,7 +156,7 @@ static void pca9554_get_pin(Object *obj, Visitor *v, 
> const char *name,
>  {
>      PCA9554State *s = PCA9554(obj);
>      int pin, rc;
> -    uint8_t state;
> +    Pca9554PinState state;
>  
>      rc = sscanf(name, "pin%2d", &pin);
>      if (rc != 1) {
> @@ -175,7 +175,7 @@ static void pca9554_get_pin(Object *obj, Visitor *v, 
> const char *name,
>       * holds the wire level regardless of the configured direction.
>       */
>      state = (s->regs[PCA9554_INPUT] >> pin) & 0x1;
> -    visit_type_str(v, name, (char **)&pin_state[state], errp);
> +    visit_type_Pca9554PinState(v, name, &state, errp);
>  }
>  
>  static void pca9554_set_pin(Object *obj, Visitor *v, const char *name,
> @@ -183,10 +183,10 @@ static void pca9554_set_pin(Object *obj, Visitor *v, 
> const char *name,
>  {
>      PCA9554State *s = PCA9554(obj);
>      int pin, rc, val;
> -    uint8_t state, mask;
> -    g_autofree char *state_str = NULL;
> +    uint8_t mask;
> +    Pca9554PinState state;
>  
> -    if (!visit_type_str(v, name, &state_str, errp)) {
> +    if (!visit_type_Pca9554PinState(v, name, &state, errp)) {
>          return;
>      }
>      rc = sscanf(name, "pin%2d", &pin);
> @@ -199,16 +199,6 @@ static void pca9554_set_pin(Object *obj, Visitor *v, 
> const char *name,
>          return;
>      }
>  
> -    for (state = 0; state < ARRAY_SIZE(pin_state); state++) {
> -        if (!strcmp(state_str, pin_state[state])) {
> -            break;
> -        }
> -    }
> -    if (state >= ARRAY_SIZE(pin_state)) {
> -        error_setg(errp, "%s invalid pin state %s", __func__, state_str);
> -        return;
> -    }
> -
>      if (s->hw_dir) {
>          /* Warn and ignore if the guest has configured this pin as output */
>          if (!((s->regs[PCA9554_CONFIG] >> pin) & 0x1)) {
> @@ -219,13 +209,13 @@ static void pca9554_set_pin(Object *obj, Visitor *v, 
> const char *name,
>              return;
>          }
>          /* Drive the external input level */
> -        pca9554_set_ext_state(s, pin, state != PCA9554_PIN_LOW);
> +        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_LOW) {
> +        if (state == PCA9554_PIN_STATE_LOW) {
>              val &= ~(mask);
>          } else {
>              val |= mask;
> @@ -264,7 +254,7 @@ static void pca9554_reset(DeviceState *dev)
>      s->regs[PCA9554_POLARITY] = 0x0; /* No pins are inverted */
>      s->regs[PCA9554_CONFIG] = pin_mask; /* All pins are inputs */
>  
> -    memset(s->ext_state, PCA9554_PIN_HIZ, pc->pin_count);
> +    memset(s->ext_state, PCA9554_PIN_STATE_HIGH, pc->pin_count);
>      pca9554_update_pin_input(s);
>  
>      s->pointer = 0x0;
> @@ -280,7 +270,8 @@ static void pca9554_initfn(Object *obj)
>          char *name;
>  
>          name = g_strdup_printf("pin%d", pin);
> -        object_property_add(obj, name, "str", pca9554_get_pin, 
> pca9554_set_pin,
> +        object_property_add(obj, name, "Pca9554PinState",
> +                            pca9554_get_pin, pca9554_set_pin,
>                              NULL, NULL);
>          g_free(name);
>      }
> diff --git a/qapi/machine.json b/qapi/machine.json
> index 34be3156270c..46b6935ab0b8 100644
> --- a/qapi/machine.json
> +++ b/qapi/machine.json
> @@ -416,6 +416,52 @@
>  { 'enum': 'LostTickPolicy',
>    'data': ['discard', 'delay', 'slew' ] }
>  
> +##
> +# @Pca9552LedState:
> +#
> +# LED state for PCA9552.
> +#
> +# @on: LED on
> +#
> +# @off: LED off
> +#
> +# @pwm0: LED controlled by PWM0
> +#
> +# @pwm1: LED controlled by PWM1
> +#
> +# Since: 11.2
> +##
> +{ 'enum': 'Pca9552LedState',
> +  'data': ['on', 'off', 'pwm0', 'pwm1'] }
> +
> +##
> +# @Pca9552PinState:
> +#
> +# Pin state for PCA9552.
> +#
> +# @low: Pin low
> +#
> +# @high: Pin high
> +#
> +# Since: 11.2
> +##
> +{ 'enum': 'Pca9552PinState',
> +  'data': ['low', 'high'] }
> +
> +##
> +# @Pca9554PinState:
> +#
> +# Pin state for PCA9554.
> +#
> +# @low: Pin low
> +#
> +# @high: Pin high
> +#
> +# Since: 11.2
> +##
> +{ 'enum': 'Pca9554PinState',
> +  'data': ['low', 'high'] }
> +
>  ##
>  # @inject-nmi:
>  #     Injects a Non-Maskable Interrupt (machine specific: for example
> 


Reply via email to