led.c
5347 bytes
1/* Copyright 2020 zvecr<git@zvecr.com>
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#include "led.h"
17#include "host.h"
18#include "timer.h"
19#include "debug.h"
20#include "gpio.h"
21
22#ifdef BACKLIGHT_CAPS_LOCK
23# ifdef BACKLIGHT_ENABLE
24# include "backlight.h"
25extern backlight_config_t backlight_config;
26# else
27# pragma message "Cannot use BACKLIGHT_CAPS_LOCK without backlight being enabled"
28# undef BACKLIGHT_CAPS_LOCK
29# endif
30#endif
31
32#ifndef LED_PIN_ON_STATE
33# define LED_PIN_ON_STATE 1
34#endif
35
36#ifdef BACKLIGHT_CAPS_LOCK
37/** \brief Caps Lock indicator using backlight (for keyboards without dedicated LED)
38 */
39static void handle_backlight_caps_lock(led_t led_state) {
40 // Use backlight as Caps Lock indicator
41 uint8_t bl_toggle_lvl = 0;
42
43 if (led_state.caps_lock && !backlight_config.enable) {
44 // Turning Caps Lock ON and backlight is disabled in config
45 // Toggling backlight to the brightest level
46 bl_toggle_lvl = BACKLIGHT_LEVELS;
47 } else if (!led_state.caps_lock && backlight_config.enable) {
48 // Turning Caps Lock OFF and backlight is enabled in config
49 // Toggling backlight and restoring config level
50 bl_toggle_lvl = backlight_config.level;
51 }
52
53 // Set level without modify backlight_config to keep ability to restore state
54 backlight_set(bl_toggle_lvl);
55}
56#endif
57
58static uint32_t last_led_modification_time = 0;
59
60uint32_t last_led_activity_time(void) {
61 return last_led_modification_time;
62}
63
64uint32_t last_led_activity_elapsed(void) {
65 return timer_elapsed32(last_led_modification_time);
66}
67
68/** \brief Lock LED update callback - keymap/user level
69 *
70 * \return True if led_update_kb() should run its own code, false otherwise.
71 */
72__attribute__((weak)) bool led_update_user(led_t led_state) {
73 return true;
74}
75
76/** \brief Lock LED update callback - keyboard level
77 *
78 * \return Ignored for now.
79 */
80__attribute__((weak)) bool led_update_kb(led_t led_state) {
81 bool res = led_update_user(led_state);
82 if (res) {
83 led_update_ports(led_state);
84 }
85 return res;
86}
87
88/** \brief Write LED state to hardware
89 */
90__attribute__((weak)) void led_update_ports(led_t led_state) {
91#if LED_PIN_ON_STATE == 0
92 // invert the whole thing to avoid having to conditionally !led_state.x later
93 led_state.raw = ~led_state.raw;
94#endif
95
96#ifdef LED_NUM_LOCK_PIN
97 gpio_write_pin(LED_NUM_LOCK_PIN, led_state.num_lock);
98#endif
99#ifdef LED_CAPS_LOCK_PIN
100 gpio_write_pin(LED_CAPS_LOCK_PIN, led_state.caps_lock);
101#endif
102#ifdef LED_SCROLL_LOCK_PIN
103 gpio_write_pin(LED_SCROLL_LOCK_PIN, led_state.scroll_lock);
104#endif
105#ifdef LED_COMPOSE_PIN
106 gpio_write_pin(LED_COMPOSE_PIN, led_state.compose);
107#endif
108#ifdef LED_KANA_PIN
109 gpio_write_pin(LED_KANA_PIN, led_state.kana);
110#endif
111}
112
113/** \brief Initialise any LED related hardware and/or state
114 */
115__attribute__((weak)) void led_init_ports(void) {
116#ifdef LED_NUM_LOCK_PIN
117 gpio_set_pin_output(LED_NUM_LOCK_PIN);
118 gpio_write_pin(LED_NUM_LOCK_PIN, !LED_PIN_ON_STATE);
119#endif
120#ifdef LED_CAPS_LOCK_PIN
121 gpio_set_pin_output(LED_CAPS_LOCK_PIN);
122 gpio_write_pin(LED_CAPS_LOCK_PIN, !LED_PIN_ON_STATE);
123#endif
124#ifdef LED_SCROLL_LOCK_PIN
125 gpio_set_pin_output(LED_SCROLL_LOCK_PIN);
126 gpio_write_pin(LED_SCROLL_LOCK_PIN, !LED_PIN_ON_STATE);
127#endif
128#ifdef LED_COMPOSE_PIN
129 gpio_set_pin_output(LED_COMPOSE_PIN);
130 gpio_write_pin(LED_COMPOSE_PIN, !LED_PIN_ON_STATE);
131#endif
132#ifdef LED_KANA_PIN
133 gpio_set_pin_output(LED_KANA_PIN);
134 gpio_write_pin(LED_KANA_PIN, !LED_PIN_ON_STATE);
135#endif
136}
137
138/** \brief Entrypoint for protocol to LED binding
139 */
140__attribute__((weak)) void led_set(uint8_t usb_led) {
141#ifdef BACKLIGHT_CAPS_LOCK
142 handle_backlight_caps_lock((led_t)usb_led);
143#endif
144
145 led_update_kb((led_t)usb_led);
146}
147
148/** \brief Trigger behaviour on transition to suspend
149 */
150void led_suspend(void) {
151 led_t leds_off = {0};
152#ifdef BACKLIGHT_CAPS_LOCK
153 if (is_backlight_enabled()) {
154 // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off
155 leds_off.caps_lock = true;
156 }
157#endif
158 led_set(leds_off.raw);
159}
160
161/** \brief Trigger behaviour on transition from suspend
162 */
163void led_wakeup(void) {
164 led_set(host_keyboard_leds());
165}
166
167/** \brief set host led state
168 *
169 * Only sets state if change detected
170 */
171void led_task(void) {
172 static uint8_t last_led_status = 0;
173
174 // update LED
175 uint8_t led_status = host_keyboard_leds();
176 if (last_led_status != led_status) {
177 last_led_status = led_status;
178 last_led_modification_time = timer_read32();
179
180 if (debug_keyboard) {
181 dprintf("led_task: %02X\n", led_status);
182 }
183 led_set(led_status);
184 }
185}