 6f83dc6716
			
		
	
	
		6f83dc6716
		
	
	
	
	
		
			
			Testing of the LED state showed that when the LED polarity was
set to GPIO_POLARITY_ACTIVE_LOW and a low logic value was set on
the input GPIO of the LED, the LED was being turn off when it was
expected to be turned on.
Fixes: ddb67f6402 ("hw/misc/led: Allow connecting from GPIO output")
Signed-off-by: Glenn Miles <milesg@linux.vnet.ibm.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Message-id: 20231024191945.4135036-1-milesg@linux.vnet.ibm.com
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
		
	
			
		
			
				
	
	
		
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU single LED device
 | |
|  *
 | |
|  * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: GPL-2.0-or-later
 | |
|  */
 | |
| #include "qemu/osdep.h"
 | |
| #include "qapi/error.h"
 | |
| #include "migration/vmstate.h"
 | |
| #include "hw/qdev-properties.h"
 | |
| #include "hw/misc/led.h"
 | |
| #include "trace.h"
 | |
| 
 | |
| #define LED_INTENSITY_PERCENT_MAX   100
 | |
| 
 | |
| static const char * const led_color_name[] = {
 | |
|     [LED_COLOR_VIOLET]  = "violet",
 | |
|     [LED_COLOR_BLUE]    = "blue",
 | |
|     [LED_COLOR_CYAN]    = "cyan",
 | |
|     [LED_COLOR_GREEN]   = "green",
 | |
|     [LED_COLOR_YELLOW]  = "yellow",
 | |
|     [LED_COLOR_AMBER]   = "amber",
 | |
|     [LED_COLOR_ORANGE]  = "orange",
 | |
|     [LED_COLOR_RED]     = "red",
 | |
| };
 | |
| 
 | |
| static bool led_color_name_is_valid(const char *color_name)
 | |
| {
 | |
|     for (size_t i = 0; i < ARRAY_SIZE(led_color_name); i++) {
 | |
|         if (strcmp(color_name, led_color_name[i]) == 0) {
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| void led_set_intensity(LEDState *s, unsigned intensity_percent)
 | |
| {
 | |
|     if (intensity_percent > LED_INTENSITY_PERCENT_MAX) {
 | |
|         intensity_percent = LED_INTENSITY_PERCENT_MAX;
 | |
|     }
 | |
|     trace_led_set_intensity(s->description, s->color, intensity_percent);
 | |
|     if (intensity_percent != s->intensity_percent) {
 | |
|         trace_led_change_intensity(s->description, s->color,
 | |
|                                    s->intensity_percent, intensity_percent);
 | |
|     }
 | |
|     s->intensity_percent = intensity_percent;
 | |
| }
 | |
| 
 | |
| unsigned led_get_intensity(LEDState *s)
 | |
| {
 | |
|     return s->intensity_percent;
 | |
| }
 | |
| 
 | |
| void led_set_state(LEDState *s, bool is_emitting)
 | |
| {
 | |
|     led_set_intensity(s, is_emitting ? LED_INTENSITY_PERCENT_MAX : 0);
 | |
| }
 | |
| 
 | |
| static void led_set_state_gpio_handler(void *opaque, int line, int new_state)
 | |
| {
 | |
|     LEDState *s = LED(opaque);
 | |
| 
 | |
|     assert(line == 0);
 | |
|     led_set_state(s, !!new_state == s->gpio_active_high);
 | |
| }
 | |
| 
 | |
| static void led_reset(DeviceState *dev)
 | |
| {
 | |
|     LEDState *s = LED(dev);
 | |
| 
 | |
|     led_set_state(s, s->gpio_active_high);
 | |
| }
 | |
| 
 | |
| static const VMStateDescription vmstate_led = {
 | |
|     .name = TYPE_LED,
 | |
|     .version_id = 1,
 | |
|     .minimum_version_id = 1,
 | |
|     .fields = (VMStateField[]) {
 | |
|         VMSTATE_UINT8(intensity_percent, LEDState),
 | |
|         VMSTATE_END_OF_LIST()
 | |
|     }
 | |
| };
 | |
| 
 | |
| static void led_realize(DeviceState *dev, Error **errp)
 | |
| {
 | |
|     LEDState *s = LED(dev);
 | |
| 
 | |
|     if (s->color == NULL) {
 | |
|         error_setg(errp, "property 'color' not specified");
 | |
|         return;
 | |
|     } else if (!led_color_name_is_valid(s->color)) {
 | |
|         error_setg(errp, "property 'color' invalid or not supported");
 | |
|         return;
 | |
|     }
 | |
|     if (s->description == NULL) {
 | |
|         s->description = g_strdup("n/a");
 | |
|     }
 | |
| 
 | |
|     qdev_init_gpio_in(DEVICE(s), led_set_state_gpio_handler, 1);
 | |
| }
 | |
| 
 | |
| static Property led_properties[] = {
 | |
|     DEFINE_PROP_STRING("color", LEDState, color),
 | |
|     DEFINE_PROP_STRING("description", LEDState, description),
 | |
|     DEFINE_PROP_BOOL("gpio-active-high", LEDState, gpio_active_high, true),
 | |
|     DEFINE_PROP_END_OF_LIST(),
 | |
| };
 | |
| 
 | |
| static void led_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
| 
 | |
|     dc->desc = "LED";
 | |
|     dc->vmsd = &vmstate_led;
 | |
|     dc->reset = led_reset;
 | |
|     dc->realize = led_realize;
 | |
|     set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
 | |
|     device_class_set_props(dc, led_properties);
 | |
| }
 | |
| 
 | |
| static const TypeInfo led_info = {
 | |
|     .name = TYPE_LED,
 | |
|     .parent = TYPE_DEVICE,
 | |
|     .instance_size = sizeof(LEDState),
 | |
|     .class_init = led_class_init
 | |
| };
 | |
| 
 | |
| static void led_register_types(void)
 | |
| {
 | |
|     type_register_static(&led_info);
 | |
| }
 | |
| 
 | |
| type_init(led_register_types)
 | |
| 
 | |
| LEDState *led_create_simple(Object *parentobj,
 | |
|                             GpioPolarity gpio_polarity,
 | |
|                             LEDColor color,
 | |
|                             const char *description)
 | |
| {
 | |
|     g_autofree char *name = NULL;
 | |
|     DeviceState *dev;
 | |
| 
 | |
|     dev = qdev_new(TYPE_LED);
 | |
|     qdev_prop_set_bit(dev, "gpio-active-high",
 | |
|                       gpio_polarity == GPIO_POLARITY_ACTIVE_HIGH);
 | |
|     qdev_prop_set_string(dev, "color", led_color_name[color]);
 | |
|     if (!description) {
 | |
|         static unsigned undescribed_led_id;
 | |
|         name = g_strdup_printf("undescribed-led-#%u", undescribed_led_id++);
 | |
|     } else {
 | |
|         qdev_prop_set_string(dev, "description", description);
 | |
|         name = g_ascii_strdown(description, -1);
 | |
|         name = g_strdelimit(name, " #", '-');
 | |
|     }
 | |
|     object_property_add_child(parentobj, name, OBJECT(dev));
 | |
|     qdev_realize_and_unref(dev, NULL, &error_fatal);
 | |
| 
 | |
|     return LED(dev);
 | |
| }
 |