Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-2.6-microblaze.git] / drivers / input / input-leds.c
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 static enum led_brightness input_leds_brightness_get(struct led_classdev *cdev)
51 {
52         struct input_led *led = container_of(cdev, struct input_led, cdev);
53         struct input_dev *input = led->handle->dev;
54
55         return test_bit(led->code, input->led) ? cdev->max_brightness : 0;
56 }
57
58 static void input_leds_brightness_set(struct led_classdev *cdev,
59                                       enum led_brightness brightness)
60 {
61         struct input_led *led = container_of(cdev, struct input_led, cdev);
62
63         input_inject_event(led->handle, EV_LED, led->code, !!brightness);
64 }
65
66 static void input_leds_event(struct input_handle *handle, unsigned int type,
67                              unsigned int code, int value)
68 {
69 }
70
71 static int input_leds_get_count(struct input_dev *dev)
72 {
73         unsigned int led_code;
74         int count = 0;
75
76         for_each_set_bit(led_code, dev->ledbit, LED_CNT)
77                 if (input_led_info[led_code].name)
78                         count++;
79
80         return count;
81 }
82
83 static int input_leds_connect(struct input_handler *handler,
84                               struct input_dev *dev,
85                               const struct input_device_id *id)
86 {
87         struct input_leds *leds;
88         struct input_led *led;
89         unsigned int num_leds;
90         unsigned int led_code;
91         int led_no;
92         int error;
93
94         num_leds = input_leds_get_count(dev);
95         if (!num_leds)
96                 return -ENXIO;
97
98         leds = kzalloc(struct_size(leds, leds, num_leds), GFP_KERNEL);
99         if (!leds)
100                 return -ENOMEM;
101
102         leds->num_leds = num_leds;
103
104         leds->handle.dev = dev;
105         leds->handle.handler = handler;
106         leds->handle.name = "leds";
107         leds->handle.private = leds;
108
109         error = input_register_handle(&leds->handle);
110         if (error)
111                 goto err_free_mem;
112
113         error = input_open_device(&leds->handle);
114         if (error)
115                 goto err_unregister_handle;
116
117         led_no = 0;
118         for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
119                 if (!input_led_info[led_code].name)
120                         continue;
121
122                 led = &leds->leds[led_no];
123                 led->handle = &leds->handle;
124                 led->code = led_code;
125
126                 led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
127                                            dev_name(&dev->dev),
128                                            input_led_info[led_code].name);
129                 if (!led->cdev.name) {
130                         error = -ENOMEM;
131                         goto err_unregister_leds;
132                 }
133
134                 led->cdev.max_brightness = 1;
135                 led->cdev.brightness_get = input_leds_brightness_get;
136                 led->cdev.brightness_set = input_leds_brightness_set;
137                 led->cdev.default_trigger = input_led_info[led_code].trigger;
138
139                 error = led_classdev_register(&dev->dev, &led->cdev);
140                 if (error) {
141                         dev_err(&dev->dev, "failed to register LED %s: %d\n",
142                                 led->cdev.name, error);
143                         kfree(led->cdev.name);
144                         goto err_unregister_leds;
145                 }
146
147                 led_no++;
148         }
149
150         return 0;
151
152 err_unregister_leds:
153         while (--led_no >= 0) {
154                 struct input_led *led = &leds->leds[led_no];
155
156                 led_classdev_unregister(&led->cdev);
157                 kfree(led->cdev.name);
158         }
159
160         input_close_device(&leds->handle);
161
162 err_unregister_handle:
163         input_unregister_handle(&leds->handle);
164
165 err_free_mem:
166         kfree(leds);
167         return error;
168 }
169
170 static void input_leds_disconnect(struct input_handle *handle)
171 {
172         struct input_leds *leds = handle->private;
173         int i;
174
175         for (i = 0; i < leds->num_leds; i++) {
176                 struct input_led *led = &leds->leds[i];
177
178                 led_classdev_unregister(&led->cdev);
179                 kfree(led->cdev.name);
180         }
181
182         input_close_device(handle);
183         input_unregister_handle(handle);
184
185         kfree(leds);
186 }
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 static int __init input_leds_init(void)
206 {
207         return input_register_handler(&input_leds_handler);
208 }
209 module_init(input_leds_init);
210
211 static void __exit input_leds_exit(void)
212 {
213         input_unregister_handler(&input_leds_handler);
214 }
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");