blob: 074a65ed17bb4d595d2b48f13e0d2e03c2267bc6 [file] [log] [blame]
Samuel Thibaultf60c8ba2015-03-16 21:19:56 -07001/*
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
24static 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
41struct input_led {
42 struct led_classdev cdev;
43 struct input_handle *handle;
44 unsigned int code; /* One of LED_* constants */
45};
46
47struct input_leds {
48 struct input_handle handle;
49 unsigned int num_leds;
50 struct input_led leds[];
51};
52
53static 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
61static 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
69static void input_leds_event(struct input_handle *handle, unsigned int type,
70 unsigned int code, int value)
71{
72}
73
74static int input_leds_connect(struct input_handler *handler,
75 struct input_dev *dev,
76 const struct input_device_id *id)
77{
78 struct input_leds *leds;
79 unsigned int num_leds;
80 unsigned int led_code;
81 int led_no;
82 int error;
83
84 num_leds = bitmap_weight(dev->ledbit, LED_CNT);
85 if (!num_leds)
86 return -ENXIO;
87
88 leds = kzalloc(sizeof(*leds) + num_leds * sizeof(*leds->leds),
89 GFP_KERNEL);
90 if (!leds)
91 return -ENOMEM;
92
93 leds->num_leds = num_leds;
94
95 leds->handle.dev = dev;
96 leds->handle.handler = handler;
97 leds->handle.name = "leds";
98 leds->handle.private = leds;
99
100 error = input_register_handle(&leds->handle);
101 if (error)
102 goto err_free_mem;
103
104 error = input_open_device(&leds->handle);
105 if (error)
106 goto err_unregister_handle;
107
108 led_no = 0;
109 for_each_set_bit(led_code, dev->ledbit, LED_CNT) {
110 struct input_led *led = &leds->leds[led_no];
111
112 led->handle = &leds->handle;
113 led->code = led_code;
114
115 if (WARN_ON(!input_led_info[led_code].name))
116 continue;
117
118 led->cdev.name = kasprintf(GFP_KERNEL, "%s::%s",
119 dev_name(&dev->dev),
120 input_led_info[led_code].name);
121 if (!led->cdev.name) {
122 error = -ENOMEM;
123 goto err_unregister_leds;
124 }
125
126 led->cdev.max_brightness = 1;
127 led->cdev.brightness_get = input_leds_brightness_get;
128 led->cdev.brightness_set = input_leds_brightness_set;
129 led->cdev.default_trigger = input_led_info[led_code].trigger;
130
131 error = led_classdev_register(&dev->dev, &led->cdev);
132 if (error) {
133 dev_err(&dev->dev, "failed to register LED %s: %d\n",
134 led->cdev.name, error);
135 kfree(led->cdev.name);
136 goto err_unregister_leds;
137 }
138
139 led_no++;
140 }
141
142 return 0;
143
144err_unregister_leds:
145 while (--led_no >= 0) {
146 struct input_led *led = &leds->leds[led_no];
147
148 led_classdev_unregister(&led->cdev);
149 kfree(led->cdev.name);
150 }
151
152 input_close_device(&leds->handle);
153
154err_unregister_handle:
155 input_unregister_handle(&leds->handle);
156
157err_free_mem:
158 kfree(leds);
159 return error;
160}
161
162static void input_leds_disconnect(struct input_handle *handle)
163{
164 struct input_leds *leds = handle->private;
165 int i;
166
167 for (i = 0; i < leds->num_leds; i++) {
168 struct input_led *led = &leds->leds[i];
169
170 led_classdev_unregister(&led->cdev);
171 kfree(led->cdev.name);
172 }
173
174 input_close_device(handle);
175 input_unregister_handle(handle);
176
177 kfree(leds);
178}
179
180static const struct input_device_id input_leds_ids[] = {
181 {
182 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
183 .evbit = { BIT_MASK(EV_LED) },
184 },
185 { },
186};
187MODULE_DEVICE_TABLE(input, input_leds_ids);
188
189static struct input_handler input_leds_handler = {
190 .event = input_leds_event,
191 .connect = input_leds_connect,
192 .disconnect = input_leds_disconnect,
193 .name = "leds",
194 .id_table = input_leds_ids,
195};
196
197static int __init input_leds_init(void)
198{
199 return input_register_handler(&input_leds_handler);
200}
201module_init(input_leds_init);
202
203static void __exit input_leds_exit(void)
204{
205 input_unregister_handler(&input_leds_handler);
206}
207module_exit(input_leds_exit);
208
209MODULE_AUTHOR("Samuel Thibault <samuel.thibault@ens-lyon.org>");
210MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
211MODULE_DESCRIPTION("Input -> LEDs Bridge");
212MODULE_LICENSE("GPL v2");