Parent directory

via.c

28556 bytes
  1/* Copyright 2019 Jason Williams (Wilba)
  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
 17#ifndef RAW_ENABLE
 18#    error "RAW_ENABLE is not enabled"
 19#endif
 20
 21#ifndef DYNAMIC_KEYMAP_ENABLE
 22#    error "DYNAMIC_KEYMAP_ENABLE is not enabled"
 23#endif
 24
 25#include "via.h"
 26
 27#include "raw_hid.h"
 28#include "dynamic_keymap.h"
 29#include "eeprom.h"
 30#include "eeconfig.h"
 31#include "matrix.h"
 32#include "timer.h"
 33#include "wait.h"
 34#include "version.h" // for QMK_BUILDDATE used in EEPROM magic
 35
 36#if defined(AUDIO_ENABLE)
 37#    include "audio.h"
 38#endif
 39
 40#if defined(BACKLIGHT_ENABLE)
 41#    include "backlight.h"
 42#endif
 43
 44#if defined(RGBLIGHT_ENABLE)
 45#    include "rgblight.h"
 46#endif
 47
 48#if (defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE))
 49#    include <lib/lib8tion/lib8tion.h>
 50#endif
 51
 52#if defined(RGB_MATRIX_ENABLE)
 53#    include "rgb_matrix.h"
 54#endif
 55
 56#if defined(LED_MATRIX_ENABLE)
 57#    include "led_matrix.h"
 58#endif
 59
 60// Can be called in an overriding via_init_kb() to test if keyboard level code usage of
 61// EEPROM is invalid and use/save defaults.
 62bool via_eeprom_is_valid(void) {
 63    char *  p      = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
 64    uint8_t magic0 = ((p[2] & 0x0F) << 4) | (p[3] & 0x0F);
 65    uint8_t magic1 = ((p[5] & 0x0F) << 4) | (p[6] & 0x0F);
 66    uint8_t magic2 = ((p[8] & 0x0F) << 4) | (p[9] & 0x0F);
 67
 68    return (eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 0) == magic0 && eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 1) == magic1 && eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2) == magic2);
 69}
 70
 71// Sets VIA/keyboard level usage of EEPROM to valid/invalid
 72// Keyboard level code (eg. via_init_kb()) should not call this
 73void via_eeprom_set_valid(bool valid) {
 74    char *  p      = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
 75    uint8_t magic0 = ((p[2] & 0x0F) << 4) | (p[3] & 0x0F);
 76    uint8_t magic1 = ((p[5] & 0x0F) << 4) | (p[6] & 0x0F);
 77    uint8_t magic2 = ((p[8] & 0x0F) << 4) | (p[9] & 0x0F);
 78
 79    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 0, valid ? magic0 : 0xFF);
 80    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 1, valid ? magic1 : 0xFF);
 81    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2, valid ? magic2 : 0xFF);
 82}
 83
 84// Override this at the keyboard code level to check
 85// VIA's EEPROM valid state and reset to defaults as needed.
 86// Used by keyboards that store their own state in EEPROM,
 87// for backlight, rotary encoders, etc.
 88// The override should not set via_eeprom_set_valid(true) as
 89// the caller also needs to check the valid state.
 90__attribute__((weak)) void via_init_kb(void) {}
 91
 92// Called by QMK core to initialize dynamic keymaps etc.
 93void via_init(void) {
 94    // Let keyboard level test EEPROM valid state,
 95    // but not set it valid, it is done here.
 96    via_init_kb();
 97    via_set_layout_options_kb(via_get_layout_options());
 98
 99    // If the EEPROM has the magic, the data is good.
100    // OK to load from EEPROM.
101    if (!via_eeprom_is_valid()) {
102        eeconfig_init_via();
103    }
104}
105
106void eeconfig_init_via(void) {
107    // set the magic number to false, in case this gets interrupted
108    via_eeprom_set_valid(false);
109    // This resets the layout options
110    via_set_layout_options(VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT);
111    // This resets the keymaps in EEPROM to what is in flash.
112    dynamic_keymap_reset();
113    // This resets the macros in EEPROM to nothing.
114    dynamic_keymap_macro_reset();
115    // Save the magic number last, in case saving was interrupted
116    via_eeprom_set_valid(true);
117}
118
119// This is generalized so the layout options EEPROM usage can be
120// variable, between 1 and 4 bytes.
121uint32_t via_get_layout_options(void) {
122    uint32_t value = 0;
123    // Start at the most significant byte
124    void *source = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR);
125    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {
126        value = value << 8;
127        value |= eeprom_read_byte(source);
128        source++;
129    }
130    return value;
131}
132
133__attribute__((weak)) void via_set_layout_options_kb(uint32_t value) {}
134
135void via_set_layout_options(uint32_t value) {
136    via_set_layout_options_kb(value);
137    // Start at the least significant byte
138    void *target = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR + VIA_EEPROM_LAYOUT_OPTIONS_SIZE - 1);
139    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {
140        eeprom_update_byte(target, value & 0xFF);
141        value = value >> 8;
142        target--;
143    }
144}
145
146#if defined(AUDIO_ENABLE)
147float via_device_indication_song[][2] = SONG(STARTUP_SOUND);
148#endif // AUDIO_ENABLE
149
150// Used by VIA to tell a device to flash LEDs (or do something else) when that
151// device becomes the active device being configured, on startup or switching
152// between devices. This function will be called six times, at 200ms interval,
153// with an incrementing value starting at zero. Since this function is called
154// an even number of times, it can call a toggle function and leave things in
155// the original state.
156__attribute__((weak)) void via_set_device_indication(uint8_t value) {
157#if defined(BACKLIGHT_ENABLE)
158    backlight_toggle();
159#endif // BACKLIGHT_ENABLE
160#if defined(RGBLIGHT_ENABLE)
161    rgblight_toggle_noeeprom();
162#endif // RGBLIGHT_ENABLE
163#if defined(RGB_MATRIX_ENABLE)
164    rgb_matrix_toggle_noeeprom();
165#endif // RGB_MATRIX_ENABLE
166#if defined(LED_MATRIX_ENABLE)
167    led_matrix_toggle_noeeprom();
168#endif // LED_MATRIX_ENABLE
169#if defined(AUDIO_ENABLE)
170    if (value == 0) {
171        wait_ms(10);
172        PLAY_SONG(via_device_indication_song);
173    }
174#endif // AUDIO_ENABLE
175}
176
177// Called by QMK core to process VIA-specific keycodes.
178bool process_record_via(uint16_t keycode, keyrecord_t *record) {
179    // Handle macros
180    if (record->event.pressed) {
181        if (keycode >= QK_MACRO && keycode <= QK_MACRO_MAX) {
182            uint8_t id = keycode - QK_MACRO;
183            dynamic_keymap_macro_send(id);
184            return false;
185        }
186    }
187
188    return true;
189}
190
191//
192// via_custom_value_command() has the default handling of custom values for Core modules.
193// If a keyboard is using the default Core modules, it does not need to be overridden,
194// the VIA keyboard definition will have matching channel/IDs.
195//
196// If a keyboard has some extra custom values, then via_custom_value_command_kb() can be
197// overridden to handle the extra custom values, leaving via_custom_value_command() to
198// handle the custom values for Core modules.
199//
200// If a keyboard has custom values and code that are overlapping with Core modules,
201// then via_custom_value_command() can be overridden and call the same functions
202// as the default implementation, or do whatever else is required.
203//
204// DO NOT call raw_hid_send() in the override function.
205//
206
207// This is the default handler for "extra" custom values, i.e. keyboard-specific custom values
208// that are not handled by via_custom_value_command().
209__attribute__((weak)) void via_custom_value_command_kb(uint8_t *data, uint8_t length) {
210    // data = [ command_id, channel_id, value_id, value_data ]
211    uint8_t *command_id = &(data[0]);
212    // Return the unhandled state
213    *command_id = id_unhandled;
214}
215
216// This is the default handler for custom value commands.
217// It routes commands with channel IDs to command handlers as such:
218//
219//      id_qmk_backlight_channel    ->  via_qmk_backlight_command()
220//      id_qmk_rgblight_channel     ->  via_qmk_rgblight_command()
221//      id_qmk_rgb_matrix_channel   ->  via_qmk_rgb_matrix_command()
222//      id_qmk_led_matrix_channel   ->  via_qmk_led_matrix_command()
223//      id_qmk_audio_channel        ->  via_qmk_audio_command()
224//
225__attribute__((weak)) void via_custom_value_command(uint8_t *data, uint8_t length) {
226    // data = [ command_id, channel_id, value_id, value_data ]
227    uint8_t *channel_id = &(data[1]);
228
229#if defined(BACKLIGHT_ENABLE)
230    if (*channel_id == id_qmk_backlight_channel) {
231        via_qmk_backlight_command(data, length);
232        return;
233    }
234#endif // BACKLIGHT_ENABLE
235
236#if defined(RGBLIGHT_ENABLE)
237    if (*channel_id == id_qmk_rgblight_channel) {
238        via_qmk_rgblight_command(data, length);
239        return;
240    }
241#endif // RGBLIGHT_ENABLE
242
243#if defined(RGB_MATRIX_ENABLE)
244    if (*channel_id == id_qmk_rgb_matrix_channel) {
245        via_qmk_rgb_matrix_command(data, length);
246        return;
247    }
248#endif // RGB_MATRIX_ENABLE
249
250#if defined(LED_MATRIX_ENABLE)
251    if (*channel_id == id_qmk_led_matrix_channel) {
252        via_qmk_led_matrix_command(data, length);
253        return;
254    }
255#endif // LED_MATRIX_ENABLE
256
257#if defined(AUDIO_ENABLE)
258    if (*channel_id == id_qmk_audio_channel) {
259        via_qmk_audio_command(data, length);
260        return;
261    }
262#endif // AUDIO_ENABLE
263
264    (void)channel_id; // force use of variable
265
266    // If we haven't returned before here, then let the keyboard level code
267    // handle this, if it is overridden, otherwise by default, this will
268    // return the unhandled state.
269    via_custom_value_command_kb(data, length);
270}
271
272// Keyboard level code can override this, but shouldn't need to.
273// Controlling custom features should be done by overriding
274// via_custom_value_command_kb() instead.
275__attribute__((weak)) bool via_command_kb(uint8_t *data, uint8_t length) {
276    return false;
277}
278
279void raw_hid_receive(uint8_t *data, uint8_t length) {
280    uint8_t *command_id   = &(data[0]);
281    uint8_t *command_data = &(data[1]);
282
283    // If via_command_kb() returns true, the command was fully
284    // handled, including calling raw_hid_send()
285    if (via_command_kb(data, length)) {
286        return;
287    }
288
289    switch (*command_id) {
290        case id_get_protocol_version: {
291            command_data[0] = VIA_PROTOCOL_VERSION >> 8;
292            command_data[1] = VIA_PROTOCOL_VERSION & 0xFF;
293            break;
294        }
295        case id_get_keyboard_value: {
296            switch (command_data[0]) {
297                case id_uptime: {
298                    uint32_t value  = timer_read32();
299                    command_data[1] = (value >> 24) & 0xFF;
300                    command_data[2] = (value >> 16) & 0xFF;
301                    command_data[3] = (value >> 8) & 0xFF;
302                    command_data[4] = value & 0xFF;
303                    break;
304                }
305                case id_layout_options: {
306                    uint32_t value  = via_get_layout_options();
307                    command_data[1] = (value >> 24) & 0xFF;
308                    command_data[2] = (value >> 16) & 0xFF;
309                    command_data[3] = (value >> 8) & 0xFF;
310                    command_data[4] = value & 0xFF;
311                    break;
312                }
313                case id_switch_matrix_state: {
314                    uint8_t offset = command_data[1];
315                    uint8_t rows   = 28 / ((MATRIX_COLS + 7) / 8);
316                    uint8_t i      = 2;
317                    for (uint8_t row = 0; row < rows && row + offset < MATRIX_ROWS; row++) {
318                        matrix_row_t value = matrix_get_row(row + offset);
319#if (MATRIX_COLS > 24)
320                        command_data[i++] = (value >> 24) & 0xFF;
321#endif
322#if (MATRIX_COLS > 16)
323                        command_data[i++] = (value >> 16) & 0xFF;
324#endif
325#if (MATRIX_COLS > 8)
326                        command_data[i++] = (value >> 8) & 0xFF;
327#endif
328                        command_data[i++] = value & 0xFF;
329                    }
330                    break;
331                }
332                case id_firmware_version: {
333                    uint32_t value  = VIA_FIRMWARE_VERSION;
334                    command_data[1] = (value >> 24) & 0xFF;
335                    command_data[2] = (value >> 16) & 0xFF;
336                    command_data[3] = (value >> 8) & 0xFF;
337                    command_data[4] = value & 0xFF;
338                    break;
339                }
340                default: {
341                    // The value ID is not known
342                    // Return the unhandled state
343                    *command_id = id_unhandled;
344                    break;
345                }
346            }
347            break;
348        }
349        case id_set_keyboard_value: {
350            switch (command_data[0]) {
351                case id_layout_options: {
352                    uint32_t value = ((uint32_t)command_data[1] << 24) | ((uint32_t)command_data[2] << 16) | ((uint32_t)command_data[3] << 8) | (uint32_t)command_data[4];
353                    via_set_layout_options(value);
354                    break;
355                }
356                case id_device_indication: {
357                    uint8_t value = command_data[1];
358                    via_set_device_indication(value);
359                    break;
360                }
361                default: {
362                    // The value ID is not known
363                    // Return the unhandled state
364                    *command_id = id_unhandled;
365                    break;
366                }
367            }
368            break;
369        }
370        case id_dynamic_keymap_get_keycode: {
371            uint16_t keycode = dynamic_keymap_get_keycode(command_data[0], command_data[1], command_data[2]);
372            command_data[3]  = keycode >> 8;
373            command_data[4]  = keycode & 0xFF;
374            break;
375        }
376        case id_dynamic_keymap_set_keycode: {
377            dynamic_keymap_set_keycode(command_data[0], command_data[1], command_data[2], (command_data[3] << 8) | command_data[4]);
378            break;
379        }
380        case id_dynamic_keymap_reset: {
381            dynamic_keymap_reset();
382            break;
383        }
384        case id_custom_set_value:
385        case id_custom_get_value:
386        case id_custom_save: {
387            via_custom_value_command(data, length);
388            break;
389        }
390#ifdef VIA_EEPROM_ALLOW_RESET
391        case id_eeprom_reset: {
392            via_eeprom_set_valid(false);
393            eeconfig_init_via();
394            break;
395        }
396#endif
397        case id_dynamic_keymap_macro_get_count: {
398            command_data[0] = dynamic_keymap_macro_get_count();
399            break;
400        }
401        case id_dynamic_keymap_macro_get_buffer_size: {
402            uint16_t size   = dynamic_keymap_macro_get_buffer_size();
403            command_data[0] = size >> 8;
404            command_data[1] = size & 0xFF;
405            break;
406        }
407        case id_dynamic_keymap_macro_get_buffer: {
408            uint16_t offset = (command_data[0] << 8) | command_data[1];
409            uint16_t size   = command_data[2]; // size <= 28
410            dynamic_keymap_macro_get_buffer(offset, size, &command_data[3]);
411            break;
412        }
413        case id_dynamic_keymap_macro_set_buffer: {
414            uint16_t offset = (command_data[0] << 8) | command_data[1];
415            uint16_t size   = command_data[2]; // size <= 28
416            dynamic_keymap_macro_set_buffer(offset, size, &command_data[3]);
417            break;
418        }
419        case id_dynamic_keymap_macro_reset: {
420            dynamic_keymap_macro_reset();
421            break;
422        }
423        case id_dynamic_keymap_get_layer_count: {
424            command_data[0] = dynamic_keymap_get_layer_count();
425            break;
426        }
427        case id_dynamic_keymap_get_buffer: {
428            uint16_t offset = (command_data[0] << 8) | command_data[1];
429            uint16_t size   = command_data[2]; // size <= 28
430            dynamic_keymap_get_buffer(offset, size, &command_data[3]);
431            break;
432        }
433        case id_dynamic_keymap_set_buffer: {
434            uint16_t offset = (command_data[0] << 8) | command_data[1];
435            uint16_t size   = command_data[2]; // size <= 28
436            dynamic_keymap_set_buffer(offset, size, &command_data[3]);
437            break;
438        }
439#ifdef ENCODER_MAP_ENABLE
440        case id_dynamic_keymap_get_encoder: {
441            uint16_t keycode = dynamic_keymap_get_encoder(command_data[0], command_data[1], command_data[2] != 0);
442            command_data[3]  = keycode >> 8;
443            command_data[4]  = keycode & 0xFF;
444            break;
445        }
446        case id_dynamic_keymap_set_encoder: {
447            dynamic_keymap_set_encoder(command_data[0], command_data[1], command_data[2] != 0, (command_data[3] << 8) | command_data[4]);
448            break;
449        }
450#endif
451        default: {
452            // The command ID is not known
453            // Return the unhandled state
454            *command_id = id_unhandled;
455            break;
456        }
457    }
458
459    // Return the same buffer, optionally with values changed
460    // (i.e. returning state to the host, or the unhandled state).
461    raw_hid_send(data, length);
462}
463
464#if defined(BACKLIGHT_ENABLE)
465
466void via_qmk_backlight_command(uint8_t *data, uint8_t length) {
467    // data = [ command_id, channel_id, value_id, value_data ]
468    uint8_t *command_id        = &(data[0]);
469    uint8_t *value_id_and_data = &(data[2]);
470
471    switch (*command_id) {
472        case id_custom_set_value: {
473            via_qmk_backlight_set_value(value_id_and_data);
474            break;
475        }
476        case id_custom_get_value: {
477            via_qmk_backlight_get_value(value_id_and_data);
478            break;
479        }
480        case id_custom_save: {
481            via_qmk_backlight_save();
482            break;
483        }
484        default: {
485            *command_id = id_unhandled;
486            break;
487        }
488    }
489}
490
491#    if BACKLIGHT_LEVELS == 0
492#        error BACKLIGHT_LEVELS == 0
493#    endif
494
495void via_qmk_backlight_get_value(uint8_t *data) {
496    // data = [ value_id, value_data ]
497    uint8_t *value_id   = &(data[0]);
498    uint8_t *value_data = &(data[1]);
499    switch (*value_id) {
500        case id_qmk_backlight_brightness: {
501            // level / BACKLIGHT_LEVELS * 255
502            value_data[0] = ((uint16_t)get_backlight_level() * UINT8_MAX) / BACKLIGHT_LEVELS;
503            break;
504        }
505        case id_qmk_backlight_effect: {
506#    ifdef BACKLIGHT_BREATHING
507            value_data[0] = is_backlight_breathing() ? 1 : 0;
508#    else
509            value_data[0] = 0;
510#    endif
511            break;
512        }
513    }
514}
515
516void via_qmk_backlight_set_value(uint8_t *data) {
517    // data = [ value_id, value_data ]
518    uint8_t *value_id   = &(data[0]);
519    uint8_t *value_data = &(data[1]);
520    switch (*value_id) {
521        case id_qmk_backlight_brightness: {
522            // level / 255 * BACKLIGHT_LEVELS
523            backlight_level_noeeprom(((uint16_t)value_data[0] * BACKLIGHT_LEVELS) / UINT8_MAX);
524            break;
525        }
526        case id_qmk_backlight_effect: {
527#    ifdef BACKLIGHT_BREATHING
528            if (value_data[0] == 0) {
529                backlight_disable_breathing();
530            } else {
531                backlight_enable_breathing();
532            }
533#    endif
534            break;
535        }
536    }
537}
538
539void via_qmk_backlight_save(void) {
540    eeconfig_update_backlight_current();
541}
542
543#endif // BACKLIGHT_ENABLE
544
545#if defined(RGBLIGHT_ENABLE)
546#    ifndef RGBLIGHT_LIMIT_VAL
547#        define RGBLIGHT_LIMIT_VAL 255
548#    endif
549
550void via_qmk_rgblight_command(uint8_t *data, uint8_t length) {
551    // data = [ command_id, channel_id, value_id, value_data ]
552    uint8_t *command_id        = &(data[0]);
553    uint8_t *value_id_and_data = &(data[2]);
554
555    switch (*command_id) {
556        case id_custom_set_value: {
557            via_qmk_rgblight_set_value(value_id_and_data);
558            break;
559        }
560        case id_custom_get_value: {
561            via_qmk_rgblight_get_value(value_id_and_data);
562            break;
563        }
564        case id_custom_save: {
565            via_qmk_rgblight_save();
566            break;
567        }
568        default: {
569            *command_id = id_unhandled;
570            break;
571        }
572    }
573}
574
575void via_qmk_rgblight_get_value(uint8_t *data) {
576    // data = [ value_id, value_data ]
577    uint8_t *value_id   = &(data[0]);
578    uint8_t *value_data = &(data[1]);
579    switch (*value_id) {
580        case id_qmk_rgblight_brightness: {
581            value_data[0] = ((uint16_t)rgblight_get_val() * UINT8_MAX) / RGBLIGHT_LIMIT_VAL;
582            break;
583        }
584        case id_qmk_rgblight_effect: {
585            value_data[0] = rgblight_is_enabled() ? rgblight_get_mode() : 0;
586            break;
587        }
588        case id_qmk_rgblight_effect_speed: {
589            value_data[0] = rgblight_get_speed();
590            break;
591        }
592        case id_qmk_rgblight_color: {
593            value_data[0] = rgblight_get_hue();
594            value_data[1] = rgblight_get_sat();
595            break;
596        }
597    }
598}
599
600void via_qmk_rgblight_set_value(uint8_t *data) {
601    // data = [ value_id, value_data ]
602    uint8_t *value_id   = &(data[0]);
603    uint8_t *value_data = &(data[1]);
604    switch (*value_id) {
605        case id_qmk_rgblight_brightness: {
606            rgblight_sethsv_noeeprom(rgblight_get_hue(), rgblight_get_sat(), ((uint16_t)value_data[0] * RGBLIGHT_LIMIT_VAL) / UINT8_MAX);
607            break;
608        }
609        case id_qmk_rgblight_effect: {
610            if (value_data[0] == 0) {
611                rgblight_disable_noeeprom();
612            } else {
613                rgblight_enable_noeeprom();
614                rgblight_mode_noeeprom(value_data[0]);
615            }
616            break;
617        }
618        case id_qmk_rgblight_effect_speed: {
619            rgblight_set_speed_noeeprom(value_data[0]);
620            break;
621        }
622        case id_qmk_rgblight_color: {
623            rgblight_sethsv_noeeprom(value_data[0], value_data[1], rgblight_get_val());
624            break;
625        }
626    }
627}
628
629void via_qmk_rgblight_save(void) {
630    eeconfig_update_rgblight_current();
631}
632
633#endif // QMK_RGBLIGHT_ENABLE
634
635#if defined(RGB_MATRIX_ENABLE)
636
637void via_qmk_rgb_matrix_command(uint8_t *data, uint8_t length) {
638    // data = [ command_id, channel_id, value_id, value_data ]
639    uint8_t *command_id        = &(data[0]);
640    uint8_t *value_id_and_data = &(data[2]);
641
642    switch (*command_id) {
643        case id_custom_set_value: {
644            via_qmk_rgb_matrix_set_value(value_id_and_data);
645            break;
646        }
647        case id_custom_get_value: {
648            via_qmk_rgb_matrix_get_value(value_id_and_data);
649            break;
650        }
651        case id_custom_save: {
652            via_qmk_rgb_matrix_save();
653            break;
654        }
655        default: {
656            *command_id = id_unhandled;
657            break;
658        }
659    }
660}
661
662void via_qmk_rgb_matrix_get_value(uint8_t *data) {
663    // data = [ value_id, value_data ]
664    uint8_t *value_id   = &(data[0]);
665    uint8_t *value_data = &(data[1]);
666
667    switch (*value_id) {
668        case id_qmk_rgb_matrix_brightness: {
669            value_data[0] = ((uint16_t)rgb_matrix_get_val() * UINT8_MAX) / RGB_MATRIX_MAXIMUM_BRIGHTNESS;
670            break;
671        }
672        case id_qmk_rgb_matrix_effect: {
673            value_data[0] = rgb_matrix_is_enabled() ? rgb_matrix_get_mode() : 0;
674            break;
675        }
676        case id_qmk_rgb_matrix_effect_speed: {
677            value_data[0] = rgb_matrix_get_speed();
678            break;
679        }
680        case id_qmk_rgb_matrix_color: {
681            value_data[0] = rgb_matrix_get_hue();
682            value_data[1] = rgb_matrix_get_sat();
683            break;
684        }
685    }
686}
687
688void via_qmk_rgb_matrix_set_value(uint8_t *data) {
689    // data = [ value_id, value_data ]
690    uint8_t *value_id   = &(data[0]);
691    uint8_t *value_data = &(data[1]);
692    switch (*value_id) {
693        case id_qmk_rgb_matrix_brightness: {
694            rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), scale8(value_data[0], RGB_MATRIX_MAXIMUM_BRIGHTNESS));
695            break;
696        }
697        case id_qmk_rgb_matrix_effect: {
698            if (value_data[0] == 0) {
699                rgb_matrix_disable_noeeprom();
700            } else {
701                rgb_matrix_enable_noeeprom();
702                rgb_matrix_mode_noeeprom(value_data[0]);
703            }
704            break;
705        }
706        case id_qmk_rgb_matrix_effect_speed: {
707            rgb_matrix_set_speed_noeeprom(value_data[0]);
708            break;
709        }
710        case id_qmk_rgb_matrix_color: {
711            rgb_matrix_sethsv_noeeprom(value_data[0], value_data[1], rgb_matrix_get_val());
712            break;
713        }
714    }
715}
716
717void via_qmk_rgb_matrix_save(void) {
718    eeconfig_update_rgb_matrix();
719}
720
721#endif // RGB_MATRIX_ENABLE
722
723#if defined(LED_MATRIX_ENABLE)
724
725void via_qmk_led_matrix_command(uint8_t *data, uint8_t length) {
726    // data = [ command_id, channel_id, value_id, value_data ]
727    uint8_t *command_id        = &(data[0]);
728    uint8_t *value_id_and_data = &(data[2]);
729
730    switch (*command_id) {
731        case id_custom_set_value: {
732            via_qmk_led_matrix_set_value(value_id_and_data);
733            break;
734        }
735        case id_custom_get_value: {
736            via_qmk_led_matrix_get_value(value_id_and_data);
737            break;
738        }
739        case id_custom_save: {
740            via_qmk_led_matrix_save();
741            break;
742        }
743        default: {
744            *command_id = id_unhandled;
745            break;
746        }
747    }
748}
749
750void via_qmk_led_matrix_get_value(uint8_t *data) {
751    // data = [ value_id, value_data ]
752    uint8_t *value_id   = &(data[0]);
753    uint8_t *value_data = &(data[1]);
754
755    switch (*value_id) {
756        case id_qmk_led_matrix_brightness: {
757            value_data[0] = ((uint16_t)led_matrix_get_val() * UINT8_MAX) / LED_MATRIX_MAXIMUM_BRIGHTNESS;
758            break;
759        }
760        case id_qmk_led_matrix_effect: {
761            value_data[0] = led_matrix_is_enabled() ? led_matrix_get_mode() : 0;
762            break;
763        }
764        case id_qmk_led_matrix_effect_speed: {
765            value_data[0] = led_matrix_get_speed();
766            break;
767        }
768    }
769}
770
771void via_qmk_led_matrix_set_value(uint8_t *data) {
772    // data = [ value_id, value_data ]
773    uint8_t *value_id   = &(data[0]);
774    uint8_t *value_data = &(data[1]);
775    switch (*value_id) {
776        case id_qmk_led_matrix_brightness: {
777            led_matrix_set_val_noeeprom(scale8(value_data[0], LED_MATRIX_MAXIMUM_BRIGHTNESS));
778            break;
779        }
780        case id_qmk_led_matrix_effect: {
781            if (value_data[0] == 0) {
782                led_matrix_disable_noeeprom();
783            } else {
784                led_matrix_enable_noeeprom();
785                led_matrix_mode_noeeprom(value_data[0]);
786            }
787            break;
788        }
789        case id_qmk_led_matrix_effect_speed: {
790            led_matrix_set_speed_noeeprom(value_data[0]);
791            break;
792        }
793    }
794}
795
796void via_qmk_led_matrix_save(void) {
797    eeconfig_update_led_matrix();
798}
799
800#endif // LED_MATRIX_ENABLE
801
802#if defined(AUDIO_ENABLE)
803
804extern audio_config_t audio_config;
805
806void via_qmk_audio_command(uint8_t *data, uint8_t length) {
807    // data = [ command_id, channel_id, value_id, value_data ]
808    uint8_t *command_id        = &(data[0]);
809    uint8_t *value_id_and_data = &(data[2]);
810
811    switch (*command_id) {
812        case id_custom_set_value: {
813            via_qmk_audio_set_value(value_id_and_data);
814            break;
815        }
816        case id_custom_get_value: {
817            via_qmk_audio_get_value(value_id_and_data);
818            break;
819        }
820        case id_custom_save: {
821            via_qmk_audio_save();
822            break;
823        }
824        default: {
825            *command_id = id_unhandled;
826            break;
827        }
828    }
829}
830
831void via_qmk_audio_get_value(uint8_t *data) {
832    // data = [ value_id, value_data ]
833    uint8_t *value_id   = &(data[0]);
834    uint8_t *value_data = &(data[1]);
835    switch (*value_id) {
836        case id_qmk_audio_enable: {
837            value_data[0] = audio_config.enable ? 1 : 0;
838            break;
839        }
840        case id_qmk_audio_clicky_enable: {
841            value_data[0] = audio_config.clicky_enable ? 1 : 0;
842            break;
843        }
844    }
845}
846
847void via_qmk_audio_set_value(uint8_t *data) {
848    // data = [ value_id, value_data ]
849    uint8_t *value_id   = &(data[0]);
850    uint8_t *value_data = &(data[1]);
851    switch (*value_id) {
852        case id_qmk_audio_enable: {
853            audio_config.enable = value_data[0] ? 1 : 0;
854            break;
855        }
856        case id_qmk_audio_clicky_enable: {
857            audio_config.clicky_enable = value_data[0] ? 1 : 0;
858            break;
859        }
860    }
861}
862
863void via_qmk_audio_save(void) {
864    eeconfig_update_audio(audio_config.raw);
865}
866
867#endif // QMK_AUDIO_ENABLE