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