Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * LED support for the input layer
4 : : *
5 : : * Copyright 2010-2015 Samuel Thibault <samuel.thibault@ens-lyon.org>
6 : : */
7 : :
8 : : #include <linux/kernel.h>
9 : : #include <linux/slab.h>
10 : : #include <linux/module.h>
11 : : #include <linux/init.h>
12 : : #include <linux/leds.h>
13 : : #include <linux/input.h>
14 : :
15 : : #if IS_ENABLED(CONFIG_VT)
16 : : #define VT_TRIGGER(_name) .trigger = _name
17 : : #else
18 : : #define VT_TRIGGER(_name) .trigger = NULL
19 : : #endif
20 : :
21 : : static const struct {
22 : : const char *name;
23 : : const char *trigger;
24 : : } input_led_info[LED_CNT] = {
25 : : [LED_NUML] = { "numlock", VT_TRIGGER("kbd-numlock") },
26 : : [LED_CAPSL] = { "capslock", VT_TRIGGER("kbd-capslock") },
27 : : [LED_SCROLLL] = { "scrolllock", VT_TRIGGER("kbd-scrolllock") },
28 : : [LED_COMPOSE] = { "compose" },
29 : : [LED_KANA] = { "kana", VT_TRIGGER("kbd-kanalock") },
30 : : [LED_SLEEP] = { "sleep" } ,
31 : : [LED_SUSPEND] = { "suspend" },
32 : : [LED_MUTE] = { "mute" },
33 : : [LED_MISC] = { "misc" },
34 : : [LED_MAIL] = { "mail" },
35 : : [LED_CHARGING] = { "charging" },
36 : : };
37 : :
38 : : struct input_led {
39 : : struct led_classdev cdev;
40 : : struct input_handle *handle;
41 : : unsigned int code; /* One of LED_* constants */
42 : : };
43 : :
44 : : struct input_leds {
45 : : struct input_handle handle;
46 : : unsigned int num_leds;
47 : : struct input_led leds[];
48 : : };
49 : :
50 : 90 : static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev)
51 : : {
52 : 90 : struct input_led *led = container_of(cdev, struct input_led, cdev);
53 : 90 : struct input_dev *input = led->handle->dev;
54 : :
55 [ - + ]: 90 : return test_bit(led->code, input->led) ? cdev->max_brightness : 0;
56 : : }
57 : :
58 : 90 : static void input_leds_brightness_set(struct led_classdev *cdev,
59 : : enum led_brightness brightness)
60 : : {
61 : 90 : struct input_led *led = container_of(cdev, struct input_led, cdev);
62 : :
63 : 90 : input_inject_event(led->handle, EV_LED, led->code, !!brightness);
64 : 90 : }
65 : :
66 : 0 : static void input_leds_event(struct input_handle *handle, unsigned int type,
67 : : unsigned int code, int value)
68 : : {
69 : 0 : }
70 : :
71 : 30 : static int input_leds_get_count(struct input_dev *dev)
72 : : {
73 : 30 : unsigned int led_code;
74 : 30 : int count = 0;
75 : :
76 [ + + ]: 120 : for_each_set_bit(led_code, dev->ledbit, LED_CNT)
77 [ + - ]: 90 : if (input_led_info[led_code].name)
78 : 90 : count++;
79 : :
80 : 30 : return count;
81 : : }
82 : :
83 : 30 : static int input_leds_connect(struct input_handler *handler,
84 : : struct input_dev *dev,
85 : : const struct input_device_id *id)
86 : : {
87 : 30 : struct input_leds *leds;
88 : 30 : struct input_led *led;
89 : 30 : unsigned int num_leds;
90 : 30 : unsigned int led_code;
91 : 30 : int led_no;
92 : 30 : int error;
93 : :
94 : 30 : num_leds = input_leds_get_count(dev);
95 [ + - ]: 30 : if (!num_leds)
96 : : return -ENXIO;
97 : :
98 [ - + ]: 30 : leds = kzalloc(struct_size(leds, leds, num_leds), GFP_KERNEL);
99 [ + - ]: 30 : if (!leds)
100 : : return -ENOMEM;
101 : :
102 : 30 : leds->num_leds = num_leds;
103 : :
104 : 30 : leds->handle.dev = dev;
105 : 30 : leds->handle.handler = handler;
106 : 30 : leds->handle.name = "leds";
107 : 30 : leds->handle.private = leds;
108 : :
109 : 30 : error = input_register_handle(&leds->handle);
110 [ - + ]: 30 : if (error)
111 : 0 : goto err_free_mem;
112 : :
113 : 30 : error = input_open_device(&leds->handle);
114 [ - + ]: 30 : if (error)
115 : 0 : goto err_unregister_handle;
116 : :
117 : 30 : led_no = 0;
118 [ + + ]: 120 : for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
119 [ - + ]: 90 : if (!input_led_info[led_code].name)
120 : 0 : continue;
121 : :
122 : 90 : led = &leds->leds[led_no];
123 : 90 : led->handle = &leds->handle;
124 : 90 : led->code = led_code;
125 : :
126 [ + - ]: 180 : led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
127 : : dev_name(&dev->dev),
128 : : input_led_info[led_code].name);
129 [ - + ]: 90 : if (!led->cdev.name) {
130 : 0 : error = -ENOMEM;
131 : 0 : goto err_unregister_leds;
132 : : }
133 : :
134 : 90 : led->cdev.max_brightness = 1;
135 : 90 : led->cdev.brightness_get = input_leds_brightness_get;
136 : 90 : led->cdev.brightness_set = input_leds_brightness_set;
137 : 90 : led->cdev.default_trigger = input_led_info[led_code].trigger;
138 : :
139 : 90 : error = led_classdev_register(&dev->dev, &led->cdev);
140 [ - + ]: 90 : if (error) {
141 : 0 : dev_err(&dev->dev, "failed to register LED %s: %d\n",
142 : : led->cdev.name, error);
143 : 0 : kfree(led->cdev.name);
144 : 0 : goto err_unregister_leds;
145 : : }
146 : :
147 : 90 : led_no++;
148 : : }
149 : :
150 : : return 0;
151 : :
152 : 0 : err_unregister_leds:
153 [ # # ]: 0 : while (--led_no >= 0) {
154 : 0 : struct input_led *led = &leds->leds[led_no];
155 : :
156 : 0 : led_classdev_unregister(&led->cdev);
157 : 0 : kfree(led->cdev.name);
158 : : }
159 : :
160 : 0 : input_close_device(&leds->handle);
161 : :
162 : 0 : err_unregister_handle:
163 : 0 : input_unregister_handle(&leds->handle);
164 : :
165 : 0 : err_free_mem:
166 : 0 : kfree(leds);
167 : 0 : return error;
168 : : }
169 : :
170 : 0 : static void input_leds_disconnect(struct input_handle *handle)
171 : : {
172 : 0 : struct input_leds *leds = handle->private;
173 : 0 : int i;
174 : :
175 [ # # ]: 0 : for (i = 0; i < leds->num_leds; i++) {
176 : 0 : struct input_led *led = &leds->leds[i];
177 : :
178 : 0 : led_classdev_unregister(&led->cdev);
179 : 0 : kfree(led->cdev.name);
180 : : }
181 : :
182 : 0 : input_close_device(handle);
183 : 0 : input_unregister_handle(handle);
184 : :
185 : 0 : kfree(leds);
186 : 0 : }
187 : :
188 : : static const struct input_device_id input_leds_ids[] = {
189 : : {
190 : : .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
191 : : .evbit = { BIT_MASK(EV_LED) },
192 : : },
193 : : { },
194 : : };
195 : : MODULE_DEVICE_TABLE(input, input_leds_ids);
196 : :
197 : : static struct input_handler input_leds_handler = {
198 : : .event = input_leds_event,
199 : : .connect = input_leds_connect,
200 : : .disconnect = input_leds_disconnect,
201 : : .name = "leds",
202 : : .id_table = input_leds_ids,
203 : : };
204 : :
205 : 30 : static int __init input_leds_init(void)
206 : : {
207 : 30 : return input_register_handler(&input_leds_handler);
208 : : }
209 : : module_init(input_leds_init);
210 : :
211 : 0 : static void __exit input_leds_exit(void)
212 : : {
213 : 0 : input_unregister_handler(&input_leds_handler);
214 : 0 : }
215 : : module_exit(input_leds_exit);
216 : :
217 : : MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
218 : : MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
219 : : MODULE_DESCRIPTION("Input -> LEDs Bridge");
220 : : MODULE_LICENSE("GPL v2");
|