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 
input_leds_brightness_get(struct led_classdev * cdev)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 
input_leds_brightness_set(struct led_classdev * cdev,enum led_brightness brightness)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 
input_leds_event(struct input_handle * handle,unsigned int type,unsigned int code,int value)66 static void input_leds_event(struct input_handle *handle, unsigned int type,
67 			     unsigned int code, int value)
68 {
69 }
70 
input_leds_get_count(struct input_dev * dev)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 
input_leds_connect(struct input_handler * handler,struct input_dev * dev,const struct input_device_id * id)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 
input_leds_disconnect(struct input_handle * handle)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 
input_leds_init(void)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 
input_leds_exit(void)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");
221