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", 22 [LED_COLOR_YELLOW] = "yellow", 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); 44 if (intensity_percent != s->intensity_percent) { 45 trace_led_change_intensity(s->description, s->color, 46 s->intensity_percent, intensity_percent); 47 } 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 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 69 static void led_reset(DeviceState *dev) 70 { 71 LEDState *s = LED(dev); 72 73 led_set_state(s, s->gpio_active_high); 74 } 75 76 static const VMStateDescription vmstate_led = { 77 .name = TYPE_LED, 78 .version_id = 1, 79 .minimum_version_id = 1, 80 .fields = (const 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 } 100 101 qdev_init_gpio_in(DEVICE(s), led_set_state_gpio_handler, 1); 102 } 103 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(), 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 device_class_set_legacy_reset(dc, 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, 138 GpioPolarity gpio_polarity, 139 LEDColor color, 140 const char *description) 141 { 142 g_autofree char *name = NULL; 143 DeviceState *dev; 144 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]); 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 } 162