Re: [PATCH] INPUT: Route keyboard LEDs through the generic LEDs layer.

From: Pali RohÃr
Date: Sun Apr 06 2014 - 06:03:20 EST


2014-04-01 9:02 GMT+02:00 Pali RohÃr <pali.rohar@xxxxxxxxx>:
> 2014-03-31 14:23 GMT+02:00 Samuel Thibault <samuel.thibault@xxxxxxxxxxxx>:
>> This permits to reassign keyboard LEDs to something else than keyboard "leds"
>> state, by adding keyboard led and modifier triggers connected to a series
>> of VT input LEDs, themselves connected to VT input triggers, which
>> per-input device LEDs use by default. Userland can thus easily change the LED
>> behavior of (a priori) all input devices, or of particular input devices.
>>
>> This also permits to fix #7063 from userland by using a modifier to implement
>> proper CapsLock behavior and have the keyboard caps lock led show that modifier
>> state.
>>
>> [ebroder@xxxxxxxxxxxx: Rebased to 3.2-rc1 or so, cleaned up some includes, and fixed some constants]
>> [blogic@xxxxxxxxxxx: CONFIG_INPUT_LEDS stubs should be static inline]
>> [akpm@xxxxxxxxxxxxxxxxxxxx: remove unneeded `extern', fix comment layout]
>> Signed-off-by: Samuel Thibault <samuel.thibault@xxxxxxxxxxxx>
>> Signed-off-by: Evan Broder <evan@xxxxxxxxxxx>
>> Reviewed-by: David Herrmann <dh.herrmann@xxxxxxxxx>
>> Tested-by: Pavel Machek <pavel@xxxxxx>
>> Acked-by: Peter Korsgaard <jacmet@xxxxxxxxxx>
>> Signed-off-by: John Crispin <blogic@xxxxxxxxxxx>
>> Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
>> ---
>> Changed in this version:
>> - fixes symbol dependencies between input.c and leds.c (notably
>> input_led_connect/disconnect) by stuffing them together in input.ko.
>> - documents the new leds field of struct input_dev.
>>
>> --- a/Documentation/leds/leds-class.txt
>> +++ b/Documentation/leds/leds-class.txt
>> @@ -2,9 +2,6 @@
>> LED handling under Linux
>> ========================
>>
>> -If you're reading this and thinking about keyboard leds, these are
>> -handled by the input subsystem and the led class is *not* needed.
>> -
>> In its simplest form, the LED class just allows control of LEDs from
>> userspace. LEDs appear in /sys/class/leds/. The maximum brightness of the
>> LED is defined in max_brightness file. The brightness file will set the brightness
>> --- a/drivers/input/input.c
>> +++ b/drivers/input/input.c
>> @@ -708,6 +708,9 @@ static void input_disconnect_device(stru
>> handle->open = 0;
>>
>> spin_unlock_irq(&dev->event_lock);
>> +
>> + if (is_event_supported(EV_LED, dev->evbit, EV_MAX))
>> + input_led_disconnect(dev);
>> }
>>
>> /**
>> @@ -2134,6 +2137,9 @@ int input_register_device(struct input_d
>>
>> list_add_tail(&dev->node, &input_dev_list);
>>
>> + if (is_event_supported(EV_LED, dev->evbit, EV_MAX))
>> + input_led_connect(dev);
>> +
>> list_for_each_entry(handler, &input_handler_list, node)
>> input_attach_handler(dev, handler);
>>
>> --- a/drivers/input/Kconfig
>> +++ b/drivers/input/Kconfig
>> @@ -178,6 +178,15 @@ comment "Input Device Drivers"
>>
>> source "drivers/input/keyboard/Kconfig"
>>
>> +config INPUT_LEDS
>> + bool "LED Support"
>> + depends on LEDS_CLASS = INPUT || LEDS_CLASS = y
>> + select LEDS_TRIGGERS
>> + default y
>> + help
>> + This option enables support for LEDs on keyboards managed
>> + by the input layer.
>> +
>> source "drivers/input/mouse/Kconfig"
>>
>> source "drivers/input/joystick/Kconfig"
>> --- a/drivers/input/Makefile
>> +++ b/drivers/input/Makefile
>> @@ -6,6 +6,9 @@
>>
>> obj-$(CONFIG_INPUT) += input-core.o
>> input-core-y := input.o input-compat.o input-mt.o ff-core.o
>> +ifeq ($(CONFIG_INPUT_LEDS),y)
>> +input-core-y += leds.o
>> +endif
>>
>> obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
>> obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
>> --- a/drivers/leds/Kconfig
>> +++ b/drivers/leds/Kconfig
>> @@ -11,9 +11,6 @@ menuconfig NEW_LEDS
>> Say Y to enable Linux LED support. This allows control of supported
>> LEDs from both userspace and optionally, by kernel events (triggers).
>>
>> - This is not related to standard keyboard LEDs which are controlled
>> - via the input system.
>> -
>> if NEW_LEDS
>>
>> config LEDS_CLASS
>> --- a/drivers/tty/Kconfig
>> +++ b/drivers/tty/Kconfig
>> @@ -13,6 +13,10 @@ config VT
>> bool "Virtual terminal" if EXPERT
>> depends on !S390 && !UML
>> select INPUT
>> + select NEW_LEDS
>> + select LEDS_CLASS
>> + select LEDS_TRIGGERS
>> + select INPUT_LEDS
>> default y
>> ---help---
>> If you say Y here, you will get support for terminal devices with
>> --- a/drivers/tty/vt/keyboard.c
>> +++ b/drivers/tty/vt/keyboard.c
>> @@ -33,6 +33,7 @@
>> #include <linux/string.h>
>> #include <linux/init.h>
>> #include <linux/slab.h>
>> +#include <linux/leds.h>
>>
>> #include <linux/kbd_kern.h>
>> #include <linux/kbd_diacr.h>
>> @@ -130,6 +131,7 @@ static char rep; /* flag telling cha
>> static int shift_state = 0;
>>
>> static unsigned char ledstate = 0xff; /* undefined */
>> +static unsigned char lockstate = 0xff; /* undefined */
>> static unsigned char ledioctl;
>>
>> /*
>> @@ -961,6 +963,41 @@ static void k_brl(struct vc_data *vc, un
>> }
>> }
>>
>> +/* We route VT keyboard "leds" through triggers */
>> +static void kbd_ledstate_trigger_activate(struct led_classdev *cdev);
>> +
>> +static struct led_trigger ledtrig_ledstate[] = {
>> +#define DEFINE_LEDSTATE_TRIGGER(kbd_led, nam) \
>> + [kbd_led] = { \
>> + .name = nam, \
>> + .activate = kbd_ledstate_trigger_activate, \
>> + }
>> + DEFINE_LEDSTATE_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"),
>> + DEFINE_LEDSTATE_TRIGGER(VC_NUMLOCK, "kbd-numlock"),
>> + DEFINE_LEDSTATE_TRIGGER(VC_CAPSLOCK, "kbd-capslock"),
>> + DEFINE_LEDSTATE_TRIGGER(VC_KANALOCK, "kbd-kanalock"),
>> +#undef DEFINE_LEDSTATE_TRIGGER
>> +};
>> +
>> +static void kbd_lockstate_trigger_activate(struct led_classdev *cdev);
>> +
>> +static struct led_trigger ledtrig_lockstate[] = {
>> +#define DEFINE_LOCKSTATE_TRIGGER(kbd_led, nam) \
>> + [kbd_led] = { \
>> + .name = nam, \
>> + .activate = kbd_lockstate_trigger_activate, \
>> + }
>> + DEFINE_LOCKSTATE_TRIGGER(VC_SHIFTLOCK, "kbd-shiftlock"),
>> + DEFINE_LOCKSTATE_TRIGGER(VC_ALTGRLOCK, "kbd-altgrlock"),
>> + DEFINE_LOCKSTATE_TRIGGER(VC_CTRLLOCK, "kbd-ctrllock"),
>> + DEFINE_LOCKSTATE_TRIGGER(VC_ALTLOCK, "kbd-altlock"),
>> + DEFINE_LOCKSTATE_TRIGGER(VC_SHIFTLLOCK, "kbd-shiftllock"),
>> + DEFINE_LOCKSTATE_TRIGGER(VC_SHIFTRLOCK, "kbd-shiftrlock"),
>> + DEFINE_LOCKSTATE_TRIGGER(VC_CTRLLLOCK, "kbd-ctrlllock"),
>> + DEFINE_LOCKSTATE_TRIGGER(VC_CTRLRLOCK, "kbd-ctrlrlock"),
>> +#undef DEFINE_LOCKSTATE_TRIGGER
>> +};
>> +
>> /*
>> * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
>> * or (ii) whatever pattern of lights people want to show using KDSETLED,
>> @@ -995,18 +1032,25 @@ static inline unsigned char getleds(void
>> return kbd->ledflagstate;
>> }
>>
>> -static int kbd_update_leds_helper(struct input_handle *handle, void *data)
>> +/* Called on trigger connection, to set initial state */
>> +static void kbd_ledstate_trigger_activate(struct led_classdev *cdev)
>> {
>> - unsigned char leds = *(unsigned char *)data;
>> + struct led_trigger *trigger = cdev->trigger;
>> + int led = trigger - ledtrig_ledstate;
>>
>> - if (test_bit(EV_LED, handle->dev->evbit)) {
>> - input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01));
>> - input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02));
>> - input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04));
>> - input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
>> - }
>> + tasklet_disable(&keyboard_tasklet);
>> + led_trigger_event(trigger, ledstate & (1 << led) ? LED_FULL : LED_OFF);
>> + tasklet_enable(&keyboard_tasklet);
>> +}
>>
>> - return 0;
>> +static void kbd_lockstate_trigger_activate(struct led_classdev *cdev)
>> +{
>> + struct led_trigger *trigger = cdev->trigger;
>> + int led = trigger - ledtrig_lockstate;
>> +
>> + tasklet_disable(&keyboard_tasklet);
>> + led_trigger_event(trigger, lockstate & (1 << led) ? LED_FULL : LED_OFF);
>> + tasklet_enable(&keyboard_tasklet);
>> }
>>
>> /**
>> @@ -1095,16 +1139,29 @@ static void kbd_bh(unsigned long dummy)
>> {
>> unsigned char leds;
>> unsigned long flags;
>> -
>> + int i;
>> +
>> spin_lock_irqsave(&led_lock, flags);
>> leds = getleds();
>> spin_unlock_irqrestore(&led_lock, flags);
>>
>> if (leds != ledstate) {
>> - input_handler_for_each_handle(&kbd_handler, &leds,
>> - kbd_update_leds_helper);
>> + for (i = 0; i < ARRAY_SIZE(ledtrig_ledstate); i++)
>> + if ((leds ^ ledstate) & (1 << i))
>> + led_trigger_event(&ledtrig_ledstate[i],
>> + leds & (1 << i)
>> + ? LED_FULL : LED_OFF);
>> ledstate = leds;
>> }
>> +
>> + if (kbd->lockstate != lockstate) {
>> + for (i = 0; i < ARRAY_SIZE(ledtrig_lockstate); i++)
>> + if ((kbd->lockstate ^ lockstate) & (1 << i))
>> + led_trigger_event(&ledtrig_lockstate[i],
>> + kbd->lockstate & (1 << i)
>> + ? LED_FULL : LED_OFF);
>> + lockstate = kbd->lockstate;
>> + }
>> }
>>
>> DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0);
>> @@ -1442,20 +1499,6 @@ static void kbd_disconnect(struct input_
>> kfree(handle);
>> }
>>
>> -/*
>> - * Start keyboard handler on the new keyboard by refreshing LED state to
>> - * match the rest of the system.
>> - */
>> -static void kbd_start(struct input_handle *handle)
>> -{
>> - tasklet_disable(&keyboard_tasklet);
>> -
>> - if (ledstate != 0xff)
>> - kbd_update_leds_helper(handle, &ledstate);
>> -
>> - tasklet_enable(&keyboard_tasklet);
>> -}
>> -
>> static const struct input_device_id kbd_ids[] = {
>> {
>> .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
>> @@ -1477,7 +1520,6 @@ static struct input_handler kbd_handler
>> .match = kbd_match,
>> .connect = kbd_connect,
>> .disconnect = kbd_disconnect,
>> - .start = kbd_start,
>> .name = "kbd",
>> .id_table = kbd_ids,
>> };
>> @@ -1501,6 +1543,20 @@ int __init kbd_init(void)
>> if (error)
>> return error;
>>
>> + for (i = 0; i < ARRAY_SIZE(ledtrig_ledstate); i++) {
>> + error = led_trigger_register(&ledtrig_ledstate[i]);
>> + if (error)
>> + pr_err("error %d while registering trigger %s\n",
>> + error, ledtrig_ledstate[i].name);
>> + }
>> +
>> + for (i = 0; i < ARRAY_SIZE(ledtrig_lockstate); i++) {
>> + error = led_trigger_register(&ledtrig_lockstate[i]);
>> + if (error)
>> + pr_err("error %d while registering trigger %s\n",
>> + error, ledtrig_lockstate[i].name);
>> + }
>> +
>> tasklet_enable(&keyboard_tasklet);
>> tasklet_schedule(&keyboard_tasklet);
>>
>> --- a/include/linux/input.h
>> +++ b/include/linux/input.h
>> @@ -79,6 +79,7 @@ struct input_value {
>> * @led: reflects current state of device's LEDs
>> * @snd: reflects current state of sound effects
>> * @sw: reflects current state of device's switches
>> + * @leds: leds objects for the device's LEDs
>> * @open: this method is called when the very first user calls
>> * input_open_device(). The driver must prepare the device
>> * to start generating events (start polling thread,
>> @@ -164,6 +165,8 @@ struct input_dev {
>> unsigned long snd[BITS_TO_LONGS(SND_CNT)];
>> unsigned long sw[BITS_TO_LONGS(SW_CNT)];
>>
>> + struct led_classdev *leds;
>> +
>> int (*open)(struct input_dev *dev);
>> void (*close)(struct input_dev *dev);
>> int (*flush)(struct input_dev *dev, struct file *file);
>> @@ -531,4 +534,22 @@ int input_ff_erase(struct input_dev *dev
>> int input_ff_create_memless(struct input_dev *dev, void *data,
>> int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
>>
>> +#ifdef CONFIG_INPUT_LEDS
>> +
>> +int input_led_connect(struct input_dev *dev);
>> +void input_led_disconnect(struct input_dev *dev);
>> +
>> +#else
>> +
>> +static inline int input_led_connect(struct input_dev *dev)
>> +{
>> + return 0;
>> +}
>> +
>> +static inline void input_led_disconnect(struct input_dev *dev)
>> +{
>> +}
>> +
>> +#endif
>> +
>> #endif
>> --- /dev/null
>> +++ b/drivers/input/leds.c
>> @@ -0,0 +1,249 @@
>> +/*
>> + * LED support for the input layer
>> + *
>> + * Copyright 2010-2014 Samuel Thibault <samuel.thibault@xxxxxxxxxxxx>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/slab.h>
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/leds.h>
>> +#include <linux/input.h>
>> +
>> +/*
>> + * Keyboard LEDs are propagated by default like the following example:
>> + *
>> + * VT keyboard numlock trigger
>> + * -> vt::numl VT LED
>> + * -> vt-numl VT trigger
>> + * -> per-device inputX::numl LED
>> + *
>> + * Userland can however choose the trigger for the vt::numl LED, or
>> + * independently choose the trigger for any inputx::numl LED.
>> + *
>> + *
>> + * VT LED classes and triggers are registered on-demand according to
>> + * existing LED devices
>> + */
>> +
>> +/* Handler for VT LEDs, just triggers the corresponding VT trigger. */
>> +static void vt_led_set(struct led_classdev *cdev,
>> + enum led_brightness brightness);
>> +static struct led_classdev vt_leds[LED_CNT] = {
>> +#define DEFINE_INPUT_LED(vt_led, nam, deftrig) \
>> + [vt_led] = { \
>> + .name = "vt::"nam, \
>> + .max_brightness = 1, \
>> + .brightness_set = vt_led_set, \
>> + .default_trigger = deftrig, \
>> + }
>> +/* Default triggers for the VT LEDs just correspond to the legacy
>> + * usage. */
>> + DEFINE_INPUT_LED(LED_NUML, "numl", "kbd-numlock"),
>> + DEFINE_INPUT_LED(LED_CAPSL, "capsl", "kbd-capslock"),
>> + DEFINE_INPUT_LED(LED_SCROLLL, "scrolll", "kbd-scrollock"),
>> + DEFINE_INPUT_LED(LED_COMPOSE, "compose", NULL),
>> + DEFINE_INPUT_LED(LED_KANA, "kana", "kbd-kanalock"),
>> + DEFINE_INPUT_LED(LED_SLEEP, "sleep", NULL),
>> + DEFINE_INPUT_LED(LED_SUSPEND, "suspend", NULL),
>> + DEFINE_INPUT_LED(LED_MUTE, "mute", NULL),
>> + DEFINE_INPUT_LED(LED_MISC, "misc", NULL),
>> + DEFINE_INPUT_LED(LED_MAIL, "mail", NULL),
>> + DEFINE_INPUT_LED(LED_CHARGING, "charging", NULL),
>> +};
>> +static const char *const vt_led_names[LED_CNT] = {
>> + [LED_NUML] = "numl",
>> + [LED_CAPSL] = "capsl",
>> + [LED_SCROLLL] = "scrolll",
>> + [LED_COMPOSE] = "compose",
>> + [LED_KANA] = "kana",
>> + [LED_SLEEP] = "sleep",
>> + [LED_SUSPEND] = "suspend",
>> + [LED_MUTE] = "mute",
>> + [LED_MISC] = "misc",
>> + [LED_MAIL] = "mail",
>> + [LED_CHARGING] = "charging",
>> +};
>> +/* Handler for hotplug initialization */
>> +static void vt_led_trigger_activate(struct led_classdev *cdev);
>> +/* VT triggers */
>> +static struct led_trigger vt_led_triggers[LED_CNT] = {
>> +#define DEFINE_INPUT_LED_TRIGGER(vt_led, nam) \
>> + [vt_led] = { \
>> + .name = "vt-"nam, \
>> + .activate = vt_led_trigger_activate, \
>> + }
>> + DEFINE_INPUT_LED_TRIGGER(LED_NUML, "numl"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_CAPSL, "capsl"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_SCROLLL, "scrolll"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_COMPOSE, "compose"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_KANA, "kana"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_SLEEP, "sleep"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_SUSPEND, "suspend"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_MUTE, "mute"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_MISC, "misc"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_MAIL, "mail"),
>> + DEFINE_INPUT_LED_TRIGGER(LED_CHARGING, "charging"),
>> +};
>> +
>> +/* Lock for registration coherency */
>> +static DEFINE_MUTEX(vt_led_registered_lock);
>> +
>> +/* Which VT LED classes and triggers are registered */
>> +static unsigned long vt_led_registered[BITS_TO_LONGS(LED_CNT)];
>> +
>> +/* Number of input devices having each LED */
>> +static int vt_led_references[LED_CNT];
>> +
>> +/* VT LED state change, tell the VT trigger. */
>> +static void vt_led_set(struct led_classdev *cdev,
>> + enum led_brightness brightness)
>> +{
>> + int led = cdev - vt_leds;
>> +
>> + led_trigger_event(&vt_led_triggers[led], !!brightness);
>> +}
>> +
>> +/* LED state change for some keyboard, notify that keyboard. */
>> +static void perdevice_input_led_set(struct led_classdev *cdev,
>> + enum led_brightness brightness)
>> +{
>> + struct input_dev *dev;
>> + struct led_classdev *leds;
>> + int led;
>> +
>> + dev = cdev->dev->platform_data;
>> + if (!dev)
>> + /* Still initializing */
>> + return;
>> + leds = dev->leds;
>> + led = cdev - leds;
>> +
>> + input_event(dev, EV_LED, led, !!brightness);
>> + input_event(dev, EV_SYN, SYN_REPORT, 0);
>> +}
>> +
>> +/* Keyboard hotplug, initialize its LED status */
>> +static void vt_led_trigger_activate(struct led_classdev *cdev)
>> +{
>> + struct led_trigger *trigger = cdev->trigger;
>> + int led = trigger - vt_led_triggers;
>> +
>> + if (cdev->brightness_set)
>> + cdev->brightness_set(cdev, vt_leds[led].brightness);
>> +}
>> +
>> +/* Free led stuff from input device, used at abortion and disconnection. */
>> +static void input_led_delete(struct input_dev *dev)
>> +{
>> + if (dev) {
>> + struct led_classdev *leds = dev->leds;
>> + if (leds) {
>> + int i;
>> + for (i = 0; i < LED_CNT; i++)
>> + kfree(leds[i].name);
>> + kfree(leds);
>> + dev->leds = NULL;
>> + }
>> + }
>> +}
>> +
>> +/* A new input device with potential LEDs to connect. */
>> +int input_led_connect(struct input_dev *dev)
>> +{
>> + int i, error = 0;
>> + struct led_classdev *leds;
>> +
>> + dev->leds = leds = kcalloc(LED_CNT, sizeof(*leds), GFP_KERNEL);
>> + if (!dev->leds)
>> + return -ENOMEM;
>> +
>> + /* lazily register missing VT LEDs */
>> + mutex_lock(&vt_led_registered_lock);
>> + for (i = 0; i < LED_CNT; i++)
>> + if (vt_leds[i].name && test_bit(i, dev->ledbit)) {
>> + if (!vt_led_references[i]) {
>> + led_trigger_register(&vt_led_triggers[i]);
>> + /* This keyboard is first to have led i,
>> + * try to register it */
>> + if (!led_classdev_register(NULL, &vt_leds[i]))
>> + vt_led_references[i] = 1;
>> + else
>> + led_trigger_unregister(&vt_led_triggers[i]);
>> + } else
>> + vt_led_references[i]++;
>> + }
>> + mutex_unlock(&vt_led_registered_lock);
>> +
>> + /* and register this device's LEDs */
>> + for (i = 0; i < LED_CNT; i++)
>> + if (vt_leds[i].name && test_bit(i, dev->ledbit)) {
>> + leds[i].name = kasprintf(GFP_KERNEL, "%s::%s",
>> + dev_name(&dev->dev),
>> + vt_led_names[i]);
>> + if (!leds[i].name) {
>> + error = -ENOMEM;
>> + goto err;
>> + }
>> + leds[i].max_brightness = 1;
>> + leds[i].brightness_set = perdevice_input_led_set;
>> + leds[i].default_trigger = vt_led_triggers[i].name;
>> + }
>> +
>> + /* No issue so far, we can register for real. */
>> + for (i = 0; i < LED_CNT; i++)
>> + if (leds[i].name) {
>> + led_classdev_register(&dev->dev, &leds[i]);
>> + leds[i].dev->platform_data = dev;
>> + perdevice_input_led_set(&leds[i],
>> + vt_leds[i].brightness);
>> + }
>> +
>> + return 0;
>> +
>> +err:
>> + input_led_delete(dev);
>> + return error;
>> +}
>> +
>> +/*
>> + * Disconnected input device. Clean it, and deregister now-useless VT LEDs
>> + * and triggers.
>> + */
>> +void input_led_disconnect(struct input_dev *dev)
>> +{
>> + int i;
>> + struct led_classdev *leds = dev->leds;
>> +
>> + for (i = 0; i < LED_CNT; i++)
>> + if (leds[i].name)
>> + led_classdev_unregister(&leds[i]);
>> +
>> + input_led_delete(dev);
>> +
>> + mutex_lock(&vt_led_registered_lock);
>> + for (i = 0; i < LED_CNT; i++) {
>> + if (!vt_leds[i].name || !test_bit(i, dev->ledbit))
>> + continue;
>> +
>> + vt_led_references[i]--;
>> + if (vt_led_references[i]) {
>> + /* Still some devices needing it */
>> + continue;
>> + }
>> +
>> + led_classdev_unregister(&vt_leds[i]);
>> + led_trigger_unregister(&vt_led_triggers[i]);
>> + clear_bit(i, vt_led_registered);
>> + }
>> + mutex_unlock(&vt_led_registered_lock);
>> +}
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("User LED support for input layer");
>> +MODULE_AUTHOR("Samuel Thibault <samuel.thibault@xxxxxxxxxxxx>");
>>
>
> Dmitry, can you review this patch and include it into 3.15?
>

(Per Pavel and Sebastian suggestion resending email with included
keyword INPUT in subject)

--
Pali RohÃr
pali.rohar@xxxxxxxxx
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/