xref: /qemu/hw/misc/led.c (revision 51209c2aed343a45f79b522706c807abbdcf01a3)
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 const 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  };
109  
110  static void led_class_init(ObjectClass *klass, const void *data)
111  {
112      DeviceClass *dc = DEVICE_CLASS(klass);
113  
114      dc->desc = "LED";
115      dc->vmsd = &vmstate_led;
116      device_class_set_legacy_reset(dc, led_reset);
117      dc->realize = led_realize;
118      set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
119      device_class_set_props(dc, led_properties);
120  }
121  
122  static const TypeInfo led_info = {
123      .name = TYPE_LED,
124      .parent = TYPE_DEVICE,
125      .instance_size = sizeof(LEDState),
126      .class_init = led_class_init
127  };
128  
129  static void led_register_types(void)
130  {
131      type_register_static(&led_info);
132  }
133  
134  type_init(led_register_types)
135  
136  LEDState *led_create_simple(Object *parentobj,
137                              GpioPolarity gpio_polarity,
138                              LEDColor color,
139                              const char *description)
140  {
141      g_autofree char *name = NULL;
142      DeviceState *dev;
143  
144      dev = qdev_new(TYPE_LED);
145      qdev_prop_set_bit(dev, "gpio-active-high",
146                        gpio_polarity == GPIO_POLARITY_ACTIVE_HIGH);
147      qdev_prop_set_string(dev, "color", led_color_name[color]);
148      if (!description) {
149          static unsigned undescribed_led_id;
150          name = g_strdup_printf("undescribed-led-#%u", undescribed_led_id++);
151      } else {
152          qdev_prop_set_string(dev, "description", description);
153          name = g_ascii_strdown(description, -1);
154          name = g_strdelimit(name, " #", '-');
155      }
156      object_property_add_child(parentobj, name, OBJECT(dev));
157      qdev_realize_and_unref(dev, NULL, &error_fatal);
158  
159      return LED(dev);
160  }
161