qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

led.c (5347B)


      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"
     25 extern 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  */
     39 static 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 
     58 static uint32_t last_led_modification_time = 0;
     59 
     60 uint32_t last_led_activity_time(void) {
     61     return last_led_modification_time;
     62 }
     63 
     64 uint32_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  */
    150 void 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  */
    163 void 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  */
    171 void 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 }