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