]>
Commit | Line | Data |
---|---|---|
c1b29826 PMD |
1 | /* |
2 | * QEMU single LED device | |
3 | * | |
4 | * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org> | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0-or-later | |
7 | */ | |
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" | |
13 | #include "trace.h" | |
14 | ||
15 | #define LED_INTENSITY_PERCENT_MAX 100 | |
16 | ||
17 | static const char * const led_color_name[] = { | |
18 | [LED_COLOR_VIOLET] = "violet", | |
19 | [LED_COLOR_BLUE] = "blue", | |
20 | [LED_COLOR_CYAN] = "cyan", | |
21 | [LED_COLOR_GREEN] = "green", | |
956eb204 | 22 | [LED_COLOR_YELLOW] = "yellow", |
c1b29826 PMD |
23 | [LED_COLOR_AMBER] = "amber", |
24 | [LED_COLOR_ORANGE] = "orange", | |
25 | [LED_COLOR_RED] = "red", | |
26 | }; | |
27 | ||
28 | static bool led_color_name_is_valid(const char *color_name) | |
29 | { | |
30 | for (size_t i = 0; i < ARRAY_SIZE(led_color_name); i++) { | |
31 | if (strcmp(color_name, led_color_name[i]) == 0) { | |
32 | return true; | |
33 | } | |
34 | } | |
35 | return false; | |
36 | } | |
37 | ||
38 | void led_set_intensity(LEDState *s, unsigned intensity_percent) | |
39 | { | |
40 | if (intensity_percent > LED_INTENSITY_PERCENT_MAX) { | |
41 | intensity_percent = LED_INTENSITY_PERCENT_MAX; | |
42 | } | |
43 | trace_led_set_intensity(s->description, s->color, intensity_percent); | |
4aef4399 PMD |
44 | if (intensity_percent != s->intensity_percent) { |
45 | trace_led_change_intensity(s->description, s->color, | |
46 | s->intensity_percent, intensity_percent); | |
47 | } | |
c1b29826 PMD |
48 | s->intensity_percent = intensity_percent; |
49 | } | |
50 | ||
51 | unsigned led_get_intensity(LEDState *s) | |
52 | { | |
53 | return s->intensity_percent; | |
54 | } | |
55 | ||
56 | void led_set_state(LEDState *s, bool is_emitting) | |
57 | { | |
58 | led_set_intensity(s, is_emitting ? LED_INTENSITY_PERCENT_MAX : 0); | |
59 | } | |
60 | ||
ddb67f64 PMD |
61 | static void led_set_state_gpio_handler(void *opaque, int line, int new_state) |
62 | { | |
63 | LEDState *s = LED(opaque); | |
64 | ||
65 | assert(line == 0); | |
66 | led_set_state(s, !!new_state != s->gpio_active_high); | |
67 | } | |
68 | ||
c1b29826 PMD |
69 | static void led_reset(DeviceState *dev) |
70 | { | |
71 | LEDState *s = LED(dev); | |
72 | ||
ddb67f64 | 73 | led_set_state(s, s->gpio_active_high); |
c1b29826 PMD |
74 | } |
75 | ||
76 | static const VMStateDescription vmstate_led = { | |
77 | .name = TYPE_LED, | |
78 | .version_id = 1, | |
79 | .minimum_version_id = 1, | |
80 | .fields = (VMStateField[]) { | |
81 | VMSTATE_UINT8(intensity_percent, LEDState), | |
82 | VMSTATE_END_OF_LIST() | |
83 | } | |
84 | }; | |
85 | ||
86 | static void led_realize(DeviceState *dev, Error **errp) | |
87 | { | |
88 | LEDState *s = LED(dev); | |
89 | ||
90 | if (s->color == NULL) { | |
91 | error_setg(errp, "property 'color' not specified"); | |
92 | return; | |
93 | } else if (!led_color_name_is_valid(s->color)) { | |
94 | error_setg(errp, "property 'color' invalid or not supported"); | |
95 | return; | |
96 | } | |
97 | if (s->description == NULL) { | |
98 | s->description = g_strdup("n/a"); | |
99 | } | |
ddb67f64 PMD |
100 | |
101 | qdev_init_gpio_in(DEVICE(s), led_set_state_gpio_handler, 1); | |
c1b29826 PMD |
102 | } |
103 | ||
104 | static Property led_properties[] = { | |
105 | DEFINE_PROP_STRING("color", LEDState, color), | |
106 | DEFINE_PROP_STRING("description", LEDState, description), | |
ddb67f64 | 107 | DEFINE_PROP_BOOL("gpio-active-high", LEDState, gpio_active_high, true), |
c1b29826 PMD |
108 | DEFINE_PROP_END_OF_LIST(), |
109 | }; | |
110 | ||
111 | static void led_class_init(ObjectClass *klass, void *data) | |
112 | { | |
113 | DeviceClass *dc = DEVICE_CLASS(klass); | |
114 | ||
115 | dc->desc = "LED"; | |
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); | |
121 | } | |
122 | ||
123 | static const TypeInfo led_info = { | |
124 | .name = TYPE_LED, | |
125 | .parent = TYPE_DEVICE, | |
126 | .instance_size = sizeof(LEDState), | |
127 | .class_init = led_class_init | |
128 | }; | |
129 | ||
130 | static void led_register_types(void) | |
131 | { | |
132 | type_register_static(&led_info); | |
133 | } | |
134 | ||
135 | type_init(led_register_types) | |
136 | ||
137 | LEDState *led_create_simple(Object *parentobj, | |
ddb67f64 | 138 | GpioPolarity gpio_polarity, |
c1b29826 PMD |
139 | LEDColor color, |
140 | const char *description) | |
141 | { | |
142 | g_autofree char *name = NULL; | |
143 | DeviceState *dev; | |
144 | ||
145 | dev = qdev_new(TYPE_LED); | |
ddb67f64 PMD |
146 | qdev_prop_set_bit(dev, "gpio-active-high", |
147 | gpio_polarity == GPIO_POLARITY_ACTIVE_HIGH); | |
c1b29826 PMD |
148 | qdev_prop_set_string(dev, "color", led_color_name[color]); |
149 | if (!description) { | |
150 | static unsigned undescribed_led_id; | |
151 | name = g_strdup_printf("undescribed-led-#%u", undescribed_led_id++); | |
152 | } else { | |
153 | qdev_prop_set_string(dev, "description", description); | |
154 | name = g_ascii_strdown(description, -1); | |
155 | name = g_strdelimit(name, " #", '-'); | |
156 | } | |
157 | object_property_add_child(parentobj, name, OBJECT(dev)); | |
158 | qdev_realize_and_unref(dev, NULL, &error_fatal); | |
159 | ||
160 | return LED(dev); | |
161 | } |