brintos

brintos / linux-shallow public Read only

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