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-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html