Parent directory

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}