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 }