diff options
Diffstat (limited to 'quantum/keyboard.c')
-rw-r--r-- | quantum/keyboard.c | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/quantum/keyboard.c b/quantum/keyboard.c new file mode 100644 index 000000000..0eb41e7d3 --- /dev/null +++ b/quantum/keyboard.c | |||
@@ -0,0 +1,569 @@ | |||
1 | /* | ||
2 | Copyright 2011, 2012, 2013 Jun Wako <wakojun@gmail.com> | ||
3 | |||
4 | This program is free software: you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation, either version 2 of the License, or | ||
7 | (at your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include <stdint.h> | ||
19 | #include "keyboard.h" | ||
20 | #include "matrix.h" | ||
21 | #include "keymap.h" | ||
22 | #include "host.h" | ||
23 | #include "led.h" | ||
24 | #include "keycode.h" | ||
25 | #include "timer.h" | ||
26 | #include "sync_timer.h" | ||
27 | #include "print.h" | ||
28 | #include "debug.h" | ||
29 | #include "command.h" | ||
30 | #include "util.h" | ||
31 | #include "sendchar.h" | ||
32 | #include "eeconfig.h" | ||
33 | #include "action_layer.h" | ||
34 | #ifdef BACKLIGHT_ENABLE | ||
35 | # include "backlight.h" | ||
36 | #endif | ||
37 | #ifdef MOUSEKEY_ENABLE | ||
38 | # include "mousekey.h" | ||
39 | #endif | ||
40 | #ifdef PS2_MOUSE_ENABLE | ||
41 | # include "ps2_mouse.h" | ||
42 | #endif | ||
43 | #ifdef SERIAL_MOUSE_ENABLE | ||
44 | # include "serial_mouse.h" | ||
45 | #endif | ||
46 | #ifdef ADB_MOUSE_ENABLE | ||
47 | # include "adb.h" | ||
48 | #endif | ||
49 | #ifdef RGBLIGHT_ENABLE | ||
50 | # include "rgblight.h" | ||
51 | #endif | ||
52 | #ifdef LED_MATRIX_ENABLE | ||
53 | # include "led_matrix.h" | ||
54 | #endif | ||
55 | #ifdef RGB_MATRIX_ENABLE | ||
56 | # include "rgb_matrix.h" | ||
57 | #endif | ||
58 | #ifdef ENCODER_ENABLE | ||
59 | # include "encoder.h" | ||
60 | #endif | ||
61 | #ifdef STENO_ENABLE | ||
62 | # include "process_steno.h" | ||
63 | #endif | ||
64 | #ifdef SERIAL_LINK_ENABLE | ||
65 | # include "serial_link/system/serial_link.h" | ||
66 | #endif | ||
67 | #ifdef VISUALIZER_ENABLE | ||
68 | # include "visualizer/visualizer.h" | ||
69 | #endif | ||
70 | #ifdef POINTING_DEVICE_ENABLE | ||
71 | # include "pointing_device.h" | ||
72 | #endif | ||
73 | #ifdef MIDI_ENABLE | ||
74 | # include "process_midi.h" | ||
75 | #endif | ||
76 | #ifdef JOYSTICK_ENABLE | ||
77 | # include "process_joystick.h" | ||
78 | #endif | ||
79 | #ifdef HD44780_ENABLE | ||
80 | # include "hd44780.h" | ||
81 | #endif | ||
82 | #ifdef QWIIC_ENABLE | ||
83 | # include "qwiic.h" | ||
84 | #endif | ||
85 | #ifdef OLED_DRIVER_ENABLE | ||
86 | # include "oled_driver.h" | ||
87 | #endif | ||
88 | #ifdef ST7565_ENABLE | ||
89 | # include "st7565.h" | ||
90 | #endif | ||
91 | #ifdef VELOCIKEY_ENABLE | ||
92 | # include "velocikey.h" | ||
93 | #endif | ||
94 | #ifdef VIA_ENABLE | ||
95 | # include "via.h" | ||
96 | #endif | ||
97 | #ifdef DIP_SWITCH_ENABLE | ||
98 | # include "dip_switch.h" | ||
99 | #endif | ||
100 | #ifdef STM32_EEPROM_ENABLE | ||
101 | # include "eeprom_stm32.h" | ||
102 | #endif | ||
103 | #ifdef EEPROM_DRIVER | ||
104 | # include "eeprom_driver.h" | ||
105 | #endif | ||
106 | #if defined(CRC_ENABLE) | ||
107 | # include "crc.h" | ||
108 | #endif | ||
109 | #ifdef DIGITIZER_ENABLE | ||
110 | # include "digitizer.h" | ||
111 | #endif | ||
112 | |||
113 | static uint32_t last_input_modification_time = 0; | ||
114 | uint32_t last_input_activity_time(void) { return last_input_modification_time; } | ||
115 | uint32_t last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); } | ||
116 | |||
117 | static uint32_t last_matrix_modification_time = 0; | ||
118 | uint32_t last_matrix_activity_time(void) { return last_matrix_modification_time; } | ||
119 | uint32_t last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); } | ||
120 | void last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); } | ||
121 | |||
122 | static uint32_t last_encoder_modification_time = 0; | ||
123 | uint32_t last_encoder_activity_time(void) { return last_encoder_modification_time; } | ||
124 | uint32_t last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); } | ||
125 | void last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); } | ||
126 | |||
127 | // Only enable this if console is enabled to print to | ||
128 | #if defined(DEBUG_MATRIX_SCAN_RATE) | ||
129 | static uint32_t matrix_timer = 0; | ||
130 | static uint32_t matrix_scan_count = 0; | ||
131 | static uint32_t last_matrix_scan_count = 0; | ||
132 | |||
133 | void matrix_scan_perf_task(void) { | ||
134 | matrix_scan_count++; | ||
135 | |||
136 | uint32_t timer_now = timer_read32(); | ||
137 | if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) { | ||
138 | # if defined(CONSOLE_ENABLE) | ||
139 | dprintf("matrix scan frequency: %lu\n", matrix_scan_count); | ||
140 | # endif | ||
141 | last_matrix_scan_count = matrix_scan_count; | ||
142 | matrix_timer = timer_now; | ||
143 | matrix_scan_count = 0; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; } | ||
148 | #else | ||
149 | # define matrix_scan_perf_task() | ||
150 | #endif | ||
151 | |||
152 | #ifdef MATRIX_HAS_GHOST | ||
153 | extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; | ||
154 | static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) { | ||
155 | matrix_row_t out = 0; | ||
156 | for (uint8_t col = 0; col < MATRIX_COLS; col++) { | ||
157 | // read each key in the row data and check if the keymap defines it as a real key | ||
158 | if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) { | ||
159 | // this creates new row data, if a key is defined in the keymap, it will be set here | ||
160 | out |= 1 << col; | ||
161 | } | ||
162 | } | ||
163 | return out; | ||
164 | } | ||
165 | |||
166 | static inline bool popcount_more_than_one(matrix_row_t rowdata) { | ||
167 | rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero | ||
168 | return rowdata; | ||
169 | } | ||
170 | |||
171 | static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) { | ||
172 | /* No ghost exists when less than 2 keys are down on the row. | ||
173 | If there are "active" blanks in the matrix, the key can't be pressed by the user, | ||
174 | there is no doubt as to which keys are really being pressed. | ||
175 | The ghosts will be ignored, they are KC_NO. */ | ||
176 | rowdata = get_real_keys(row, rowdata); | ||
177 | if ((popcount_more_than_one(rowdata)) == 0) { | ||
178 | return false; | ||
179 | } | ||
180 | /* Ghost occurs when the row shares a column line with other row, | ||
181 | and two columns are read on each row. Blanks in the matrix don't matter, | ||
182 | so they are filtered out. | ||
183 | If there are two or more real keys pressed and they match columns with | ||
184 | at least two of another row's real keys, the row will be ignored. Keep in mind, | ||
185 | we are checking one row at a time, not all of them at once. | ||
186 | */ | ||
187 | for (uint8_t i = 0; i < MATRIX_ROWS; i++) { | ||
188 | if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) { | ||
189 | return true; | ||
190 | } | ||
191 | } | ||
192 | return false; | ||
193 | } | ||
194 | |||
195 | #endif | ||
196 | |||
197 | void disable_jtag(void) { | ||
198 | // To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles. | ||
199 | #if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) | ||
200 | MCUCR |= _BV(JTD); | ||
201 | MCUCR |= _BV(JTD); | ||
202 | #elif defined(__AVR_ATmega32A__) | ||
203 | MCUCSR |= _BV(JTD); | ||
204 | MCUCSR |= _BV(JTD); | ||
205 | #endif | ||
206 | } | ||
207 | |||
208 | /** \brief matrix_setup | ||
209 | * | ||
210 | * FIXME: needs doc | ||
211 | */ | ||
212 | __attribute__((weak)) void matrix_setup(void) {} | ||
213 | |||
214 | /** \brief keyboard_pre_init_user | ||
215 | * | ||
216 | * FIXME: needs doc | ||
217 | */ | ||
218 | __attribute__((weak)) void keyboard_pre_init_user(void) {} | ||
219 | |||
220 | /** \brief keyboard_pre_init_kb | ||
221 | * | ||
222 | * FIXME: needs doc | ||
223 | */ | ||
224 | __attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } | ||
225 | |||
226 | /** \brief keyboard_post_init_user | ||
227 | * | ||
228 | * FIXME: needs doc | ||
229 | */ | ||
230 | |||
231 | __attribute__((weak)) void keyboard_post_init_user() {} | ||
232 | |||
233 | /** \brief keyboard_post_init_kb | ||
234 | * | ||
235 | * FIXME: needs doc | ||
236 | */ | ||
237 | |||
238 | __attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); } | ||
239 | |||
240 | /** \brief keyboard_setup | ||
241 | * | ||
242 | * FIXME: needs doc | ||
243 | */ | ||
244 | void keyboard_setup(void) { | ||
245 | #ifndef NO_JTAG_DISABLE | ||
246 | disable_jtag(); | ||
247 | #endif | ||
248 | print_set_sendchar(sendchar); | ||
249 | #ifdef STM32_EEPROM_ENABLE | ||
250 | EEPROM_Init(); | ||
251 | #endif | ||
252 | #ifdef EEPROM_DRIVER | ||
253 | eeprom_driver_init(); | ||
254 | #endif | ||
255 | matrix_setup(); | ||
256 | keyboard_pre_init_kb(); | ||
257 | } | ||
258 | |||
259 | #ifndef SPLIT_KEYBOARD | ||
260 | |||
261 | /** \brief is_keyboard_master | ||
262 | * | ||
263 | * FIXME: needs doc | ||
264 | */ | ||
265 | __attribute__((weak)) bool is_keyboard_master(void) { return true; } | ||
266 | |||
267 | /** \brief is_keyboard_left | ||
268 | * | ||
269 | * FIXME: needs doc | ||
270 | */ | ||
271 | __attribute__((weak)) bool is_keyboard_left(void) { return true; } | ||
272 | |||
273 | #endif | ||
274 | |||
275 | /** \brief should_process_keypress | ||
276 | * | ||
277 | * Override this function if you have a condition where keypresses processing should change: | ||
278 | * - splits where the slave side needs to process for rgb/oled functionality | ||
279 | */ | ||
280 | __attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); } | ||
281 | |||
282 | /** \brief housekeeping_task_kb | ||
283 | * | ||
284 | * Override this function if you have a need to execute code for every keyboard main loop iteration. | ||
285 | * This is specific to keyboard-level functionality. | ||
286 | */ | ||
287 | __attribute__((weak)) void housekeeping_task_kb(void) {} | ||
288 | |||
289 | /** \brief housekeeping_task_user | ||
290 | * | ||
291 | * Override this function if you have a need to execute code for every keyboard main loop iteration. | ||
292 | * This is specific to user/keymap-level functionality. | ||
293 | */ | ||
294 | __attribute__((weak)) void housekeeping_task_user(void) {} | ||
295 | |||
296 | /** \brief housekeeping_task | ||
297 | * | ||
298 | * Invokes hooks for executing code after QMK is done after each loop iteration. | ||
299 | */ | ||
300 | void housekeeping_task(void) { | ||
301 | housekeeping_task_kb(); | ||
302 | housekeeping_task_user(); | ||
303 | } | ||
304 | |||
305 | /** \brief keyboard_init | ||
306 | * | ||
307 | * FIXME: needs doc | ||
308 | */ | ||
309 | void keyboard_init(void) { | ||
310 | timer_init(); | ||
311 | sync_timer_init(); | ||
312 | matrix_init(); | ||
313 | #if defined(CRC_ENABLE) | ||
314 | crc_init(); | ||
315 | #endif | ||
316 | #ifdef VIA_ENABLE | ||
317 | via_init(); | ||
318 | #endif | ||
319 | #ifdef QWIIC_ENABLE | ||
320 | qwiic_init(); | ||
321 | #endif | ||
322 | #ifdef OLED_DRIVER_ENABLE | ||
323 | oled_init(OLED_ROTATION_0); | ||
324 | #endif | ||
325 | #ifdef ST7565_ENABLE | ||
326 | st7565_init(DISPLAY_ROTATION_0); | ||
327 | #endif | ||
328 | #ifdef PS2_MOUSE_ENABLE | ||
329 | ps2_mouse_init(); | ||
330 | #endif | ||
331 | #ifdef SERIAL_MOUSE_ENABLE | ||
332 | serial_mouse_init(); | ||
333 | #endif | ||
334 | #ifdef ADB_MOUSE_ENABLE | ||
335 | adb_mouse_init(); | ||
336 | #endif | ||
337 | #ifdef BACKLIGHT_ENABLE | ||
338 | backlight_init(); | ||
339 | #endif | ||
340 | #ifdef RGBLIGHT_ENABLE | ||
341 | rgblight_init(); | ||
342 | #endif | ||
343 | #ifdef ENCODER_ENABLE | ||
344 | encoder_init(); | ||
345 | #endif | ||
346 | #ifdef STENO_ENABLE | ||
347 | steno_init(); | ||
348 | #endif | ||
349 | #ifdef POINTING_DEVICE_ENABLE | ||
350 | pointing_device_init(); | ||
351 | #endif | ||
352 | #if defined(NKRO_ENABLE) && defined(FORCE_NKRO) | ||
353 | keymap_config.nkro = 1; | ||
354 | eeconfig_update_keymap(keymap_config.raw); | ||
355 | #endif | ||
356 | #ifdef DIP_SWITCH_ENABLE | ||
357 | dip_switch_init(); | ||
358 | #endif | ||
359 | |||
360 | #if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) | ||
361 | debug_enable = true; | ||
362 | #endif | ||
363 | |||
364 | keyboard_post_init_kb(); /* Always keep this last */ | ||
365 | } | ||
366 | |||
367 | /** \brief key_event_task | ||
368 | * | ||
369 | * This function is responsible for calling into other systems when they need to respond to electrical switch press events. | ||
370 | * This is differnet than keycode events as no layer processing, or filtering occurs. | ||
371 | */ | ||
372 | void switch_events(uint8_t row, uint8_t col, bool pressed) { | ||
373 | #if defined(LED_MATRIX_ENABLE) | ||
374 | process_led_matrix(row, col, pressed); | ||
375 | #endif | ||
376 | #if defined(RGB_MATRIX_ENABLE) | ||
377 | process_rgb_matrix(row, col, pressed); | ||
378 | #endif | ||
379 | } | ||
380 | |||
381 | /** \brief Keyboard task: Do keyboard routine jobs | ||
382 | * | ||
383 | * Do routine keyboard jobs: | ||
384 | * | ||
385 | * * scan matrix | ||
386 | * * handle mouse movements | ||
387 | * * run visualizer code | ||
388 | * * handle midi commands | ||
389 | * * light LEDs | ||
390 | * | ||
391 | * This is repeatedly called as fast as possible. | ||
392 | */ | ||
393 | void keyboard_task(void) { | ||
394 | static matrix_row_t matrix_prev[MATRIX_ROWS]; | ||
395 | static uint8_t led_status = 0; | ||
396 | matrix_row_t matrix_row = 0; | ||
397 | matrix_row_t matrix_change = 0; | ||
398 | #ifdef QMK_KEYS_PER_SCAN | ||
399 | uint8_t keys_processed = 0; | ||
400 | #endif | ||
401 | #ifdef ENCODER_ENABLE | ||
402 | bool encoders_changed = false; | ||
403 | #endif | ||
404 | |||
405 | uint8_t matrix_changed = matrix_scan(); | ||
406 | if (matrix_changed) last_matrix_activity_trigger(); | ||
407 | |||
408 | for (uint8_t r = 0; r < MATRIX_ROWS; r++) { | ||
409 | matrix_row = matrix_get_row(r); | ||
410 | matrix_change = matrix_row ^ matrix_prev[r]; | ||
411 | if (matrix_change) { | ||
412 | #ifdef MATRIX_HAS_GHOST | ||
413 | if (has_ghost_in_row(r, matrix_row)) { | ||
414 | continue; | ||
415 | } | ||
416 | #endif | ||
417 | if (debug_matrix) matrix_print(); | ||
418 | matrix_row_t col_mask = 1; | ||
419 | for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) { | ||
420 | if (matrix_change & col_mask) { | ||
421 | if (should_process_keypress()) { | ||
422 | action_exec((keyevent_t){ | ||
423 | .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */ | ||
424 | }); | ||
425 | } | ||
426 | // record a processed key | ||
427 | matrix_prev[r] ^= col_mask; | ||
428 | |||
429 | switch_events(r, c, (matrix_row & col_mask)); | ||
430 | |||
431 | #ifdef QMK_KEYS_PER_SCAN | ||
432 | // only jump out if we have processed "enough" keys. | ||
433 | if (++keys_processed >= QMK_KEYS_PER_SCAN) | ||
434 | #endif | ||
435 | // process a key per task call | ||
436 | goto MATRIX_LOOP_END; | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | } | ||
441 | // call with pseudo tick event when no real key event. | ||
442 | #ifdef QMK_KEYS_PER_SCAN | ||
443 | // we can get here with some keys processed now. | ||
444 | if (!keys_processed) | ||
445 | #endif | ||
446 | action_exec(TICK); | ||
447 | |||
448 | MATRIX_LOOP_END: | ||
449 | |||
450 | #ifdef DEBUG_MATRIX_SCAN_RATE | ||
451 | matrix_scan_perf_task(); | ||
452 | #endif | ||
453 | |||
454 | #if defined(RGBLIGHT_ENABLE) | ||
455 | rgblight_task(); | ||
456 | #endif | ||
457 | |||
458 | #ifdef LED_MATRIX_ENABLE | ||
459 | led_matrix_task(); | ||
460 | #endif | ||
461 | #ifdef RGB_MATRIX_ENABLE | ||
462 | rgb_matrix_task(); | ||
463 | #endif | ||
464 | |||
465 | #if defined(BACKLIGHT_ENABLE) | ||
466 | # if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS) | ||
467 | backlight_task(); | ||
468 | # endif | ||
469 | #endif | ||
470 | |||
471 | #ifdef ENCODER_ENABLE | ||
472 | encoders_changed = encoder_read(); | ||
473 | if (encoders_changed) last_encoder_activity_trigger(); | ||
474 | #endif | ||
475 | |||
476 | #ifdef QWIIC_ENABLE | ||
477 | qwiic_task(); | ||
478 | #endif | ||
479 | |||
480 | #ifdef OLED_DRIVER_ENABLE | ||
481 | oled_task(); | ||
482 | # ifndef OLED_DISABLE_TIMEOUT | ||
483 | // Wake up oled if user is using those fabulous keys or spinning those encoders! | ||
484 | # ifdef ENCODER_ENABLE | ||
485 | if (matrix_changed || encoders_changed) oled_on(); | ||
486 | # else | ||
487 | if (matrix_changed) oled_on(); | ||
488 | # endif | ||
489 | # endif | ||
490 | #endif | ||
491 | |||
492 | #ifdef ST7565_ENABLE | ||
493 | st7565_task(); | ||
494 | # ifndef ST7565_DISABLE_TIMEOUT | ||
495 | // Wake up display if user is using those fabulous keys or spinning those encoders! | ||
496 | # ifdef ENCODER_ENABLE | ||
497 | if (matrix_changed || encoders_changed) st7565_on(); | ||
498 | # else | ||
499 | if (matrix_changed) st7565_on(); | ||
500 | # endif | ||
501 | # endif | ||
502 | #endif | ||
503 | |||
504 | #ifdef MOUSEKEY_ENABLE | ||
505 | // mousekey repeat & acceleration | ||
506 | mousekey_task(); | ||
507 | #endif | ||
508 | |||
509 | #ifdef PS2_MOUSE_ENABLE | ||
510 | ps2_mouse_task(); | ||
511 | #endif | ||
512 | |||
513 | #ifdef SERIAL_MOUSE_ENABLE | ||
514 | serial_mouse_task(); | ||
515 | #endif | ||
516 | |||
517 | #ifdef ADB_MOUSE_ENABLE | ||
518 | adb_mouse_task(); | ||
519 | #endif | ||
520 | |||
521 | #ifdef SERIAL_LINK_ENABLE | ||
522 | serial_link_update(); | ||
523 | #endif | ||
524 | |||
525 | #ifdef VISUALIZER_ENABLE | ||
526 | visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds()); | ||
527 | #endif | ||
528 | |||
529 | #ifdef POINTING_DEVICE_ENABLE | ||
530 | pointing_device_task(); | ||
531 | #endif | ||
532 | |||
533 | #ifdef MIDI_ENABLE | ||
534 | midi_task(); | ||
535 | #endif | ||
536 | |||
537 | #ifdef VELOCIKEY_ENABLE | ||
538 | if (velocikey_enabled()) { | ||
539 | velocikey_decelerate(); | ||
540 | } | ||
541 | #endif | ||
542 | |||
543 | #ifdef JOYSTICK_ENABLE | ||
544 | joystick_task(); | ||
545 | #endif | ||
546 | |||
547 | #ifdef DIGITIZER_ENABLE | ||
548 | digitizer_task(); | ||
549 | #endif | ||
550 | |||
551 | // update LED | ||
552 | if (led_status != host_keyboard_leds()) { | ||
553 | led_status = host_keyboard_leds(); | ||
554 | keyboard_set_leds(led_status); | ||
555 | } | ||
556 | } | ||
557 | |||
558 | /** \brief keyboard set leds | ||
559 | * | ||
560 | * FIXME: needs doc | ||
561 | */ | ||
562 | void keyboard_set_leds(uint8_t leds) { | ||
563 | if (debug_keyboard) { | ||
564 | debug("keyboard_set_led: "); | ||
565 | debug_hex8(leds); | ||
566 | debug("\n"); | ||
567 | } | ||
568 | led_set(leds); | ||
569 | } | ||