Skip to content

Commit

Permalink
tty/vt/keyboard: define LED triggers for VT LED states
Browse files Browse the repository at this point in the history
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: Samuel Thibault <samuel.thibault@ens-lyon.org>
Tested-by: Pavel Machek <pavel@ucw.cz>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
  • Loading branch information
Samuel Thibault authored and Dmitry Torokhov committed Jun 16, 2015
1 parent f60c8ba commit 5235552
Showing 1 changed file with 117 additions and 24 deletions.
141 changes: 117 additions & 24 deletions drivers/tty/vt/keyboard.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 5235552

Please sign in to comment.