Commit 52355522 authored by Samuel Thibault's avatar Samuel Thibault Committed by Dmitry Torokhov

tty/vt/keyboard: define LED triggers for VT LED states

Now that input core allows controlling keyboards LEDs via standard LED
subsystem triggers let's switch VT keyboard code to make use of this
feature. We will define the following standard triggers: "kbd-scrollock",
"kbd-numlock", "kbd-capslock", and "kbd-kanalock" which are default
triggers for respective LEDs on keyboards.
Signed-off-by: default avatarSamuel Thibault <samuel.thibault@ens-lyon.org>
Tested-by: default avatarPavel Machek <pavel@ucw.cz>
Acked-by: default avatarPavel Machek <pavel@ucw.cz>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent f60c8ba7
......@@ -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>
......@@ -961,6 +962,110 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
}
}
#if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS)
struct kbd_led_trigger {
struct led_trigger trigger;
unsigned int mask;
};
static void kbd_led_trigger_activate(struct led_classdev *cdev)
{
struct kbd_led_trigger *trigger =
container_of(cdev->trigger, struct kbd_led_trigger, trigger);
tasklet_disable(&keyboard_tasklet);
if (ledstate != 0xff)
led_trigger_event(&trigger->trigger,
ledstate & trigger->mask ?
LED_FULL : LED_OFF);
tasklet_enable(&keyboard_tasklet);
}
#define KBD_LED_TRIGGER(_led_bit, _name) { \
.trigger = { \
.name = _name, \
.activate = kbd_led_trigger_activate, \
}, \
.mask = BIT(_led_bit), \
}
static struct kbd_led_trigger kbd_led_triggers[] = {
KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"),
KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"),
KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"),
KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"),
};
static void kbd_propagate_led_state(unsigned int old_state,
unsigned int new_state)
{
struct kbd_led_trigger *trigger;
unsigned int changed = old_state ^ new_state;
int i;
for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) {
trigger = &kbd_led_triggers[i];
if (changed & trigger->mask)
led_trigger_event(&trigger->trigger,
new_state & trigger->mask ?
LED_FULL : LED_OFF);
}
}
static int kbd_update_leds_helper(struct input_handle *handle, void *data)
{
unsigned int led_state = *(unsigned int *)data;
if (test_bit(EV_LED, handle->dev->evbit))
kbd_propagate_led_state(~led_state, led_state);
return 0;
}
static void kbd_init_leds(void)
{
int error;
int i;
for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) {
error = led_trigger_register(&kbd_led_triggers[i].trigger);
if (error)
pr_err("error %d while registering trigger %s\n",
error, kbd_led_triggers[i].trigger.name);
}
}
#else
static int kbd_update_leds_helper(struct input_handle *handle, void *data)
{
unsigned int leds = *(unsigned int *)data;
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);
}
return 0;
}
static void kbd_propagate_led_state(unsigned int old_state,
unsigned int new_state)
{
input_handler_for_each_handle(&kbd_handler, &new_state,
kbd_update_leds_helper);
}
static void kbd_init_leds(void)
{
}
#endif
/*
* The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
* or (ii) whatever pattern of lights people want to show using KDSETLED,
......@@ -995,20 +1100,6 @@ static inline unsigned char getleds(void)
return kb->ledflagstate;
}
static int kbd_update_leds_helper(struct input_handle *handle, void *data)
{
unsigned char leds = *(unsigned char *)data;
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);
}
return 0;
}
/**
* vt_get_leds - helper for braille console
* @console: console to read
......@@ -1085,24 +1176,22 @@ void vt_kbd_con_stop(int console)
}
/*
* This is the tasklet that updates LED state on all keyboards
* attached to the box. The reason we use tasklet is that we
* need to handle the scenario when keyboard handler is not
* registered yet but we already getting updates from the VT to
* update led state.
* This is the tasklet that updates LED state of LEDs using standard
* keyboard triggers. The reason we use tasklet is that we need to
* handle the scenario when keyboard handler is not registered yet
* but we already getting updates from the VT to update led state.
*/
static void kbd_bh(unsigned long dummy)
{
unsigned char leds;
unsigned long flags;
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);
kbd_propagate_led_state(ledstate, leds);
ledstate = leds;
}
}
......@@ -1450,8 +1539,10 @@ static void kbd_start(struct input_handle *handle)
{
tasklet_disable(&keyboard_tasklet);
if (ledstate != 0xff)
kbd_update_leds_helper(handle, &ledstate);
if (ledstate != 0xff) {
unsigned int state = ledstate;
kbd_update_leds_helper(handle, &state);
}
tasklet_enable(&keyboard_tasklet);
}
......@@ -1497,6 +1588,8 @@ int __init kbd_init(void)
kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE;
}
kbd_init_leds();
error = input_register_handler(&kbd_handler);
if (error)
return error;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment