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