2 * QEMU single LED device
4 * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org>
6 * SPDX-License-Identifier: GPL-2.0-or-later
8 #include "qemu/osdep.h"
9 #include "qapi/error.h"
10 #include "migration/vmstate.h"
11 #include "hw/qdev-properties.h"
12 #include "hw/misc/led.h"
16 #define LED_INTENSITY_PERCENT_MAX 100
18 static const char * const led_color_name
[] = {
19 [LED_COLOR_VIOLET
] = "violet",
20 [LED_COLOR_BLUE
] = "blue",
21 [LED_COLOR_CYAN
] = "cyan",
22 [LED_COLOR_GREEN
] = "green",
23 [LED_COLOR_AMBER
] = "amber",
24 [LED_COLOR_ORANGE
] = "orange",
25 [LED_COLOR_RED
] = "red",
28 static bool led_color_name_is_valid(const char *color_name
)
30 for (size_t i
= 0; i
< ARRAY_SIZE(led_color_name
); i
++) {
31 if (strcmp(color_name
, led_color_name
[i
]) == 0) {
38 void led_set_intensity(LEDState
*s
, unsigned intensity_percent
)
40 if (intensity_percent
> LED_INTENSITY_PERCENT_MAX
) {
41 intensity_percent
= LED_INTENSITY_PERCENT_MAX
;
43 trace_led_set_intensity(s
->description
, s
->color
, intensity_percent
);
44 if (intensity_percent
!= s
->intensity_percent
) {
45 trace_led_change_intensity(s
->description
, s
->color
,
46 s
->intensity_percent
, intensity_percent
);
48 s
->intensity_percent
= intensity_percent
;
51 unsigned led_get_intensity(LEDState
*s
)
53 return s
->intensity_percent
;
56 void led_set_state(LEDState
*s
, bool is_emitting
)
58 led_set_intensity(s
, is_emitting
? LED_INTENSITY_PERCENT_MAX
: 0);
61 static void led_set_state_gpio_handler(void *opaque
, int line
, int new_state
)
63 LEDState
*s
= LED(opaque
);
66 led_set_state(s
, !!new_state
!= s
->gpio_active_high
);
69 static void led_reset(DeviceState
*dev
)
71 LEDState
*s
= LED(dev
);
73 led_set_state(s
, s
->gpio_active_high
);
76 static const VMStateDescription vmstate_led
= {
79 .minimum_version_id
= 1,
80 .fields
= (VMStateField
[]) {
81 VMSTATE_UINT8(intensity_percent
, LEDState
),
86 static void led_realize(DeviceState
*dev
, Error
**errp
)
88 LEDState
*s
= LED(dev
);
90 if (s
->color
== NULL
) {
91 error_setg(errp
, "property 'color' not specified");
93 } else if (!led_color_name_is_valid(s
->color
)) {
94 error_setg(errp
, "property 'color' invalid or not supported");
97 if (s
->description
== NULL
) {
98 s
->description
= g_strdup("n/a");
101 qdev_init_gpio_in(DEVICE(s
), led_set_state_gpio_handler
, 1);
104 static Property led_properties
[] = {
105 DEFINE_PROP_STRING("color", LEDState
, color
),
106 DEFINE_PROP_STRING("description", LEDState
, description
),
107 DEFINE_PROP_BOOL("gpio-active-high", LEDState
, gpio_active_high
, true),
108 DEFINE_PROP_END_OF_LIST(),
111 static void led_class_init(ObjectClass
*klass
, void *data
)
113 DeviceClass
*dc
= DEVICE_CLASS(klass
);
116 dc
->vmsd
= &vmstate_led
;
117 dc
->reset
= led_reset
;
118 dc
->realize
= led_realize
;
119 set_bit(DEVICE_CATEGORY_DISPLAY
, dc
->categories
);
120 device_class_set_props(dc
, led_properties
);
123 static const TypeInfo led_info
= {
125 .parent
= TYPE_DEVICE
,
126 .instance_size
= sizeof(LEDState
),
127 .class_init
= led_class_init
130 static void led_register_types(void)
132 type_register_static(&led_info
);
135 type_init(led_register_types
)
137 LEDState
*led_create_simple(Object
*parentobj
,
138 GpioPolarity gpio_polarity
,
140 const char *description
)
142 g_autofree
char *name
= NULL
;
145 dev
= qdev_new(TYPE_LED
);
146 qdev_prop_set_bit(dev
, "gpio-active-high",
147 gpio_polarity
== GPIO_POLARITY_ACTIVE_HIGH
);
148 qdev_prop_set_string(dev
, "color", led_color_name
[color
]);
150 static unsigned undescribed_led_id
;
151 name
= g_strdup_printf("undescribed-led-#%u", undescribed_led_id
++);
153 qdev_prop_set_string(dev
, "description", description
);
154 name
= g_ascii_strdown(description
, -1);
155 name
= g_strdelimit(name
, " #", '-');
157 object_property_add_child(parentobj
, name
, OBJECT(dev
));
158 qdev_realize_and_unref(dev
, NULL
, &error_fatal
);