qmk_firmware

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

nvm_dynamic_keymap.c (8588B)


      1 // Copyright 2024 Nick Brassel (@tzarc)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 
      4 #include "compiler_support.h"
      5 #include "keycodes.h"
      6 #include "eeprom.h"
      7 #include "dynamic_keymap.h"
      8 #include "nvm_dynamic_keymap.h"
      9 #include "nvm_eeprom_eeconfig_internal.h"
     10 #include "nvm_eeprom_via_internal.h"
     11 
     12 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     13 
     14 #ifdef ENCODER_ENABLE
     15 #    include "encoder.h"
     16 #endif
     17 
     18 #ifdef VIA_ENABLE
     19 #    include "via.h"
     20 #    define DYNAMIC_KEYMAP_EEPROM_START (VIA_EEPROM_CONFIG_END)
     21 #else
     22 #    define DYNAMIC_KEYMAP_EEPROM_START (EECONFIG_SIZE)
     23 #endif
     24 
     25 #ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
     26 #    define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR (TOTAL_EEPROM_BYTE_COUNT - 1)
     27 #endif
     28 
     29 STATIC_ASSERT(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR <= (TOTAL_EEPROM_BYTE_COUNT - 1), "DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is configured to use more space than what is available for the selected EEPROM driver");
     30 
     31 // Due to usage of uint16_t check for max 65535
     32 STATIC_ASSERT(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR <= 65535, "DYNAMIC_KEYMAP_EEPROM_MAX_ADDR must be less than 65536");
     33 
     34 // If DYNAMIC_KEYMAP_EEPROM_ADDR not explicitly defined in config.h,
     35 #ifndef DYNAMIC_KEYMAP_EEPROM_ADDR
     36 #    define DYNAMIC_KEYMAP_EEPROM_ADDR DYNAMIC_KEYMAP_EEPROM_START
     37 #endif
     38 
     39 // Dynamic encoders starts after dynamic keymaps
     40 #ifndef DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR
     41 #    define DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2))
     42 #endif
     43 
     44 // Dynamic macro starts after dynamic encoders, but only when using ENCODER_MAP
     45 #ifdef ENCODER_MAP_ENABLE
     46 #    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
     47 #        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * NUM_ENCODERS * 2 * 2))
     48 #    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
     49 #else      // ENCODER_MAP_ENABLE
     50 #    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
     51 #        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR)
     52 #    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
     53 #endif     // ENCODER_MAP_ENABLE
     54 
     55 // Sanity check that dynamic keymaps fit in available EEPROM
     56 // If there's not 100 bytes available for macros, then something is wrong.
     57 // The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,
     58 // or DYNAMIC_KEYMAP_EEPROM_MAX_ADDR to increase it, *only if* the microcontroller has
     59 // more than the default.
     60 STATIC_ASSERT((int64_t)(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (int64_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) >= 100, "Dynamic keymaps are configured to use more EEPROM than is available.");
     61 
     62 #ifndef TOTAL_EEPROM_BYTE_COUNT
     63 #    error Unknown total EEPROM size. Cannot derive maximum for dynamic keymaps.
     64 #endif
     65 // Dynamic macros are stored after the keymaps and use what is available
     66 // up to and including DYNAMIC_KEYMAP_EEPROM_MAX_ADDR.
     67 #ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE
     68 #    define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + 1)
     69 #endif
     70 
     71 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     72 
     73 void nvm_dynamic_keymap_erase(void) {
     74     // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.
     75 }
     76 
     77 void nvm_dynamic_keymap_macro_erase(void) {
     78     // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.
     79 }
     80 
     81 static inline void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column) {
     82     return ((void *)DYNAMIC_KEYMAP_EEPROM_ADDR) + (layer * MATRIX_ROWS * MATRIX_COLS * 2) + (row * MATRIX_COLS * 2) + (column * 2);
     83 }
     84 
     85 uint16_t nvm_dynamic_keymap_read_keycode(uint8_t layer, uint8_t row, uint8_t column) {
     86     if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return KC_NO;
     87     void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
     88     // Big endian, so we can read/write EEPROM directly from host if we want
     89     uint16_t keycode = eeprom_read_byte(address) << 8;
     90     keycode |= eeprom_read_byte(address + 1);
     91     return keycode;
     92 }
     93 
     94 void nvm_dynamic_keymap_update_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) {
     95     if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return;
     96     void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
     97     // Big endian, so we can read/write EEPROM directly from host if we want
     98     eeprom_update_byte(address, (uint8_t)(keycode >> 8));
     99     eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));
    100 }
    101 
    102 #ifdef ENCODER_MAP_ENABLE
    103 static void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t encoder_id) {
    104     return ((void *)DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) + (layer * NUM_ENCODERS * 2 * 2) + (encoder_id * 2 * 2);
    105 }
    106 
    107 uint16_t nvm_dynamic_keymap_read_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) {
    108     if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return KC_NO;
    109     void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
    110     // Big endian, so we can read/write EEPROM directly from host if we want
    111     uint16_t keycode = ((uint16_t)eeprom_read_byte(address + (clockwise ? 0 : 2))) << 8;
    112     keycode |= eeprom_read_byte(address + (clockwise ? 0 : 2) + 1);
    113     return keycode;
    114 }
    115 
    116 void nvm_dynamic_keymap_update_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) {
    117     if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return;
    118     void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
    119     // Big endian, so we can read/write EEPROM directly from host if we want
    120     eeprom_update_byte(address + (clockwise ? 0 : 2), (uint8_t)(keycode >> 8));
    121     eeprom_update_byte(address + (clockwise ? 0 : 2) + 1, (uint8_t)(keycode & 0xFF));
    122 }
    123 #endif // ENCODER_MAP_ENABLE
    124 
    125 void nvm_dynamic_keymap_read_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
    126     uint32_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
    127     void    *source                     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
    128     uint8_t *target                     = data;
    129     for (uint32_t i = 0; i < size; i++) {
    130         if (offset + i < dynamic_keymap_eeprom_size) {
    131             *target = eeprom_read_byte(source);
    132         } else {
    133             *target = 0x00;
    134         }
    135         source++;
    136         target++;
    137     }
    138 }
    139 
    140 void nvm_dynamic_keymap_update_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
    141     uint32_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
    142     void    *target                     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
    143     uint8_t *source                     = data;
    144     for (uint32_t i = 0; i < size; i++) {
    145         if (offset + i < dynamic_keymap_eeprom_size) {
    146             eeprom_update_byte(target, *source);
    147         }
    148         source++;
    149         target++;
    150     }
    151 }
    152 
    153 uint32_t nvm_dynamic_keymap_macro_size(void) {
    154     return DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE;
    155 }
    156 
    157 void nvm_dynamic_keymap_macro_read_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
    158     void    *source = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
    159     uint8_t *target = data;
    160     for (uint16_t i = 0; i < size; i++) {
    161         if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
    162             *target = eeprom_read_byte(source);
    163         } else {
    164             *target = 0x00;
    165         }
    166         source++;
    167         target++;
    168     }
    169 }
    170 
    171 void nvm_dynamic_keymap_macro_update_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
    172     void    *target = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
    173     uint8_t *source = data;
    174     for (uint16_t i = 0; i < size; i++) {
    175         if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
    176             eeprom_update_byte(target, *source);
    177         }
    178         source++;
    179         target++;
    180     }
    181 }
    182 
    183 void nvm_dynamic_keymap_macro_reset(void) {
    184     void   *start     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
    185     void   *end       = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
    186     long    remaining = end - start;
    187     uint8_t dummy[16] = {0};
    188     for (int i = 0; i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE; i += sizeof(dummy)) {
    189         int this_loop = remaining < sizeof(dummy) ? remaining : sizeof(dummy);
    190         eeprom_update_block(dummy, start, this_loop);
    191         start += this_loop;
    192         remaining -= this_loop;
    193     }
    194 }