diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/tty/vt/keyboard.c | 141 |
1 files changed, 117 insertions, 24 deletions
diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index 8a89f6e7715d..fc080bf1c4d2 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include <linux/string.h> | 33 | #include <linux/string.h> |
| 34 | #include <linux/init.h> | 34 | #include <linux/init.h> |
| 35 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
| 36 | #include <linux/leds.h> | ||
| 36 | 37 | ||
| 37 | #include <linux/kbd_kern.h> | 38 | #include <linux/kbd_kern.h> |
| 38 | #include <linux/kbd_diacr.h> | 39 | #include <linux/kbd_diacr.h> |
| @@ -961,6 +962,110 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag) | |||
| 961 | } | 962 | } |
| 962 | } | 963 | } |
| 963 | 964 | ||
| 965 | #if IS_ENABLED(CONFIG_INPUT_LEDS) && IS_ENABLED(CONFIG_LEDS_TRIGGERS) | ||
| 966 | |||
| 967 | struct kbd_led_trigger { | ||
| 968 | struct led_trigger trigger; | ||
| 969 | unsigned int mask; | ||
| 970 | }; | ||
| 971 | |||
| 972 | static void kbd_led_trigger_activate(struct led_classdev *cdev) | ||
| 973 | { | ||
| 974 | struct kbd_led_trigger *trigger = | ||
| 975 | container_of(cdev->trigger, struct kbd_led_trigger, trigger); | ||
| 976 | |||
| 977 | tasklet_disable(&keyboard_tasklet); | ||
| 978 | if (ledstate != 0xff) | ||
| 979 | led_trigger_event(&trigger->trigger, | ||
| 980 | ledstate & trigger->mask ? | ||
| 981 | LED_FULL : LED_OFF); | ||
| 982 | tasklet_enable(&keyboard_tasklet); | ||
| 983 | } | ||
| 984 | |||
| 985 | #define KBD_LED_TRIGGER(_led_bit, _name) { \ | ||
| 986 | .trigger = { \ | ||
| 987 | .name = _name, \ | ||
| 988 | .activate = kbd_led_trigger_activate, \ | ||
| 989 | }, \ | ||
| 990 | .mask = BIT(_led_bit), \ | ||
| 991 | } | ||
| 992 | |||
| 993 | static struct kbd_led_trigger kbd_led_triggers[] = { | ||
| 994 | KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrollock"), | ||
| 995 | KBD_LED_TRIGGER(VC_NUMLOCK, "kbd-numlock"), | ||
| 996 | KBD_LED_TRIGGER(VC_CAPSLOCK, "kbd-capslock"), | ||
| 997 | KBD_LED_TRIGGER(VC_KANALOCK, "kbd-kanalock"), | ||
| 998 | }; | ||
| 999 | |||
| 1000 | static void kbd_propagate_led_state(unsigned int old_state, | ||
| 1001 | unsigned int new_state) | ||
| 1002 | { | ||
| 1003 | struct kbd_led_trigger *trigger; | ||
| 1004 | unsigned int changed = old_state ^ new_state; | ||
| 1005 | int i; | ||
| 1006 | |||
| 1007 | for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) { | ||
| 1008 | trigger = &kbd_led_triggers[i]; | ||
| 1009 | |||
| 1010 | if (changed & trigger->mask) | ||
| 1011 | led_trigger_event(&trigger->trigger, | ||
| 1012 | new_state & trigger->mask ? | ||
| 1013 | LED_FULL : LED_OFF); | ||
| 1014 | } | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | static int kbd_update_leds_helper(struct input_handle *handle, void *data) | ||
| 1018 | { | ||
| 1019 | unsigned int led_state = *(unsigned int *)data; | ||
| 1020 | |||
| 1021 | if (test_bit(EV_LED, handle->dev->evbit)) | ||
| 1022 | kbd_propagate_led_state(~led_state, led_state); | ||
| 1023 | |||
| 1024 | return 0; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | static void kbd_init_leds(void) | ||
| 1028 | { | ||
| 1029 | int error; | ||
| 1030 | int i; | ||
| 1031 | |||
| 1032 | for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) { | ||
| 1033 | error = led_trigger_register(&kbd_led_triggers[i].trigger); | ||
| 1034 | if (error) | ||
| 1035 | pr_err("error %d while registering trigger %s\n", | ||
| 1036 | error, kbd_led_triggers[i].trigger.name); | ||
| 1037 | } | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | #else | ||
| 1041 | |||
| 1042 | static int kbd_update_leds_helper(struct input_handle *handle, void *data) | ||
| 1043 | { | ||
| 1044 | unsigned int leds = *(unsigned int *)data; | ||
| 1045 | |||
| 1046 | if (test_bit(EV_LED, handle->dev->evbit)) { | ||
| 1047 | input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); | ||
| 1048 | input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); | ||
| 1049 | input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); | ||
| 1050 | input_inject_event(handle, EV_SYN, SYN_REPORT, 0); | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | return 0; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | static void kbd_propagate_led_state(unsigned int old_state, | ||
| 1057 | unsigned int new_state) | ||
| 1058 | { | ||
| 1059 | input_handler_for_each_handle(&kbd_handler, &new_state, | ||
| 1060 | kbd_update_leds_helper); | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | static void kbd_init_leds(void) | ||
| 1064 | { | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | #endif | ||
| 1068 | |||
| 964 | /* | 1069 | /* |
| 965 | * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, | 1070 | * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, |
| 966 | * or (ii) whatever pattern of lights people want to show using KDSETLED, | 1071 | * or (ii) whatever pattern of lights people want to show using KDSETLED, |
| @@ -995,20 +1100,6 @@ static inline unsigned char getleds(void) | |||
| 995 | return kb->ledflagstate; | 1100 | return kb->ledflagstate; |
| 996 | } | 1101 | } |
| 997 | 1102 | ||
| 998 | static int kbd_update_leds_helper(struct input_handle *handle, void *data) | ||
| 999 | { | ||
| 1000 | unsigned char leds = *(unsigned char *)data; | ||
| 1001 | |||
| 1002 | if (test_bit(EV_LED, handle->dev->evbit)) { | ||
| 1003 | input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); | ||
| 1004 | input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); | ||
| 1005 | input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); | ||
| 1006 | input_inject_event(handle, EV_SYN, SYN_REPORT, 0); | ||
| 1007 | } | ||
| 1008 | |||
| 1009 | return 0; | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | /** | 1103 | /** |
| 1013 | * vt_get_leds - helper for braille console | 1104 | * vt_get_leds - helper for braille console |
| 1014 | * @console: console to read | 1105 | * @console: console to read |
| @@ -1085,24 +1176,22 @@ void vt_kbd_con_stop(int console) | |||
| 1085 | } | 1176 | } |
| 1086 | 1177 | ||
| 1087 | /* | 1178 | /* |
| 1088 | * This is the tasklet that updates LED state on all keyboards | 1179 | * This is the tasklet that updates LED state of LEDs using standard |
| 1089 | * attached to the box. The reason we use tasklet is that we | 1180 | * keyboard triggers. The reason we use tasklet is that we need to |
| 1090 | * need to handle the scenario when keyboard handler is not | 1181 | * handle the scenario when keyboard handler is not registered yet |
| 1091 | * registered yet but we already getting updates from the VT to | 1182 | * but we already getting updates from the VT to update led state. |
| 1092 | * update led state. | ||
| 1093 | */ | 1183 | */ |
| 1094 | static void kbd_bh(unsigned long dummy) | 1184 | static void kbd_bh(unsigned long dummy) |
| 1095 | { | 1185 | { |
| 1096 | unsigned char leds; | 1186 | unsigned char leds; |
| 1097 | unsigned long flags; | 1187 | unsigned long flags; |
| 1098 | 1188 | ||
| 1099 | spin_lock_irqsave(&led_lock, flags); | 1189 | spin_lock_irqsave(&led_lock, flags); |
| 1100 | leds = getleds(); | 1190 | leds = getleds(); |
| 1101 | spin_unlock_irqrestore(&led_lock, flags); | 1191 | spin_unlock_irqrestore(&led_lock, flags); |
| 1102 | 1192 | ||
| 1103 | if (leds != ledstate) { | 1193 | if (leds != ledstate) { |
| 1104 | input_handler_for_each_handle(&kbd_handler, &leds, | 1194 | kbd_propagate_led_state(ledstate, leds); |
| 1105 | kbd_update_leds_helper); | ||
| 1106 | ledstate = leds; | 1195 | ledstate = leds; |
| 1107 | } | 1196 | } |
| 1108 | } | 1197 | } |
| @@ -1450,8 +1539,10 @@ static void kbd_start(struct input_handle *handle) | |||
| 1450 | { | 1539 | { |
| 1451 | tasklet_disable(&keyboard_tasklet); | 1540 | tasklet_disable(&keyboard_tasklet); |
| 1452 | 1541 | ||
| 1453 | if (ledstate != 0xff) | 1542 | if (ledstate != 0xff) { |
| 1454 | kbd_update_leds_helper(handle, &ledstate); | 1543 | unsigned int state = ledstate; |
| 1544 | kbd_update_leds_helper(handle, &state); | ||
| 1545 | } | ||
| 1455 | 1546 | ||
| 1456 | tasklet_enable(&keyboard_tasklet); | 1547 | tasklet_enable(&keyboard_tasklet); |
| 1457 | } | 1548 | } |
| @@ -1497,6 +1588,8 @@ int __init kbd_init(void) | |||
| 1497 | kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; | 1588 | kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; |
| 1498 | } | 1589 | } |
| 1499 | 1590 | ||
| 1591 | kbd_init_leds(); | ||
| 1592 | |||
| 1500 | error = input_register_handler(&kbd_handler); | 1593 | error = input_register_handler(&kbd_handler); |
| 1501 | if (error) | 1594 | if (error) |
| 1502 | return error; | 1595 | return error; |
