summaryrefslogtreecommitdiff
path: root/quantum/led.c
blob: e2b598510997a1678bdf17f34d744e4b956c1f73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/* Copyright 2020 zvecr<git@zvecr.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "led.h"
#include "host.h"
#include "timer.h"
#include "debug.h"
#include "gpio.h"

#ifdef BACKLIGHT_CAPS_LOCK
#    ifdef BACKLIGHT_ENABLE
#        include "backlight.h"
extern backlight_config_t backlight_config;
#    else
#        pragma message "Cannot use BACKLIGHT_CAPS_LOCK without backlight being enabled"
#        undef BACKLIGHT_CAPS_LOCK
#    endif
#endif

#ifndef LED_PIN_ON_STATE
#    define LED_PIN_ON_STATE 1
#endif

#ifdef BACKLIGHT_CAPS_LOCK
/** \brief Caps Lock indicator using backlight (for keyboards without dedicated LED)
 */
static void handle_backlight_caps_lock(led_t led_state) {
    // Use backlight as Caps Lock indicator
    uint8_t bl_toggle_lvl = 0;

    if (led_state.caps_lock && !backlight_config.enable) {
        // Turning Caps Lock ON and backlight is disabled in config
        // Toggling backlight to the brightest level
        bl_toggle_lvl = BACKLIGHT_LEVELS;
    } else if (!led_state.caps_lock && backlight_config.enable) {
        // Turning Caps Lock OFF and backlight is enabled in config
        // Toggling backlight and restoring config level
        bl_toggle_lvl = backlight_config.level;
    }

    // Set level without modify backlight_config to keep ability to restore state
    backlight_set(bl_toggle_lvl);
}
#endif

static uint32_t last_led_modification_time = 0;
uint32_t        last_led_activity_time(void) {
    return last_led_modification_time;
}
uint32_t last_led_activity_elapsed(void) {
    return timer_elapsed32(last_led_modification_time);
}

/** \brief Lock LED set callback - keymap/user level
 *
 * \deprecated Use led_update_user() instead.
 */
__attribute__((weak)) void led_set_user(uint8_t usb_led) {}

/** \brief Lock LED update callback - keymap/user level
 *
 * \return True if led_update_kb() should run its own code, false otherwise.
 */
__attribute__((weak)) bool led_update_user(led_t led_state) {
    return true;
}

/** \brief Lock LED update callback - keyboard level
 *
 * \return Ignored for now.
 */
__attribute__((weak)) bool led_update_kb(led_t led_state) {
    bool res = led_update_user(led_state);
    if (res) {
        led_update_ports(led_state);
    }
    return res;
}

/** \brief Write LED state to hardware
 */
__attribute__((weak)) void led_update_ports(led_t led_state) {
#if LED_PIN_ON_STATE == 0
    // invert the whole thing to avoid having to conditionally !led_state.x later
    led_state.raw = ~led_state.raw;
#endif

#ifdef LED_NUM_LOCK_PIN
    gpio_write_pin(LED_NUM_LOCK_PIN, led_state.num_lock);
#endif
#ifdef LED_CAPS_LOCK_PIN
    gpio_write_pin(LED_CAPS_LOCK_PIN, led_state.caps_lock);
#endif
#ifdef LED_SCROLL_LOCK_PIN
    gpio_write_pin(LED_SCROLL_LOCK_PIN, led_state.scroll_lock);
#endif
#ifdef LED_COMPOSE_PIN
    gpio_write_pin(LED_COMPOSE_PIN, led_state.compose);
#endif
#ifdef LED_KANA_PIN
    gpio_write_pin(LED_KANA_PIN, led_state.kana);
#endif
}

/** \brief Initialise any LED related hardware and/or state
 */
__attribute__((weak)) void led_init_ports(void) {
#ifdef LED_NUM_LOCK_PIN
    gpio_set_pin_output(LED_NUM_LOCK_PIN);
    gpio_write_pin(LED_NUM_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_CAPS_LOCK_PIN
    gpio_set_pin_output(LED_CAPS_LOCK_PIN);
    gpio_write_pin(LED_CAPS_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_SCROLL_LOCK_PIN
    gpio_set_pin_output(LED_SCROLL_LOCK_PIN);
    gpio_write_pin(LED_SCROLL_LOCK_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_COMPOSE_PIN
    gpio_set_pin_output(LED_COMPOSE_PIN);
    gpio_write_pin(LED_COMPOSE_PIN, !LED_PIN_ON_STATE);
#endif
#ifdef LED_KANA_PIN
    gpio_set_pin_output(LED_KANA_PIN);
    gpio_write_pin(LED_KANA_PIN, !LED_PIN_ON_STATE);
#endif
}

/** \brief Entrypoint for protocol to LED binding
 */
__attribute__((weak)) void led_set(uint8_t usb_led) {
#ifdef BACKLIGHT_CAPS_LOCK
    handle_backlight_caps_lock((led_t)usb_led);
#endif

    led_set_user(usb_led);
    led_update_kb((led_t)usb_led);
}

/** \brief Trigger behaviour on transition to suspend
 */
void led_suspend(void) {
    led_t leds_off = {0};
#ifdef BACKLIGHT_CAPS_LOCK
    if (is_backlight_enabled()) {
        // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off
        leds_off.caps_lock = true;
    }
#endif
    led_set(leds_off.raw);
}

/** \brief Trigger behaviour on transition from suspend
 */
void led_wakeup(void) {
    led_set(host_keyboard_leds());
}

/** \brief set host led state
 *
 * Only sets state if change detected
 */
void led_task(void) {
    static uint8_t last_led_status = 0;

    // update LED
    uint8_t led_status = host_keyboard_leds();
    if (last_led_status != led_status) {
        last_led_status            = led_status;
        last_led_modification_time = timer_read32();

        if (debug_keyboard) {
            dprintf("led_task: %02X\n", led_status);
        }
        led_set(led_status);
    }
}