xref: /OK3568_Linux_fs/kernel/drivers/input/input-leds.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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