Browse Source

Non-volatile memory data repository pattern (#24356)

* First batch of eeconfig conversions.

* Offset and length for datablocks.

* `via`, `dynamic_keymap`.

* Fix filename.

* Commentary.

* wilba leds

* satisfaction75

* satisfaction75

* more keyboard whack-a-mole

* satisfaction75

* omnikeyish

* more whack-a-mole

* `generic_features.mk` to automatically pick up nvm repositories

* thievery

* deferred variable resolve

* whitespace

* convert api to structs/unions

* convert api to structs/unions

* convert api to structs/unions

* fixups

* code-side docs

* code size fix

* rollback

* nvm_xxxxx_erase

* Updated location of eeconfig magic numbers so non-EEPROM nvm drivers can use them too.

* Fixup build.

* Fixup compilation error with encoders.

* Build fixes.

* Add `via_ci` keymap to onekey to exercise VIA bindings (and thus dynamic keymap et.al.), fixup compilation errors based on preprocessor+sizeof.

* Build failure rectification.
Nick Brassel 1 year ago
parent
commit
2b00b846dc
87 changed files with 1470 additions and 845 deletions
  1. 2 0
      builddefs/common_features.mk
  2. 1 0
      builddefs/generic_features.mk
  3. 1 1
      drivers/eeprom/eeprom_transient.h
  4. 1 1
      keyboards/akko/5087/5087.c
  5. 38 67
      keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.c
  6. 8 3
      keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.h
  7. 11 6
      keyboards/cannonkeys/lib/satisfaction75/satisfaction_encoder.c
  8. 1 1
      keyboards/cannonkeys/lib/satisfaction75/satisfaction_oled.c
  9. 4 1
      keyboards/cannonkeys/satisfaction75/config.h
  10. 5 0
      keyboards/cannonkeys/satisfaction75_hs/config.h
  11. 2 2
      keyboards/cipulot/common/ec_board.c
  12. 0 26
      keyboards/cipulot/common/eeprom_tools.h
  13. 3 4
      keyboards/cipulot/common/via_ec.c
  14. 2 2
      keyboards/cipulot/ec_980c/ec_980c.c
  15. 2 2
      keyboards/cipulot/ec_typek/ec_typek.c
  16. 2 2
      keyboards/contra/keymaps/default/keymap.c
  17. 2 2
      keyboards/dm9records/plaid/keymaps/default/keymap.c
  18. 4 0
      keyboards/handwired/onekey/keymaps/via_ci/config.h
  19. 30 0
      keyboards/handwired/onekey/keymaps/via_ci/keymap.c
  20. 7 0
      keyboards/handwired/onekey/keymaps/via_ci/keymap.json
  21. 1 0
      keyboards/handwired/onekey/teensy_32/rules.mk
  22. 1 1
      keyboards/handwired/onekey/teensy_lc/rules.mk
  23. 2 2
      keyboards/handwired/ortho_brass/keymaps/default/keymap.c
  24. 1 1
      keyboards/helix/rev2/rev2.c
  25. 1 1
      keyboards/helix/rev3_4rows/rev3_4rows.c
  26. 1 1
      keyboards/helix/rev3_5rows/rev3_5rows.c
  27. 2 1
      keyboards/horrortroll/nyx/rev1/lib/startup_swirl_anim.h
  28. 1 1
      keyboards/inland/kb83/kb83.c
  29. 2 2
      keyboards/jian/keymaps/advanced/keymap.c
  30. 1 1
      keyboards/monsgeek/m1/m1.c
  31. 1 1
      keyboards/monsgeek/m3/m3.c
  32. 1 0
      keyboards/omnikeyish/dynamic_macro.c
  33. 2 2
      keyboards/planck/keymaps/default/keymap.c
  34. 2 2
      keyboards/planck/rev7/keymaps/default/keymap.c
  35. 1 0
      keyboards/rocketboard_16/keycode_lookup.c
  36. 2 2
      keyboards/splitography/keymaps/default/keymap.c
  37. 2 2
      keyboards/splitography/keymaps/default_with_ctl_shft_alt_switched/keymap.c
  38. 2 2
      keyboards/splitography/keymaps/dvorak/keymap.c
  39. 2 1
      keyboards/system76/launch_1/launch_1.c
  40. 1 1
      keyboards/vitamins_included/rev2/rev2.c
  41. 7 52
      keyboards/wilba_tech/wt_main.c
  42. 2 0
      keyboards/wilba_tech/wt_mono_backlight.c
  43. 2 0
      keyboards/wilba_tech/wt_rgb_backlight.c
  44. 1 1
      keyboards/xelus/la_plus/rgb_matrix_kb.inc
  45. 1 1
      quantum/action_util.c
  46. 6 6
      quantum/audio/audio.c
  47. 1 1
      quantum/audio/audio.h
  48. 11 20
      quantum/backlight/backlight.c
  49. 3 5
      quantum/backlight/backlight.h
  50. 3 3
      quantum/command.c
  51. 38 165
      quantum/dynamic_keymap.c
  52. 8 1
      quantum/dynamic_keymap.h
  53. 211 233
      quantum/eeconfig.c
  54. 71 85
      quantum/eeconfig.h
  55. 13 13
      quantum/haptic.c
  56. 1 1
      quantum/haptic.h
  57. 3 3
      quantum/keyboard.c
  58. 1 1
      quantum/keycode_config.h
  59. 2 2
      quantum/led_matrix/led_matrix.c
  60. 1 1
      quantum/led_matrix/led_matrix.h
  61. 1 1
      quantum/led_matrix/led_matrix_types.h
  62. 1 1
      quantum/logging/debug.h
  63. 193 0
      quantum/nvm/eeprom/nvm_dynamic_keymap.c
  64. 292 0
      quantum/nvm/eeprom/nvm_eeconfig.c
  65. 59 0
      quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h
  66. 22 0
      quantum/nvm/eeprom/nvm_eeprom_via_internal.h
  67. 77 0
      quantum/nvm/eeprom/nvm_via.c
  68. 27 0
      quantum/nvm/nvm_dynamic_keymap.h
  69. 105 0
      quantum/nvm/nvm_eeconfig.h
  70. 17 0
      quantum/nvm/nvm_via.h
  71. 30 0
      quantum/nvm/readme.md
  72. 31 0
      quantum/nvm/rules.mk
  73. 3 0
      quantum/os_detection.h
  74. 3 3
      quantum/process_keycode/process_autocorrect.c
  75. 3 3
      quantum/process_keycode/process_clicky.c
  76. 2 2
      quantum/process_keycode/process_magic.c
  77. 2 5
      quantum/process_keycode/process_steno.c
  78. 2 2
      quantum/rgb_matrix/rgb_matrix.c
  79. 2 2
      quantum/rgb_matrix/rgb_matrix.h
  80. 1 1
      quantum/rgb_matrix/rgb_matrix_types.h
  81. 14 31
      quantum/rgblight/rgblight.c
  82. 1 4
      quantum/rgblight/rgblight.h
  83. 2 2
      quantum/unicode/unicode.c
  84. 1 1
      quantum/unicode/unicode.h
  85. 32 28
      quantum/via.c
  86. 5 20
      quantum/via.h
  87. 1 1
      tests/test_common/test_fixture.cpp

+ 2 - 0
builddefs/common_features.mk

@@ -30,6 +30,8 @@ QUANTUM_SRC += \
     $(QUANTUM_DIR)/logging/sendchar.c \
     $(QUANTUM_DIR)/process_keycode/process_default_layer.c \
 
+include $(QUANTUM_DIR)/nvm/rules.mk
+
 VPATH += $(QUANTUM_DIR)/logging
 # Fall back to lib/printf if there is no platform provided print
 ifeq ("$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/printf.mk)","")

+ 1 - 0
builddefs/generic_features.mk

@@ -61,6 +61,7 @@ define HANDLE_GENERIC_FEATURE
     SRC += $$(wildcard $$(QUANTUM_DIR)/process_keycode/process_$2.c)
     SRC += $$(wildcard $$(QUANTUM_DIR)/$2/$2.c)
     SRC += $$(wildcard $$(QUANTUM_DIR)/$2.c)
+    SRC += $$(wildcard $$(QUANTUM_DIR)/nvm/$$(NVM_DRIVER_LOWER)/nvm_$2.c)
     VPATH += $$(wildcard $$(QUANTUM_DIR)/$2/)
     OPT_DEFS += -D$1_ENABLE
 endef

+ 1 - 1
drivers/eeprom/eeprom_transient.h

@@ -20,6 +20,6 @@
     The size of the transient EEPROM buffer size.
 */
 #ifndef TRANSIENT_EEPROM_SIZE
-#    include "eeconfig.h"
+#    include "nvm_eeprom_eeconfig_internal.h"
 #    define TRANSIENT_EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO
 #endif

+ 1 - 1
keyboards/akko/5087/5087.c

@@ -164,7 +164,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) {
             if (record->event.pressed) {
                 set_single_persistent_default_layer(MAC_B);
                 keymap_config.no_gui = 0;
-                eeconfig_update_keymap(keymap_config.raw);
+                eeconfig_update_keymap(&keymap_config);
             }
             return false;
         case QK_RGB_MATRIX_TOGGLE:

+ 38 - 67
keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.c

@@ -53,10 +53,26 @@ void board_init(void) {
   SYSCFG->CFGR1 &= ~(SYSCFG_CFGR1_SPI2_DMA_RMP);
 }
 
+uint32_t read_custom_config(void *data, uint32_t offset, uint32_t length) {
+#ifdef VIA_ENABLE
+    return via_read_custom_config(data, offset, length);
+#else
+    return eeconfig_read_kb_datablock(data, offset, length);
+#endif
+}
+
+uint32_t write_custom_config(const void *data, uint32_t offset, uint32_t length) {
+#ifdef VIA_ENABLE
+    return via_update_custom_config(data, offset, length);
+#else
+    return eeconfig_update_kb_datablock(data, offset, length);
+#endif
+}
+
 void keyboard_post_init_kb(void) {
       /*
         This is a workaround to some really weird behavior
-        Without this code, the OLED will turn on, but not when you initially plug the keyboard in. 
+        Without this code, the OLED will turn on, but not when you initially plug the keyboard in.
         You have to manually trigger a user reset to get the OLED to initialize properly
         I'm not sure what the root cause is at this time, but this workaround fixes it.
     */
@@ -74,11 +90,11 @@ void keyboard_post_init_kb(void) {
 void custom_set_value(uint8_t *data) {
     uint8_t *value_id = &(data[0]);
     uint8_t *value_data = &(data[1]);
-    
+
     switch ( *value_id ) {
         case id_oled_default_mode:
         {
-            eeprom_update_byte((uint8_t*)EEPROM_DEFAULT_OLED, value_data[0]);
+            write_custom_config(&value_data[0], EEPROM_DEFAULT_OLED_OFFSET, 1);
             break;
         }
         case id_oled_mode:
@@ -92,7 +108,7 @@ void custom_set_value(uint8_t *data) {
             uint8_t index = value_data[0];
             uint8_t enable = value_data[1];
             enabled_encoder_modes = (enabled_encoder_modes & ~(1<<index)) | (enable<<index);
-            eeprom_update_byte((uint8_t*)EEPROM_ENABLED_ENCODER_MODES, enabled_encoder_modes);
+            write_custom_config(&enabled_encoder_modes, EEPROM_ENABLED_ENCODER_MODES_OFFSET, 1);
             break;
         }
         case id_encoder_custom:
@@ -109,11 +125,12 @@ void custom_set_value(uint8_t *data) {
 void custom_get_value(uint8_t *data) {
     uint8_t *value_id = &(data[0]);
     uint8_t *value_data = &(data[1]);
-    
+
     switch ( *value_id ) {
         case id_oled_default_mode:
         {
-            uint8_t default_oled = eeprom_read_byte((uint8_t*)EEPROM_DEFAULT_OLED);
+            uint8_t default_oled;
+            read_custom_config(&default_oled, EEPROM_DEFAULT_OLED_OFFSET, 1);
             value_data[0] = default_oled;
             break;
         }
@@ -179,7 +196,6 @@ void via_custom_value_command_kb(uint8_t *data, uint8_t length) {
 }
 #endif
 
-
 void read_host_led_state(void) {
   led_t led_state = host_keyboard_led_state();
   if (led_state.num_lock)    {
@@ -290,25 +306,25 @@ bool encoder_update_kb(uint8_t index, bool clockwise) {
 }
 
 void custom_config_reset(void){
-  void *p = (void*)(VIA_EEPROM_CUSTOM_CONFIG_ADDR);
-  void *end = (void*)(VIA_EEPROM_CUSTOM_CONFIG_ADDR+VIA_EEPROM_CUSTOM_CONFIG_SIZE);
-  while ( p != end ) {
-    eeprom_update_byte(p, 0);
-    ++p;
+  for(int i = 0; i < VIA_EEPROM_CUSTOM_CONFIG_SIZE; ++i) {
+    uint8_t dummy = 0;
+    write_custom_config(&dummy, i, 1);
   }
-  eeprom_update_byte((uint8_t*)EEPROM_ENABLED_ENCODER_MODES, 0x1F);
+
+  uint8_t encoder_modes = 0x1F;
+  write_custom_config(&encoder_modes, EEPROM_ENABLED_ENCODER_MODES_OFFSET, 1);
 }
 
 void custom_config_load(void){
 #ifdef DYNAMIC_KEYMAP_ENABLE
-  oled_mode = eeprom_read_byte((uint8_t*)EEPROM_DEFAULT_OLED);
-  enabled_encoder_modes = eeprom_read_byte((uint8_t*)EEPROM_ENABLED_ENCODER_MODES);
+  read_custom_config(&oled_mode, EEPROM_DEFAULT_OLED_OFFSET, 1);
+  read_custom_config(&enabled_encoder_modes, EEPROM_ENABLED_ENCODER_MODES_OFFSET, 1);
 #endif
 }
 
 // Called from via_init() if VIA_ENABLE
 // Called from matrix_init_kb() if not VIA_ENABLE
-void via_init_kb(void)
+void satisfaction_core_init(void)
 {
   // This checks both an EEPROM reset (from bootmagic lite, keycodes)
   // and also firmware build date (from via_eeprom_is_valid())
@@ -326,8 +342,7 @@ void via_init_kb(void)
 void matrix_init_kb(void)
 {
 #ifndef VIA_ENABLE
-  via_init_kb();
-  via_eeprom_set_valid(true);
+  satisfaction_core_init();
 #endif // VIA_ENABLE
 
   rtcGetTime(&RTCD1, &last_timespec);
@@ -335,6 +350,11 @@ void matrix_init_kb(void)
   oled_request_wakeup();
 }
 
+#ifdef VIA_ENABLE
+void via_init_kb(void) {
+    satisfaction_core_init();
+}
+#endif // VIA_ENABLE
 
 void housekeeping_task_kb(void) {
   rtcGetTime(&RTCD1, &last_timespec);
@@ -345,52 +365,3 @@ void housekeeping_task_kb(void) {
     oled_request_repaint();
   }
 }
-
-//
-// In the case of VIA being disabled, we still need to check if
-// keyboard level EEPROM memory is valid before loading.
-// Thus these are copies of the same functions in VIA, since
-// the backlight settings reuse VIA's EEPROM magic/version,
-// and the ones in via.c won't be compiled in.
-//
-// Yes, this is sub-optimal, and is only here for completeness
-// (i.e. catering to the 1% of people that want wilba.tech LED bling
-// AND want persistent settings BUT DON'T want to use dynamic keymaps/VIA).
-//
-#ifndef VIA_ENABLE
-
-bool via_eeprom_is_valid(void)
-{
-    char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
-    uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3]  & 0x0F );
-    uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6]  & 0x0F );
-    uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9]  & 0x0F );
-
-    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 );
-}
-
-// Sets VIA/keyboard level usage of EEPROM to valid/invalid
-// Keyboard level code (eg. via_init_kb()) should not call this
-void via_eeprom_set_valid(bool valid)
-{
-    char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
-    uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3]  & 0x0F );
-    uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6]  & 0x0F );
-    uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9]  & 0x0F );
-
-    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0, valid ? magic0 : 0xFF);
-    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1, valid ? magic1 : 0xFF);
-    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2, valid ? magic2 : 0xFF);
-}
-
-void via_eeprom_reset(void)
-{
-    // Set the VIA specific EEPROM state as invalid.
-    via_eeprom_set_valid(false);
-    // Set the TMK/QMK EEPROM state as invalid.
-    eeconfig_disable();
-}
-
-#endif // VIA_ENABLE

+ 8 - 3
keyboards/cannonkeys/lib/satisfaction75/satisfaction_core.h

@@ -6,12 +6,14 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#include <hal.h>
+
 #include "via.h" // only for EEPROM address
 #include "satisfaction_keycodes.h"
 
-#define EEPROM_ENABLED_ENCODER_MODES (VIA_EEPROM_CUSTOM_CONFIG_ADDR)
-#define EEPROM_DEFAULT_OLED (VIA_EEPROM_CUSTOM_CONFIG_ADDR+1)
-#define EEPROM_CUSTOM_ENCODER (VIA_EEPROM_CUSTOM_CONFIG_ADDR+2)
+#define EEPROM_ENABLED_ENCODER_MODES_OFFSET 0
+#define EEPROM_DEFAULT_OLED_OFFSET 1
+#define EEPROM_CUSTOM_ENCODER_OFFSET 2
 
 enum s75_keyboard_value_id {
   id_encoder_modes = 1,
@@ -94,3 +96,6 @@ void oled_request_repaint(void);
 bool oled_task_needs_to_repaint(void);
 
 void custom_config_load(void);
+
+uint32_t read_custom_config(void *data, uint32_t offset, uint32_t length);
+uint32_t write_custom_config(const void *data, uint32_t offset, uint32_t length);

+ 11 - 6
keyboards/cannonkeys/lib/satisfaction75/satisfaction_encoder.c

@@ -215,10 +215,13 @@ uint16_t handle_encoder_press(void){
 
 uint16_t retrieve_custom_encoder_config(uint8_t encoder_idx, uint8_t behavior){
 #ifdef DYNAMIC_KEYMAP_ENABLE
-    void* addr = (void*)(EEPROM_CUSTOM_ENCODER + (encoder_idx * 6) + (behavior * 2));
+    uint32_t offset = EEPROM_CUSTOM_ENCODER_OFFSET + (encoder_idx * 6) + (behavior * 2);
     //big endian
-    uint16_t keycode = eeprom_read_byte(addr) << 8;
-    keycode |= eeprom_read_byte(addr + 1);
+    uint8_t hi, lo;
+    read_custom_config(&hi, offset+0, 1);
+    read_custom_config(&lo, offset+1, 1);
+    uint16_t keycode = hi << 8;
+    keycode |= lo;
     return keycode;
 #else
     return 0;
@@ -227,8 +230,10 @@ uint16_t retrieve_custom_encoder_config(uint8_t encoder_idx, uint8_t behavior){
 
 void set_custom_encoder_config(uint8_t encoder_idx, uint8_t behavior, uint16_t new_code){
 #ifdef DYNAMIC_KEYMAP_ENABLE
-    void* addr = (void*)(EEPROM_CUSTOM_ENCODER + (encoder_idx * 6) + (behavior * 2));
-    eeprom_update_byte(addr, (uint8_t)(new_code >> 8));
-    eeprom_update_byte(addr + 1, (uint8_t)(new_code & 0xFF));
+    uint32_t offset = EEPROM_CUSTOM_ENCODER_OFFSET + (encoder_idx * 6) + (behavior * 2);
+    uint8_t hi = new_code >> 8;
+    uint8_t lo = new_code & 0xFF;
+    write_custom_config(&hi, offset+0, 1);
+    write_custom_config(&lo, offset+1, 1);
 #endif
 }

+ 1 - 1
keyboards/cannonkeys/lib/satisfaction75/satisfaction_oled.c

@@ -8,7 +8,6 @@
 #include "matrix.h"
 #include "led.h"
 #include "host.h"
-#include "oled_driver.h"
 #include "progmem.h"
 #include <stdio.h>
 
@@ -16,6 +15,7 @@ void draw_default(void);
 void draw_clock(void);
 
 #ifdef OLED_ENABLE
+#include "oled_driver.h"
 
 oled_rotation_t oled_init_kb(oled_rotation_t rotation) { return OLED_ROTATION_0; }
 

+ 4 - 1
keyboards/cannonkeys/satisfaction75/config.h

@@ -42,4 +42,7 @@
 // 6 for 3x custom encoder settings, left, right, and press (18 bytes)
 #define VIA_EEPROM_CUSTOM_CONFIG_SIZE 20
 
-
+// And if VIA isn't enabled, fall back to using standard QMK for configuration
+#ifndef VIA_ENABLE
+#define EECONFIG_KB_DATA_SIZE VIA_EEPROM_CUSTOM_CONFIG_SIZE
+#endif

+ 5 - 0
keyboards/cannonkeys/satisfaction75_hs/config.h

@@ -40,5 +40,10 @@
 // 6 for 3x custom encoder settings, left, right, and press (18 bytes)
 #define VIA_EEPROM_CUSTOM_CONFIG_SIZE 20
 
+// And if VIA isn't enabled, fall back to using standard QMK for configuration
+#ifndef VIA_ENABLE
+#define EECONFIG_KB_DATA_SIZE VIA_EEPROM_CUSTOM_CONFIG_SIZE
+#endif
+
 // VIA lighting is handled by the keyboard-level code
 #define VIA_CUSTOM_LIGHTING_ENABLE

+ 2 - 2
keyboards/cipulot/common/ec_board.c

@@ -36,7 +36,7 @@ void eeconfig_init_kb(void) {
         }
     }
     // Write default value to EEPROM now
-    eeconfig_update_kb_datablock(&eeprom_ec_config);
+    eeconfig_update_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
 
     eeconfig_init_user();
 }
@@ -44,7 +44,7 @@ void eeconfig_init_kb(void) {
 // On Keyboard startup
 void keyboard_post_init_kb(void) {
     // Read custom menu variables from memory
-    eeconfig_read_kb_datablock(&eeprom_ec_config);
+    eeconfig_read_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
 
     // Set runtime values to EEPROM values
     ec_config.actuation_mode                 = eeprom_ec_config.actuation_mode;

+ 0 - 26
keyboards/cipulot/common/eeprom_tools.h

@@ -1,26 +0,0 @@
-/* Copyright 2023 Cipulot
- *
- * 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 3 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/>.
- */
-#pragma once
-
-#include "eeprom.h"
-
-#if (EECONFIG_KB_DATA_SIZE) > 0
-#    define EEPROM_KB_PARTIAL_UPDATE(__struct, __field) eeprom_update_block(&(__struct.__field), (void *)((void *)(EECONFIG_KB_DATABLOCK) + offsetof(typeof(__struct), __field)), sizeof(__struct.__field))
-#endif
-
-#if (EECONFIG_USER_DATA_SIZE) > 0
-#    define EEPROM_USER_PARTIAL_UPDATE(__struct, __field) eeprom_update_block(&(__struct.__field), (void *)((void *)(EECONFIG_USER_DATABLOCK) + offsetof(typeof(__struct), __field)), sizeof(__struct.__field))
-#endif

+ 3 - 4
keyboards/cipulot/common/via_ec.c

@@ -13,7 +13,6 @@
  * 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 "eeprom_tools.h"
 #include "ec_switch_matrix.h"
 #include "action.h"
 #include "print.h"
@@ -73,7 +72,7 @@ void via_config_set_value(uint8_t *data) {
                 uprintf("# Actuation Mode: Rapid Trigger #\n");
                 uprintf("#################################\n");
             }
-            EEPROM_KB_PARTIAL_UPDATE(eeprom_ec_config, actuation_mode);
+            eeconfig_update_kb_datablock_field(eeprom_ec_config, actuation_mode);
             break;
         }
         case id_mode_0_actuation_threshold: {
@@ -293,7 +292,7 @@ void ec_save_threshold_data(uint8_t option) {
         ec_rescale_values(3);
         ec_rescale_values(4);
     }
-    eeconfig_update_kb_datablock(&eeprom_ec_config);
+    eeconfig_update_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
     uprintf("####################################\n");
     uprintf("# New thresholds applied and saved #\n");
     uprintf("####################################\n");
@@ -321,7 +320,7 @@ void ec_save_bottoming_reading(void) {
     ec_rescale_values(2);
     ec_rescale_values(3);
     ec_rescale_values(4);
-    eeconfig_update_kb_datablock(&eeprom_ec_config);
+    eeconfig_update_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
 }
 
 // Show the calibration data

+ 2 - 2
keyboards/cipulot/ec_980c/ec_980c.c

@@ -44,7 +44,7 @@ void eeconfig_init_kb(void) {
         }
     }
     // Write default value to EEPROM now
-    eeconfig_update_kb_datablock(&eeprom_ec_config);
+    eeconfig_update_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
 
     eeconfig_init_user();
 }
@@ -52,7 +52,7 @@ void eeconfig_init_kb(void) {
 // On Keyboard startup
 void keyboard_post_init_kb(void) {
     // Read custom menu variables from memory
-    eeconfig_read_kb_datablock(&eeprom_ec_config);
+    eeconfig_read_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
 
     // Set runtime values to EEPROM values
     ec_config.actuation_mode                 = eeprom_ec_config.actuation_mode;

+ 2 - 2
keyboards/cipulot/ec_typek/ec_typek.c

@@ -44,7 +44,7 @@ void eeconfig_init_kb(void) {
         }
     }
     // Write default value to EEPROM now
-    eeconfig_update_kb_datablock(&eeprom_ec_config);
+    eeconfig_update_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
 
     eeconfig_init_user();
 }
@@ -52,7 +52,7 @@ void eeconfig_init_kb(void) {
 // On Keyboard startup
 void keyboard_post_init_kb(void) {
     // Read custom menu variables from memory
-    eeconfig_read_kb_datablock(&eeprom_ec_config);
+    eeconfig_read_kb_datablock(&eeprom_ec_config, 0, EECONFIG_KB_DATA_SIZE);
 
     // Set runtime values to EEPROM values
     ec_config.actuation_mode                 = eeprom_ec_config.actuation_mode;

+ 2 - 2
keyboards/contra/keymaps/default/keymap.c

@@ -222,9 +222,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
         if (!eeconfig_is_enabled()) {
             eeconfig_init();
         }
-        keymap_config.raw = eeconfig_read_keymap();
+        eeconfig_read_keymap(&keymap_config);
         keymap_config.nkro = 1;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
       }
       return false;
       break;

+ 2 - 2
keyboards/dm9records/plaid/keymaps/default/keymap.c

@@ -311,9 +311,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
         if (!eeconfig_is_enabled()) {
             eeconfig_init();
         }
-        keymap_config.raw = eeconfig_read_keymap();
+        eeconfig_read_keymap(&keymap_config);
         keymap_config.nkro = 1;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
       }
       return false;
       break;

+ 4 - 0
keyboards/handwired/onekey/keymaps/via_ci/config.h

@@ -0,0 +1,4 @@
+// Copyright 2025 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+#define TRANSIENT_EEPROM_SIZE 160

+ 30 - 0
keyboards/handwired/onekey/keymaps/via_ci/keymap.c

@@ -0,0 +1,30 @@
+// Copyright 2025 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include QMK_KEYBOARD_H
+
+enum custom_keycodes {
+    KC_HELLO = SAFE_RANGE,
+};
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+    LAYOUT_ortho_1x1(KC_HELLO)
+};
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+    switch (keycode) {
+        case KC_HELLO:
+            if (record->event.pressed) {
+                send_string_P("Hello world!");
+            }
+            return false;
+    }
+    return true;
+}
+
+void keyboard_post_init_user(void) {
+  // Customise these values to desired behaviour
+  debug_enable=true;
+  debug_matrix=true;
+  //debug_keyboard=true;
+  //debug_mouse=true;
+}

+ 7 - 0
keyboards/handwired/onekey/keymaps/via_ci/keymap.json

@@ -0,0 +1,7 @@
+{
+    "config": {
+        "features": {
+            "via": true
+        }
+    }
+}

+ 1 - 0
keyboards/handwired/onekey/teensy_32/rules.mk

@@ -0,0 +1 @@
+EEPROM_DRIVER = transient

+ 1 - 1
keyboards/handwired/onekey/teensy_lc/rules.mk

@@ -1,2 +1,2 @@
 USE_CHIBIOS_CONTRIB = yes
-
+EEPROM_DRIVER = transient

+ 2 - 2
keyboards/handwired/ortho_brass/keymaps/default/keymap.c

@@ -182,9 +182,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
                 if (!eeconfig_is_enabled()) {
                     eeconfig_init();
                 }
-                keymap_config.raw  = eeconfig_read_keymap();
+                eeconfig_read_keymap(&keymap_config);
                 keymap_config.nkro = 1;
-                eeconfig_update_keymap(keymap_config.raw);
+                eeconfig_update_keymap(&keymap_config);
             }
             return false;
             break;

+ 1 - 1
keyboards/helix/rev2/rev2.c

@@ -32,7 +32,7 @@ void set_mac_mode_kb(bool macmode) {
      *   https://github.com/qmk/qmk_firmware/blob/fb4a6ad30ea7a648acd59793ed4a30c3a8d8dc32/quantum/process_keycode/process_magic.c#L80-L81
      */
     keymap_config.swap_lalt_lgui = keymap_config.swap_ralt_rgui = !macmode;
-    eeconfig_update_keymap(keymap_config.raw);
+    eeconfig_update_keymap(&keymap_config);
 }
 
 void matrix_init_kb(void) {

+ 1 - 1
keyboards/helix/rev3_4rows/rev3_4rows.c

@@ -27,7 +27,7 @@ void set_mac_mode(bool macmode) {
      *   https://github.com/qmk/qmk_firmware/blob/fb4a6ad30ea7a648acd59793ed4a30c3a8d8dc32/quantum/process_keycode/process_magic.c#L80-L81
      */
     keymap_config.swap_lalt_lgui = keymap_config.swap_ralt_rgui = !macmode;
-    eeconfig_update_keymap(keymap_config.raw);
+    eeconfig_update_keymap(&keymap_config);
 }
 
 #ifdef DIP_SWITCH_ENABLE

+ 1 - 1
keyboards/helix/rev3_5rows/rev3_5rows.c

@@ -27,7 +27,7 @@ void set_mac_mode(bool macmode) {
      *   https://github.com/qmk/qmk_firmware/blob/fb4a6ad30ea7a648acd59793ed4a30c3a8d8dc32/quantum/process_keycode/process_magic.c#L80-L81
      */
     keymap_config.swap_lalt_lgui = keymap_config.swap_ralt_rgui = !macmode;
-    eeconfig_update_keymap(keymap_config.raw);
+    eeconfig_update_keymap(&keymap_config);
 }
 
 #ifdef DIP_SWITCH_ENABLE

+ 2 - 1
keyboards/horrortroll/nyx/rev1/lib/startup_swirl_anim.h

@@ -18,6 +18,7 @@
 #include <string.h>
 #include <math.h>
 #include <lib/lib8tion/lib8tion.h>
+#include "eeconfig.h"
 
 #define LED_TRAIL 10
 
@@ -105,7 +106,7 @@ static void swirl_set_color(hsv_t hsv) {
     traverse_matrix();
 
     if (!(top <= bottom && left <= right)) {
-        eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config));
+        eeconfig_read_rgb_matrix(&rgb_matrix_config);
         rgb_matrix_mode_noeeprom(rgb_matrix_config.mode);
         return;
     }

+ 1 - 1
keyboards/inland/kb83/kb83.c

@@ -317,7 +317,7 @@ bool dip_switch_update_kb(uint8_t index, bool active) {
     }
     if(active){
         keymap_config.no_gui = 0;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
     }
     return true;
 }

+ 2 - 2
keyboards/jian/keymaps/advanced/keymap.c

@@ -472,9 +472,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
         if (!eeconfig_is_enabled()) {
             eeconfig_init();
         }
-        keymap_config.raw = eeconfig_read_keymap();
+        eeconfig_read_keymap(&keymap_config);
         keymap_config.nkro = 1;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
       }
       return false;
     case EXT_PLV:

+ 1 - 1
keyboards/monsgeek/m1/m1.c

@@ -190,7 +190,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
                 set_single_persistent_default_layer(MAC_B);
                 layer_state_set(1<<MAC_B);
                 keymap_config.no_gui     = 0;
-                eeconfig_update_keymap(keymap_config.raw);
+                eeconfig_update_keymap(&keymap_config);
             }
             return false;
         case QK_RGB_MATRIX_TOGGLE:

+ 1 - 1
keyboards/monsgeek/m3/m3.c

@@ -163,7 +163,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) {
             if (record->event.pressed) {
                 set_single_persistent_default_layer(MAC_B);
                 keymap_config.no_gui = 0;
-                eeconfig_update_keymap(keymap_config.raw);
+                eeconfig_update_keymap(&keymap_config);
             }
             return false;
         case GU_TOGG:

+ 1 - 0
keyboards/omnikeyish/dynamic_macro.c

@@ -1,5 +1,6 @@
 #include "omnikeyish.h"
 #include <string.h>
+#include "eeprom.h"
 
 dynamic_macro_t dynamic_macros[DYNAMIC_MACRO_COUNT];
 

+ 2 - 2
keyboards/planck/keymaps/default/keymap.c

@@ -215,9 +215,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
         if (!eeconfig_is_enabled()) {
             eeconfig_init();
         }
-        keymap_config.raw = eeconfig_read_keymap();
+        eeconfig_read_keymap(&keymap_config);
         keymap_config.nkro = 1;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
       }
       return false;
       break;

+ 2 - 2
keyboards/planck/rev7/keymaps/default/keymap.c

@@ -255,9 +255,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
                 if (!eeconfig_is_enabled()) {
                     eeconfig_init();
                 }
-                keymap_config.raw  = eeconfig_read_keymap();
+                eeconfig_read_keymap(&keymap_config);
                 keymap_config.nkro = 1;
-                eeconfig_update_keymap(keymap_config.raw);
+                eeconfig_update_keymap(&keymap_config);
             }
             return false;
             break;

+ 1 - 0
keyboards/rocketboard_16/keycode_lookup.c

@@ -14,6 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <stdlib.h>
 #include "keycode_lookup.h"
 #include "quantum_keycodes.h"
 #include "keymap_us.h"

+ 2 - 2
keyboards/splitography/keymaps/default/keymap.c

@@ -214,9 +214,9 @@ void plover(keyrecord_t *record) {
         if (!eeconfig_is_enabled()) {
             eeconfig_init();
         }
-        keymap_config.raw  = eeconfig_read_keymap();
+        eeconfig_read_keymap(&keymap_config);
         keymap_config.nkro = 1;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
     }
 }
 

+ 2 - 2
keyboards/splitography/keymaps/default_with_ctl_shft_alt_switched/keymap.c

@@ -214,9 +214,9 @@ void plover(keyrecord_t *record) {
         if (!eeconfig_is_enabled()) {
             eeconfig_init();
         }
-        keymap_config.raw  = eeconfig_read_keymap();
+        eeconfig_read_keymap(&keymap_config);
         keymap_config.nkro = 1;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
     }
 }
 

+ 2 - 2
keyboards/splitography/keymaps/dvorak/keymap.c

@@ -214,9 +214,9 @@ void plover(keyrecord_t *record) {
         if (!eeconfig_is_enabled()) {
             eeconfig_init();
         }
-        keymap_config.raw  = eeconfig_read_keymap();
+        eeconfig_read_keymap(&keymap_config);
         keymap_config.nkro = 1;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
     }
 }
 

+ 2 - 1
keyboards/system76/launch_1/launch_1.c

@@ -16,6 +16,7 @@
  */
 
 #include "quantum.h"
+#include "eeprom.h"
 
 #include "usb_mux.h"
 
@@ -73,7 +74,7 @@ led_config_t g_led_config = { {
 } };
 #endif // RGB_MATRIX_ENABLE
 
-bool eeprom_is_valid(void) { 
+bool eeprom_is_valid(void) {
     return (
         eeprom_read_word(((void *)EEPROM_MAGIC_ADDR)) == EEPROM_MAGIC &&
         eeprom_read_byte(((void *)EEPROM_VERSION_ADDR)) == EEPROM_VERSION

+ 1 - 1
keyboards/vitamins_included/rev2/rev2.c

@@ -12,7 +12,7 @@ bool is_keyboard_left(void) {
         gpio_set_pin_input(SPLIT_HAND_PIN);
         return x;
     #elif defined(EE_HANDS)
-        return eeprom_read_byte(EECONFIG_HANDEDNESS);
+        return eeconfig_read_handedness();
     #endif
 
     return is_keyboard_master();

+ 7 - 52
keyboards/wilba_tech/wt_main.c

@@ -33,7 +33,7 @@
 
 // Called from via_init() if VIA_ENABLE
 // Called from matrix_init_kb() if not VIA_ENABLE
-void via_init_kb(void)
+void wt_main_init(void)
 {
     // This checks both an EEPROM reset (from bootmagic lite, keycodes)
     // and also firmware build date (from via_eeprom_is_valid())
@@ -64,11 +64,9 @@ void via_init_kb(void)
 void matrix_init_kb(void)
 {
     // If VIA is disabled, we still need to load backlight settings.
-    // Call via_init_kb() the same way as via_init(), with setting
-    // EEPROM valid afterwards.
+    // Call via_init_kb() the same way as via_init_kb() does.
 #ifndef VIA_ENABLE
-    via_init_kb();
-    via_eeprom_set_valid(true);
+    wt_main_init();
 #endif // VIA_ENABLE
 
     matrix_init_user();
@@ -109,6 +107,10 @@ void suspend_wakeup_init_kb(void)
 // Moving this to the bottom of this source file is a workaround
 // for an intermittent compiler error for Atmel compiler.
 #ifdef VIA_ENABLE
+void via_init_kb(void) {
+    wt_main_init();
+}
+
 void via_custom_value_command_kb(uint8_t *data, uint8_t length) {
     uint8_t *command_id        = &(data[0]);
     uint8_t *channel_id        = &(data[1]);
@@ -159,50 +161,3 @@ void via_set_device_indication(uint8_t value)
 }
 
 #endif // VIA_ENABLE
-
-//
-// In the case of VIA being disabled, we still need to check if
-// keyboard level EEPROM memory is valid before loading.
-// Thus these are copies of the same functions in VIA, since
-// the backlight settings reuse VIA's EEPROM magic/version,
-// and the ones in via.c won't be compiled in.
-//
-// Yes, this is sub-optimal, and is only here for completeness
-// (i.e. catering to the 1% of people that want wilba.tech LED bling
-// AND want persistent settings BUT DON'T want to use dynamic keymaps/VIA).
-//
-#ifndef VIA_ENABLE
-
-bool via_eeprom_is_valid(void)
-{
-    char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
-    uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3]  & 0x0F );
-    uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6]  & 0x0F );
-    uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9]  & 0x0F );
-
-    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 );
-}
-
-void via_eeprom_set_valid(bool valid)
-{
-    char *p = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
-    uint8_t magic0 = ( ( p[2] & 0x0F ) << 4 ) | ( p[3]  & 0x0F );
-    uint8_t magic1 = ( ( p[5] & 0x0F ) << 4 ) | ( p[6]  & 0x0F );
-    uint8_t magic2 = ( ( p[8] & 0x0F ) << 4 ) | ( p[9]  & 0x0F );
-
-    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+0, valid ? magic0 : 0xFF);
-    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+1, valid ? magic1 : 0xFF);
-    eeprom_update_byte( (void*)VIA_EEPROM_MAGIC_ADDR+2, valid ? magic2 : 0xFF);
-}
-
-void via_eeprom_reset(void)
-{
-    // Set the VIA specific EEPROM state as invalid.
-    via_eeprom_set_valid(false);
-    // Set the TMK/QMK EEPROM state as invalid.
-    eeconfig_disable();
-}
-
-#endif // VIA_ENABLE

+ 2 - 0
keyboards/wilba_tech/wt_mono_backlight.c

@@ -25,6 +25,8 @@
 #include "progmem.h"
 #include "eeprom.h"
 
+#include "nvm_eeprom_eeconfig_internal.h" // expose EEPROM addresses, no appetite to move legacy/deprecated code to nvm
+#include "nvm_eeprom_via_internal.h" // expose EEPROM addresses, no appetite to move legacy/deprecated code to nvm
 #include "via.h" // uses EEPROM address, lighting value IDs
 #define MONO_BACKLIGHT_CONFIG_EEPROM_ADDR (VIA_EEPROM_CUSTOM_CONFIG_ADDR)
 

+ 2 - 0
keyboards/wilba_tech/wt_rgb_backlight.c

@@ -66,6 +66,8 @@
 #include "quantum/color.h"
 #include "eeprom.h"
 
+#include "nvm_eeprom_eeconfig_internal.h" // expose EEPROM addresses, no appetite to move legacy/deprecated code to nvm
+#include "nvm_eeprom_via_internal.h" // expose EEPROM addresses, no appetite to move legacy/deprecated code to nvm
 #include "via.h" // uses EEPROM address, lighting value IDs
 #define RGB_BACKLIGHT_CONFIG_EEPROM_ADDR (VIA_EEPROM_CUSTOM_CONFIG_ADDR)
 

+ 1 - 1
keyboards/xelus/la_plus/rgb_matrix_kb.inc

@@ -31,7 +31,7 @@ static void startup_animation_setleds(effect_params_t* params, bool dots) {
     } else if (num == 0 || num == 1 || num == 2) {
         return;
     } else if (num >= 22) {
-        eeprom_read_block(&rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_matrix_config));
+        eeconfig_read_rgb_matrix(&rgb_matrix_config);
         rgb_matrix_mode_noeeprom(rgb_matrix_config.mode);
         return;
     }

+ 1 - 1
quantum/action_util.c

@@ -220,7 +220,7 @@ bool is_oneshot_layer_active(void) {
 void oneshot_set(bool active) {
     if (keymap_config.oneshot_enable != active) {
         keymap_config.oneshot_enable = active;
-        eeconfig_update_keymap(keymap_config.raw);
+        eeconfig_update_keymap(&keymap_config);
         clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
         dprintf("Oneshot: active: %d\n", active);
     }

+ 6 - 6
quantum/audio/audio.c

@@ -149,14 +149,14 @@ void audio_driver_start(void) {
 }
 
 void eeconfig_update_audio_current(void) {
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
 }
 
 void eeconfig_update_audio_default(void) {
     audio_config.valid         = true;
     audio_config.enable        = AUDIO_DEFAULT_ON;
     audio_config.clicky_enable = AUDIO_DEFAULT_CLICKY_ON;
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
 }
 
 void audio_init(void) {
@@ -164,7 +164,7 @@ void audio_init(void) {
         return;
     }
 
-    audio_config.raw = eeconfig_read_audio();
+    eeconfig_read_audio(&audio_config);
     if (!audio_config.valid) {
         dprintf("audio_init audio_config.valid = 0. Write default values to EEPROM.\n");
         eeconfig_update_audio_default();
@@ -196,7 +196,7 @@ void audio_toggle(void) {
         stop_all_notes();
     }
     audio_config.enable ^= 1;
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
     if (audio_config.enable) {
         audio_on_user();
     } else {
@@ -206,7 +206,7 @@ void audio_toggle(void) {
 
 void audio_on(void) {
     audio_config.enable = 1;
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
     audio_on_user();
     PLAY_SONG(audio_on_song);
 }
@@ -217,7 +217,7 @@ void audio_off(void) {
     wait_ms(100);
     audio_stop_all();
     audio_config.enable = 0;
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
 }
 
 bool audio_is_on(void) {

+ 1 - 1
quantum/audio/audio.h

@@ -28,7 +28,7 @@
 #    include "audio_dac.h"
 #endif
 
-typedef union {
+typedef union audio_config_t {
     uint8_t raw;
     struct {
         bool    enable : 1;

+ 11 - 20
quantum/backlight/backlight.c

@@ -16,7 +16,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "backlight.h"
-#include "eeprom.h"
 #include "eeconfig.h"
 #include "debug.h"
 
@@ -55,7 +54,7 @@ static void backlight_check_config(void) {
  * FIXME: needs doc
  */
 void backlight_init(void) {
-    backlight_config.raw = eeconfig_read_backlight();
+    eeconfig_read_backlight(&backlight_config);
     if (!backlight_config.valid) {
         dprintf("backlight_init backlight_config.valid = 0. Write default values to EEPROM.\n");
         eeconfig_update_backlight_default();
@@ -74,7 +73,7 @@ void backlight_increase(void) {
         backlight_config.level++;
     }
     backlight_config.enable = 1;
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
     dprintf("backlight increase: %u\n", backlight_config.level);
     backlight_set(backlight_config.level);
 }
@@ -87,7 +86,7 @@ void backlight_decrease(void) {
     if (backlight_config.level > 0) {
         backlight_config.level--;
         backlight_config.enable = !!backlight_config.level;
-        eeconfig_update_backlight(backlight_config.raw);
+        eeconfig_update_backlight(&backlight_config);
     }
     dprintf("backlight decrease: %u\n", backlight_config.level);
     backlight_set(backlight_config.level);
@@ -116,7 +115,7 @@ void backlight_enable(void) {
     backlight_config.enable = true;
     if (backlight_config.raw == 1) // enabled but level == 0
         backlight_config.level = 1;
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
     dprintf("backlight enable\n");
     backlight_set(backlight_config.level);
 }
@@ -129,7 +128,7 @@ void backlight_disable(void) {
     if (!backlight_config.enable) return; // do nothing if backlight is already off
 
     backlight_config.enable = false;
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
     dprintf("backlight disable\n");
     backlight_set(0);
 }
@@ -152,7 +151,7 @@ void backlight_step(void) {
         backlight_config.level = 0;
     }
     backlight_config.enable = !!backlight_config.level;
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
     dprintf("backlight step: %u\n", backlight_config.level);
     backlight_set(backlight_config.level);
 }
@@ -173,19 +172,11 @@ void backlight_level_noeeprom(uint8_t level) {
  */
 void backlight_level(uint8_t level) {
     backlight_level_noeeprom(level);
-    eeconfig_update_backlight(backlight_config.raw);
-}
-
-uint8_t eeconfig_read_backlight(void) {
-    return eeprom_read_byte(EECONFIG_BACKLIGHT);
-}
-
-void eeconfig_update_backlight(uint8_t val) {
-    eeprom_update_byte(EECONFIG_BACKLIGHT, val);
+    eeconfig_update_backlight(&backlight_config);
 }
 
 void eeconfig_update_backlight_current(void) {
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
 }
 
 void eeconfig_update_backlight_default(void) {
@@ -193,7 +184,7 @@ void eeconfig_update_backlight_default(void) {
     backlight_config.enable    = BACKLIGHT_DEFAULT_ON;
     backlight_config.breathing = BACKLIGHT_DEFAULT_BREATHING;
     backlight_config.level     = BACKLIGHT_DEFAULT_LEVEL;
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
 }
 
 /** \brief Get backlight level
@@ -226,7 +217,7 @@ void backlight_enable_breathing(void) {
     if (backlight_config.breathing) return; // do nothing if breathing is already on
 
     backlight_config.breathing = true;
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
     dprintf("backlight breathing enable\n");
     breathing_enable();
 }
@@ -239,7 +230,7 @@ void backlight_disable_breathing(void) {
     if (!backlight_config.breathing) return; // do nothing if breathing is already off
 
     backlight_config.breathing = false;
-    eeconfig_update_backlight(backlight_config.raw);
+    eeconfig_update_backlight(&backlight_config);
     dprintf("backlight breathing disable\n");
     breathing_disable();
 }

+ 3 - 5
quantum/backlight/backlight.h

@@ -34,7 +34,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #    define BREATHING_PERIOD 6
 #endif
 
-typedef union {
+typedef union backlight_config_t {
     uint8_t raw;
     struct {
         bool    enable : 1;
@@ -58,10 +58,8 @@ void    backlight_level_noeeprom(uint8_t level);
 void    backlight_level(uint8_t level);
 uint8_t get_backlight_level(void);
 
-uint8_t eeconfig_read_backlight(void);
-void    eeconfig_update_backlight(uint8_t val);
-void    eeconfig_update_backlight_current(void);
-void    eeconfig_update_backlight_default(void);
+void eeconfig_update_backlight_current(void);
+void eeconfig_update_backlight_default(void);
 
 // implementation specific
 void backlight_init_ports(void);

+ 3 - 3
quantum/command.c

@@ -246,7 +246,7 @@ static void print_eeconfig(void) {
     xprintf("eeconfig:\ndefault_layer: %u\n", eeconfig_read_default_layer());
 
     debug_config_t dc;
-    dc.raw = eeconfig_read_debug();
+    eeconfig_read_debug(&dc);
     xprintf(/* clang-format off */
 
         "debug_config.raw: %02X\n"
@@ -263,7 +263,7 @@ static void print_eeconfig(void) {
     ); /* clang-format on */
 
     keymap_config_t kc;
-    kc.raw = eeconfig_read_keymap();
+    eeconfig_read_keymap(&kc);
     xprintf(/* clang-format off */
 
         "keymap_config.raw: %02X\n"
@@ -296,7 +296,7 @@ static void print_eeconfig(void) {
 #    ifdef BACKLIGHT_ENABLE
 
     backlight_config_t bc;
-    bc.raw = eeconfig_read_backlight();
+    eeconfig_read_backlight(&bc);
     xprintf(/* clang-format off */
         "backlight_config"
 

+ 38 - 165
quantum/dynamic_keymap.c

@@ -1,4 +1,5 @@
 /* Copyright 2017 Jason Williams (Wilba)
+ * Copyright 2024-2025 Nick Brassel (@tzarc)
  *
  * 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
@@ -21,14 +22,7 @@
 #include "progmem.h"
 #include "send_string.h"
 #include "keycodes.h"
-
-#ifdef VIA_ENABLE
-#    include "via.h"
-#    define DYNAMIC_KEYMAP_EEPROM_START (VIA_EEPROM_CONFIG_END)
-#else
-#    include "eeconfig.h"
-#    define DYNAMIC_KEYMAP_EEPROM_START (EECONFIG_SIZE)
-#endif
+#include "nvm_dynamic_keymap.h"
 
 #ifdef ENCODER_ENABLE
 #    include "encoder.h"
@@ -36,67 +30,6 @@
 #    define NUM_ENCODERS 0
 #endif
 
-#ifndef DYNAMIC_KEYMAP_LAYER_COUNT
-#    define DYNAMIC_KEYMAP_LAYER_COUNT 4
-#endif
-
-#ifndef DYNAMIC_KEYMAP_MACRO_COUNT
-#    define DYNAMIC_KEYMAP_MACRO_COUNT 16
-#endif
-
-#ifndef TOTAL_EEPROM_BYTE_COUNT
-#    error Unknown total EEPROM size. Cannot derive maximum for dynamic keymaps.
-#endif
-
-#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
-#    define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR (TOTAL_EEPROM_BYTE_COUNT - 1)
-#endif
-
-#if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > (TOTAL_EEPROM_BYTE_COUNT - 1)
-#    pragma message STR(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) " > " STR((TOTAL_EEPROM_BYTE_COUNT - 1))
-#    error DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is configured to use more space than what is available for the selected EEPROM driver
-#endif
-
-// Due to usage of uint16_t check for max 65535
-#if DYNAMIC_KEYMAP_EEPROM_MAX_ADDR > 65535
-#    pragma message STR(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) " > 65535"
-#    error DYNAMIC_KEYMAP_EEPROM_MAX_ADDR must be less than 65536
-#endif
-
-// If DYNAMIC_KEYMAP_EEPROM_ADDR not explicitly defined in config.h,
-#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR
-#    define DYNAMIC_KEYMAP_EEPROM_ADDR DYNAMIC_KEYMAP_EEPROM_START
-#endif
-
-// Dynamic encoders starts after dynamic keymaps
-#ifndef DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR
-#    define DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2))
-#endif
-
-// Dynamic macro starts after dynamic encoders, but only when using ENCODER_MAP
-#ifdef ENCODER_MAP_ENABLE
-#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
-#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * NUM_ENCODERS * 2 * 2))
-#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
-#else      // ENCODER_MAP_ENABLE
-#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
-#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR)
-#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
-#endif     // ENCODER_MAP_ENABLE
-
-// Sanity check that dynamic keymaps fit in available EEPROM
-// If there's not 100 bytes available for macros, then something is wrong.
-// The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,
-// or DYNAMIC_KEYMAP_EEPROM_MAX_ADDR to increase it, *only if* the microcontroller has
-// more than the default.
-_Static_assert((DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) >= 100, "Dynamic keymaps are configured to use more EEPROM than is available.");
-
-// Dynamic macros are stored after the keymaps and use what is available
-// up to and including DYNAMIC_KEYMAP_EEPROM_MAX_ADDR.
-#ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE
-#    define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + 1)
-#endif
-
 #ifndef DYNAMIC_KEYMAP_MACRO_DELAY
 #    define DYNAMIC_KEYMAP_MACRO_DELAY TAP_CODE_DELAY
 #endif
@@ -105,52 +38,28 @@ uint8_t dynamic_keymap_get_layer_count(void) {
     return DYNAMIC_KEYMAP_LAYER_COUNT;
 }
 
-void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column) {
-    // TODO: optimize this with some left shifts
-    return ((void *)DYNAMIC_KEYMAP_EEPROM_ADDR) + (layer * MATRIX_ROWS * MATRIX_COLS * 2) + (row * MATRIX_COLS * 2) + (column * 2);
-}
-
 uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column) {
-    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return KC_NO;
-    void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
-    // Big endian, so we can read/write EEPROM directly from host if we want
-    uint16_t keycode = eeprom_read_byte(address) << 8;
-    keycode |= eeprom_read_byte(address + 1);
-    return keycode;
+    return nvm_dynamic_keymap_read_keycode(layer, row, column);
 }
 
 void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) {
-    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return;
-    void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
-    // Big endian, so we can read/write EEPROM directly from host if we want
-    eeprom_update_byte(address, (uint8_t)(keycode >> 8));
-    eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));
+    nvm_dynamic_keymap_update_keycode(layer, row, column, keycode);
 }
 
 #ifdef ENCODER_MAP_ENABLE
-void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t encoder_id) {
-    return ((void *)DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) + (layer * NUM_ENCODERS * 2 * 2) + (encoder_id * 2 * 2);
-}
-
 uint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) {
-    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return KC_NO;
-    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
-    // Big endian, so we can read/write EEPROM directly from host if we want
-    uint16_t keycode = ((uint16_t)eeprom_read_byte(address + (clockwise ? 0 : 2))) << 8;
-    keycode |= eeprom_read_byte(address + (clockwise ? 0 : 2) + 1);
-    return keycode;
+    return nvm_dynamic_keymap_read_encoder(layer, encoder_id, clockwise);
 }
 
 void dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) {
-    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return;
-    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
-    // Big endian, so we can read/write EEPROM directly from host if we want
-    eeprom_update_byte(address + (clockwise ? 0 : 2), (uint8_t)(keycode >> 8));
-    eeprom_update_byte(address + (clockwise ? 0 : 2) + 1, (uint8_t)(keycode & 0xFF));
+    nvm_dynamic_keymap_update_encoder(layer, encoder_id, clockwise, keycode);
 }
 #endif // ENCODER_MAP_ENABLE
 
 void dynamic_keymap_reset(void) {
+    // Erase the keymaps, if necessary.
+    nvm_dynamic_keymap_erase();
+
     // Reset the keymaps in EEPROM to what is in flash.
     for (int layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {
         for (int row = 0; row < MATRIX_ROWS; row++) {
@@ -168,31 +77,11 @@ void dynamic_keymap_reset(void) {
 }
 
 void dynamic_keymap_get_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
-    uint16_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
-    void *   source                     = (void *)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
-    uint8_t *target                     = data;
-    for (uint16_t i = 0; i < size; i++) {
-        if (offset + i < dynamic_keymap_eeprom_size) {
-            *target = eeprom_read_byte(source);
-        } else {
-            *target = 0x00;
-        }
-        source++;
-        target++;
-    }
+    nvm_dynamic_keymap_read_buffer(offset, size, data);
 }
 
 void dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
-    uint16_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
-    void *   target                     = (void *)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
-    uint8_t *source                     = data;
-    for (uint16_t i = 0; i < size; i++) {
-        if (offset + i < dynamic_keymap_eeprom_size) {
-            eeprom_update_byte(target, *source);
-        }
-        source++;
-        target++;
-    }
+    nvm_dynamic_keymap_update_buffer(offset, size, data);
 }
 
 uint16_t keycode_at_keymap_location(uint8_t layer_num, uint8_t row, uint8_t column) {
@@ -216,53 +105,38 @@ uint8_t dynamic_keymap_macro_get_count(void) {
 }
 
 uint16_t dynamic_keymap_macro_get_buffer_size(void) {
-    return DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE;
+    return (uint16_t)nvm_dynamic_keymap_macro_size();
 }
 
 void dynamic_keymap_macro_get_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
-    void *   source = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
-    uint8_t *target = data;
-    for (uint16_t i = 0; i < size; i++) {
-        if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
-            *target = eeprom_read_byte(source);
-        } else {
-            *target = 0x00;
-        }
-        source++;
-        target++;
-    }
+    nvm_dynamic_keymap_macro_read_buffer(offset, size, data);
 }
 
 void dynamic_keymap_macro_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {
-    void *   target = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
-    uint8_t *source = data;
-    for (uint16_t i = 0; i < size; i++) {
-        if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
-            eeprom_update_byte(target, *source);
-        }
-        source++;
-        target++;
-    }
+    nvm_dynamic_keymap_macro_update_buffer(offset, size, data);
 }
 
-typedef struct send_string_eeprom_state_t {
-    const uint8_t *ptr;
-} send_string_eeprom_state_t;
+static uint8_t dynamic_keymap_read_byte(uint32_t offset) {
+    uint8_t d;
+    nvm_dynamic_keymap_macro_read_buffer(offset, 1, &d);
+    return d;
+}
 
-char send_string_get_next_eeprom(void *arg) {
-    send_string_eeprom_state_t *state = (send_string_eeprom_state_t *)arg;
-    char                        ret   = eeprom_read_byte(state->ptr);
-    state->ptr++;
+typedef struct send_string_nvm_state_t {
+    uint32_t offset;
+} send_string_nvm_state_t;
+
+char send_string_get_next_nvm(void *arg) {
+    send_string_nvm_state_t *state = (send_string_nvm_state_t *)arg;
+    char                     ret   = dynamic_keymap_read_byte(state->offset);
+    state->offset++;
     return ret;
 }
 
 void dynamic_keymap_macro_reset(void) {
-    void *p   = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
-    void *end = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
-    while (p != end) {
-        eeprom_update_byte(p, 0);
-        ++p;
-    }
+    // Erase the macros, if necessary.
+    nvm_dynamic_keymap_macro_erase();
+    nvm_dynamic_keymap_macro_reset();
 }
 
 void dynamic_keymap_macro_send(uint8_t id) {
@@ -274,27 +148,26 @@ void dynamic_keymap_macro_send(uint8_t id) {
     // If it's not zero, then we are in the middle
     // of buffer writing, possibly an aborted buffer
     // write. So do nothing.
-    void *p = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE - 1);
-    if (eeprom_read_byte(p) != 0) {
+    if (dynamic_keymap_read_byte(nvm_dynamic_keymap_macro_size() - 1) != 0) {
         return;
     }
 
     // Skip N null characters
     // p will then point to the Nth macro
-    p         = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
-    void *end = (void *)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
+    uint32_t offset = 0;
+    uint32_t end    = nvm_dynamic_keymap_macro_size();
     while (id > 0) {
         // If we are past the end of the buffer, then there is
         // no Nth macro in the buffer.
-        if (p == end) {
+        if (offset == end) {
             return;
         }
-        if (eeprom_read_byte(p) == 0) {
+        if (dynamic_keymap_read_byte(offset) == 0) {
             --id;
         }
-        ++p;
+        ++offset;
     }
 
-    send_string_eeprom_state_t state = {p};
-    send_string_with_delay_impl(send_string_get_next_eeprom, &state, DYNAMIC_KEYMAP_MACRO_DELAY);
+    send_string_nvm_state_t state = {.offset = 0};
+    send_string_with_delay_impl(send_string_get_next_nvm, &state, DYNAMIC_KEYMAP_MACRO_DELAY);
 }

+ 8 - 1
quantum/dynamic_keymap.h

@@ -18,8 +18,15 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#ifndef DYNAMIC_KEYMAP_LAYER_COUNT
+#    define DYNAMIC_KEYMAP_LAYER_COUNT 4
+#endif
+
+#ifndef DYNAMIC_KEYMAP_MACRO_COUNT
+#    define DYNAMIC_KEYMAP_MACRO_COUNT 16
+#endif
+
 uint8_t  dynamic_keymap_get_layer_count(void);
-void *   dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column);
 uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column);
 void     dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode);
 #ifdef ENCODER_MAP_ENABLE

+ 211 - 233
quantum/eeconfig.c

@@ -1,355 +1,333 @@
 #include <string.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include "debug.h"
 #include "eeprom.h"
 #include "eeconfig.h"
+#include "action_layer.h"
+#include "nvm_eeconfig.h"
+#include "keycode_config.h"
 
-#if defined(EEPROM_DRIVER)
+#ifdef EEPROM_DRIVER
 #    include "eeprom_driver.h"
-#endif
+#endif // EEPROM_DRIVER
+
+#ifdef BACKLIGHT_ENABLE
+#    include "backlight.h"
+#endif // BACKLIGHT_ENABLE
+
+#ifdef AUDIO_ENABLE
+#    include "audio.h"
+#endif // AUDIO_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+#    include "rgblight.h"
+#endif // RGBLIGHT_ENABLE
+
+#ifdef RGB_MATRIX_ENABLE
+#    include "rgb_matrix_types.h"
+#endif // RGB_MATRIX_ENABLE
 
-#if defined(HAPTIC_ENABLE)
+#ifdef LED_MATRIX_ENABLE
+#    include "led_matrix_types.h"
+#endif // LED_MATRIX_ENABLE
+
+#ifdef UNICODE_COMMON_ENABLE
+#    include "unicode.h"
+#endif // UNICODE_COMMON_ENABLE
+
+#ifdef HAPTIC_ENABLE
 #    include "haptic.h"
-#endif
+#endif // HAPTIC_ENABLE
 
-#if defined(VIA_ENABLE)
+#ifdef VIA_ENABLE
 bool via_eeprom_is_valid(void);
 void via_eeprom_set_valid(bool valid);
 void eeconfig_init_via(void);
-#endif
-
-_Static_assert((intptr_t)EECONFIG_HANDEDNESS == 14, "EEPROM handedness offset is incorrect");
+#else
+void dynamic_keymap_reset(void);
+#endif // VIA_ENABLE
 
-/** \brief eeconfig enable
- *
- * FIXME: needs doc
- */
 __attribute__((weak)) void eeconfig_init_user(void) {
 #if (EECONFIG_USER_DATA_SIZE) == 0
     // Reset user EEPROM value to blank, rather than to a set value
     eeconfig_update_user(0);
-#endif
+#endif // (EECONFIG_USER_DATA_SIZE) == 0
 }
 
 __attribute__((weak)) void eeconfig_init_kb(void) {
 #if (EECONFIG_KB_DATA_SIZE) == 0
     // Reset Keyboard EEPROM value to blank, rather than to a set value
     eeconfig_update_kb(0);
-#endif
+#endif // (EECONFIG_KB_DATA_SIZE) == 0
 
     eeconfig_init_user();
 }
 
-/*
- * FIXME: needs doc
- */
 void eeconfig_init_quantum(void) {
-#if defined(EEPROM_DRIVER)
-    eeprom_driver_format(false);
-#endif
+    nvm_eeconfig_erase();
+
+    eeconfig_enable();
+
+    debug_config_t debug_config = {0};
+    eeconfig_update_debug(&debug_config);
 
-    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);
-    eeprom_update_byte(EECONFIG_DEBUG, 0);
     default_layer_state = (layer_state_t)1 << 0;
     eeconfig_update_default_layer(default_layer_state);
-    // Enable oneshot and autocorrect by default: 0b0001 0100 0000 0000
-    eeprom_update_word(EECONFIG_KEYMAP, 0x1400);
-    eeprom_update_byte(EECONFIG_BACKLIGHT, 0);
-    eeprom_update_byte(EECONFIG_AUDIO, 0);
-    eeprom_update_dword(EECONFIG_RGBLIGHT, 0);
-    eeprom_update_byte(EECONFIG_RGBLIGHT_EXTENDED, 0);
-    eeprom_update_byte(EECONFIG_UNICODEMODE, 0);
-    eeprom_update_byte(EECONFIG_STENOMODE, 0);
-    eeprom_write_qword(EECONFIG_RGB_MATRIX, 0);
-    eeprom_update_dword(EECONFIG_HAPTIC, 0);
-#if defined(HAPTIC_ENABLE)
-    haptic_reset();
+
+    keymap_config_t keymap_config = {
+        .swap_control_capslock    = false,
+        .capslock_to_control      = false,
+        .swap_lalt_lgui           = false,
+        .swap_ralt_rgui           = false,
+        .no_gui                   = false,
+        .swap_grave_esc           = false,
+        .swap_backslash_backspace = false,
+        .nkro                     = false,
+        .swap_lctl_lgui           = false,
+        .swap_rctl_rgui           = false,
+        .oneshot_enable           = true, // Enable oneshot by default
+        .swap_escape_capslock     = false,
+        .autocorrect_enable       = true, // Enable autocorrect by default
+    };
+    eeconfig_update_keymap(&keymap_config);
+
+#ifdef BACKLIGHT_ENABLE
+    backlight_config_t backlight_config = {0};
+    eeconfig_update_backlight(&backlight_config);
+#endif // BACKLIGHT_ENABLE
+
+#ifdef AUDIO_ENABLE
+    audio_config_t audio_config = {0};
+    eeconfig_update_audio(&audio_config);
+#endif // AUDIO_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+    rgblight_config_t rgblight_config = {0};
+    eeconfig_update_rgblight(&rgblight_config);
+#endif // RGBLIGHT_ENABLE
+
+#ifdef UNICODE_COMMON_ENABLE
+    unicode_config_t unicode_config = {0};
+    eeconfig_update_unicode_mode(&unicode_config);
+#endif // UNICODE_COMMON_ENABLE
+
+#ifdef STENO_ENABLE
+    eeconfig_update_steno_mode(0);
+#endif // STENO_ENABLE
+
+#ifdef RGB_MATRIX_ENABLE
+    rgb_config_t rgb_matrix_config = {0};
+    eeconfig_update_rgb_matrix(&rgb_matrix_config);
 #endif
 
+#ifdef LED_MATRIX_ENABLE
+    led_eeconfig_t led_matrix_config = {0};
+    eeconfig_update_led_matrix(&led_matrix_config);
+#endif // LED_MATRIX_ENABLE
+
+#ifdef HAPTIC_ENABLE
+    haptic_config_t haptic_config = {0};
+    eeconfig_update_haptic(&haptic_config);
+    haptic_reset();
+#endif // HAPTIC_ENABLE
+
 #if (EECONFIG_KB_DATA_SIZE) > 0
     eeconfig_init_kb_datablock();
-#endif
+#endif // (EECONFIG_KB_DATA_SIZE) > 0
 
 #if (EECONFIG_USER_DATA_SIZE) > 0
     eeconfig_init_user_datablock();
-#endif
+#endif // (EECONFIG_USER_DATA_SIZE) > 0
 
 #if defined(VIA_ENABLE)
     // Invalidate VIA eeprom config, and then reset.
-    // Just in case if power is lost mid init, this makes sure that it pets
+    // Just in case if power is lost mid init, this makes sure that it gets
     // properly re-initialized.
-    via_eeprom_set_valid(false);
     eeconfig_init_via();
+#elif defined(DYNAMIC_KEYMAP_ENABLE)
+    dynamic_keymap_reset();
 #endif
 
     eeconfig_init_kb();
 }
 
-/** \brief eeconfig initialization
- *
- * FIXME: needs doc
- */
 void eeconfig_init(void) {
     eeconfig_init_quantum();
 }
 
-/** \brief eeconfig enable
- *
- * FIXME: needs doc
- */
 void eeconfig_enable(void) {
-    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);
+    nvm_eeconfig_enable();
 }
 
-/** \brief eeconfig disable
- *
- * FIXME: needs doc
- */
 void eeconfig_disable(void) {
-#if defined(EEPROM_DRIVER)
-    eeprom_driver_format(false);
-#endif
-    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF);
+    nvm_eeconfig_disable();
 }
 
-/** \brief eeconfig is enabled
- *
- * FIXME: needs doc
- */
 bool eeconfig_is_enabled(void) {
-    bool is_eeprom_enabled = (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER);
+    bool is_eeprom_enabled = nvm_eeconfig_is_enabled();
 #ifdef VIA_ENABLE
     if (is_eeprom_enabled) {
         is_eeprom_enabled = via_eeprom_is_valid();
     }
-#endif
+#endif // VIA_ENABLE
     return is_eeprom_enabled;
 }
 
-/** \brief eeconfig is disabled
- *
- * FIXME: needs doc
- */
 bool eeconfig_is_disabled(void) {
-    bool is_eeprom_disabled = (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF);
+    bool is_eeprom_disabled = nvm_eeconfig_is_disabled();
 #ifdef VIA_ENABLE
     if (!is_eeprom_disabled) {
         is_eeprom_disabled = !via_eeprom_is_valid();
     }
-#endif
+#endif // VIA_ENABLE
     return is_eeprom_disabled;
 }
 
-/** \brief eeconfig read debug
- *
- * FIXME: needs doc
- */
-uint8_t eeconfig_read_debug(void) {
-    return eeprom_read_byte(EECONFIG_DEBUG);
+void eeconfig_read_debug(debug_config_t *debug_config) {
+    nvm_eeconfig_read_debug(debug_config);
 }
-/** \brief eeconfig update debug
- *
- * FIXME: needs doc
- */
-void eeconfig_update_debug(uint8_t val) {
-    eeprom_update_byte(EECONFIG_DEBUG, val);
+void eeconfig_update_debug(const debug_config_t *debug_config) {
+    nvm_eeconfig_update_debug(debug_config);
 }
 
-/** \brief eeconfig read default layer
- *
- * FIXME: needs doc
- */
 layer_state_t eeconfig_read_default_layer(void) {
-    uint8_t val = eeprom_read_byte(EECONFIG_DEFAULT_LAYER);
-
-#ifdef DEFAULT_LAYER_STATE_IS_VALUE_NOT_BITMASK
-    // stored as a layer number, so convert back to bitmask
-    return 1 << val;
-#else
-    // stored as 8-bit-wide bitmask, so read the value directly - handling padding to 16/32 bit layer_state_t
-    return val;
-#endif
+    return nvm_eeconfig_read_default_layer();
 }
-/** \brief eeconfig update default layer
- *
- * FIXME: needs doc
- */
 void eeconfig_update_default_layer(layer_state_t state) {
-#ifdef DEFAULT_LAYER_STATE_IS_VALUE_NOT_BITMASK
-    // stored as a layer number, so only store the highest layer
-    uint8_t val = get_highest_layer(state);
-#else
-    // stored as 8-bit-wide bitmask, so write the value directly - handling truncation from 16/32 bit layer_state_t
-    uint8_t val = state;
-#endif
+    nvm_eeconfig_update_default_layer(state);
+}
+
+void eeconfig_read_keymap(keymap_config_t *keymap_config) {
+    nvm_eeconfig_read_keymap(keymap_config);
+}
+void eeconfig_update_keymap(const keymap_config_t *keymap_config) {
+    nvm_eeconfig_update_keymap(keymap_config);
+}
+
+#ifdef AUDIO_ENABLE
+void eeconfig_read_audio(audio_config_t *audio_config) {
+    nvm_eeconfig_read_audio(audio_config);
+}
+void eeconfig_update_audio(const audio_config_t *audio_config) {
+    nvm_eeconfig_update_audio(audio_config);
+}
+#endif // AUDIO_ENABLE
+
+#ifdef UNICODE_COMMON_ENABLE
+void eeconfig_read_unicode_mode(unicode_config_t *unicode_config) {
+    return nvm_eeconfig_read_unicode_mode(unicode_config);
+}
+void eeconfig_update_unicode_mode(const unicode_config_t *unicode_config) {
+    nvm_eeconfig_update_unicode_mode(unicode_config);
+}
+#endif // UNICODE_COMMON_ENABLE
 
-    eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val);
+#ifdef BACKLIGHT_ENABLE
+void eeconfig_read_backlight(backlight_config_t *backlight_config) {
+    nvm_eeconfig_read_backlight(backlight_config);
+}
+void eeconfig_update_backlight(const backlight_config_t *backlight_config) {
+    nvm_eeconfig_update_backlight(backlight_config);
+}
+#endif // BACKLIGHT_ENABLE
+
+#ifdef STENO_ENABLE
+uint8_t eeconfig_read_steno_mode(void) {
+    return nvm_eeconfig_read_steno_mode();
+}
+void eeconfig_update_steno_mode(uint8_t val) {
+    nvm_eeconfig_update_steno_mode(val);
 }
+#endif // STENO_ENABLE
 
-/** \brief eeconfig read keymap
- *
- * FIXME: needs doc
- */
-uint16_t eeconfig_read_keymap(void) {
-    return eeprom_read_word(EECONFIG_KEYMAP);
+#ifdef RGB_MATRIX_ENABLE
+void eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config) {
+    nvm_eeconfig_read_rgb_matrix(rgb_matrix_config);
 }
-/** \brief eeconfig update keymap
- *
- * FIXME: needs doc
- */
-void eeconfig_update_keymap(uint16_t val) {
-    eeprom_update_word(EECONFIG_KEYMAP, val);
+void eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config) {
+    nvm_eeconfig_update_rgb_matrix(rgb_matrix_config);
 }
+#endif // RGB_MATRIX_ENABLE
 
-/** \brief eeconfig read audio
- *
- * FIXME: needs doc
- */
-uint8_t eeconfig_read_audio(void) {
-    return eeprom_read_byte(EECONFIG_AUDIO);
+#ifdef LED_MATRIX_ENABLE
+void eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config) {
+    nvm_eeconfig_read_led_matrix(led_matrix_config);
 }
-/** \brief eeconfig update audio
- *
- * FIXME: needs doc
- */
-void eeconfig_update_audio(uint8_t val) {
-    eeprom_update_byte(EECONFIG_AUDIO, val);
+void eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config) {
+    nvm_eeconfig_update_led_matrix(led_matrix_config);
 }
+#endif // LED_MATRIX_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+void eeconfig_read_rgblight(rgblight_config_t *rgblight_config) {
+    nvm_eeconfig_read_rgblight(rgblight_config);
+}
+void eeconfig_update_rgblight(const rgblight_config_t *rgblight_config) {
+    nvm_eeconfig_update_rgblight(rgblight_config);
+}
+#endif // RGBLIGHT_ENABLE
 
 #if (EECONFIG_KB_DATA_SIZE) == 0
-/** \brief eeconfig read kb
- *
- * FIXME: needs doc
- */
 uint32_t eeconfig_read_kb(void) {
-    return eeprom_read_dword(EECONFIG_KEYBOARD);
+    return nvm_eeconfig_read_kb();
 }
-/** \brief eeconfig update kb
- *
- * FIXME: needs doc
- */
 void eeconfig_update_kb(uint32_t val) {
-    eeprom_update_dword(EECONFIG_KEYBOARD, val);
+    nvm_eeconfig_update_kb(val);
 }
 #endif // (EECONFIG_KB_DATA_SIZE) == 0
 
 #if (EECONFIG_USER_DATA_SIZE) == 0
-/** \brief eeconfig read user
- *
- * FIXME: needs doc
- */
 uint32_t eeconfig_read_user(void) {
-    return eeprom_read_dword(EECONFIG_USER);
+    return nvm_eeconfig_read_user();
 }
-/** \brief eeconfig update user
- *
- * FIXME: needs doc
- */
 void eeconfig_update_user(uint32_t val) {
-    eeprom_update_dword(EECONFIG_USER, val);
+    nvm_eeconfig_update_user(val);
 }
 #endif // (EECONFIG_USER_DATA_SIZE) == 0
 
-/** \brief eeconfig read haptic
- *
- * FIXME: needs doc
- */
-uint32_t eeconfig_read_haptic(void) {
-    return eeprom_read_dword(EECONFIG_HAPTIC);
-}
-/** \brief eeconfig update haptic
- *
- * FIXME: needs doc
- */
-void eeconfig_update_haptic(uint32_t val) {
-    eeprom_update_dword(EECONFIG_HAPTIC, val);
-}
-
-/** \brief eeconfig read split handedness
- *
- * FIXME: needs doc
- */
+#ifdef HAPTIC_ENABLE
+void eeconfig_read_haptic(haptic_config_t *haptic_config) {
+    nvm_eeconfig_read_haptic(haptic_config);
+}
+void eeconfig_update_haptic(const haptic_config_t *haptic_config) {
+    nvm_eeconfig_update_haptic(haptic_config);
+}
+#endif // HAPTIC_ENABLE
+
 bool eeconfig_read_handedness(void) {
-    return !!eeprom_read_byte(EECONFIG_HANDEDNESS);
+    return nvm_eeconfig_read_handedness();
 }
-/** \brief eeconfig update split handedness
- *
- * FIXME: needs doc
- */
 void eeconfig_update_handedness(bool val) {
-    eeprom_update_byte(EECONFIG_HANDEDNESS, !!val);
+    nvm_eeconfig_update_handedness(val);
 }
 
 #if (EECONFIG_KB_DATA_SIZE) > 0
-/** \brief eeconfig assert keyboard data block version
- *
- * FIXME: needs doc
- */
 bool eeconfig_is_kb_datablock_valid(void) {
-    return eeprom_read_dword(EECONFIG_KEYBOARD) == (EECONFIG_KB_DATA_VERSION);
-}
-/** \brief eeconfig read keyboard data block
- *
- * FIXME: needs doc
- */
-void eeconfig_read_kb_datablock(void *data) {
-    if (eeconfig_is_kb_datablock_valid()) {
-        eeprom_read_block(data, EECONFIG_KB_DATABLOCK, (EECONFIG_KB_DATA_SIZE));
-    } else {
-        memset(data, 0, (EECONFIG_KB_DATA_SIZE));
-    }
+    return nvm_eeconfig_is_kb_datablock_valid();
+}
+uint32_t eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length) {
+    return nvm_eeconfig_read_kb_datablock(data, offset, length);
+}
+uint32_t eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length) {
+    return nvm_eeconfig_update_kb_datablock(data, offset, length);
 }
-/** \brief eeconfig update keyboard data block
- *
- * FIXME: needs doc
- */
-void eeconfig_update_kb_datablock(const void *data) {
-    eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));
-    eeprom_update_block(data, EECONFIG_KB_DATABLOCK, (EECONFIG_KB_DATA_SIZE));
-}
-/** \brief eeconfig init keyboard data block
- *
- * FIXME: needs doc
- */
 __attribute__((weak)) void eeconfig_init_kb_datablock(void) {
-    uint8_t dummy_kb[(EECONFIG_KB_DATA_SIZE)] = {0};
-    eeconfig_update_kb_datablock(dummy_kb);
+    nvm_eeconfig_init_kb_datablock();
 }
 #endif // (EECONFIG_KB_DATA_SIZE) > 0
 
 #if (EECONFIG_USER_DATA_SIZE) > 0
-/** \brief eeconfig assert user data block version
- *
- * FIXME: needs doc
- */
 bool eeconfig_is_user_datablock_valid(void) {
-    return eeprom_read_dword(EECONFIG_USER) == (EECONFIG_USER_DATA_VERSION);
-}
-/** \brief eeconfig read user data block
- *
- * FIXME: needs doc
- */
-void eeconfig_read_user_datablock(void *data) {
-    if (eeconfig_is_user_datablock_valid()) {
-        eeprom_read_block(data, EECONFIG_USER_DATABLOCK, (EECONFIG_USER_DATA_SIZE));
-    } else {
-        memset(data, 0, (EECONFIG_USER_DATA_SIZE));
-    }
+    return nvm_eeconfig_is_user_datablock_valid();
+}
+uint32_t eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length) {
+    return nvm_eeconfig_read_user_datablock(data, offset, length);
+}
+uint32_t eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length) {
+    return nvm_eeconfig_update_user_datablock(data, offset, length);
 }
-/** \brief eeconfig update user data block
- *
- * FIXME: needs doc
- */
-void eeconfig_update_user_datablock(const void *data) {
-    eeprom_update_dword(EECONFIG_USER, (EECONFIG_USER_DATA_VERSION));
-    eeprom_update_block(data, EECONFIG_USER_DATABLOCK, (EECONFIG_USER_DATA_SIZE));
-}
-/** \brief eeconfig init user data block
- *
- * FIXME: needs doc
- */
 __attribute__((weak)) void eeconfig_init_user_datablock(void) {
-    uint8_t dummy_user[(EECONFIG_USER_DATA_SIZE)] = {0};
-    eeconfig_update_user_datablock(dummy_user);
+    nvm_eeconfig_init_user_datablock();
 }
 #endif // (EECONFIG_USER_DATA_SIZE) > 0

+ 71 - 85
quantum/eeconfig.h

@@ -19,59 +19,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <stdint.h>
 #include <stdbool.h>
-#include <stddef.h> // offsetof
-#include "eeprom.h"
-#include "util.h"
+#include <stddef.h>       // offsetof
 #include "action_layer.h" // layer_state_t
 
-#ifndef EECONFIG_MAGIC_NUMBER
-#    define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE5 // When changing, decrement this value to avoid future re-init issues
-#endif
-#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF
-
-// Dummy struct only used to calculate offsets
-typedef struct PACKED {
-    uint16_t magic;
-    uint8_t  debug;
-    uint8_t  default_layer;
-    uint16_t keymap;
-    uint8_t  backlight;
-    uint8_t  audio;
-    uint32_t rgblight;
-    uint8_t  unicode;
-    uint8_t  steno;
-    uint8_t  handedness;
-    uint32_t keyboard;
-    uint32_t user;
-    union { // Mutually exclusive
-        uint32_t led_matrix;
-        uint64_t rgb_matrix;
-    };
-    uint32_t haptic;
-    uint8_t  rgblight_ext;
-} eeprom_core_t;
-
-/* EEPROM parameter address */
-#define EECONFIG_MAGIC (uint16_t *)(offsetof(eeprom_core_t, magic))
-#define EECONFIG_DEBUG (uint8_t *)(offsetof(eeprom_core_t, debug))
-#define EECONFIG_DEFAULT_LAYER (uint8_t *)(offsetof(eeprom_core_t, default_layer))
-#define EECONFIG_KEYMAP (uint16_t *)(offsetof(eeprom_core_t, keymap))
-#define EECONFIG_BACKLIGHT (uint8_t *)(offsetof(eeprom_core_t, backlight))
-#define EECONFIG_AUDIO (uint8_t *)(offsetof(eeprom_core_t, audio))
-#define EECONFIG_RGBLIGHT (uint32_t *)(offsetof(eeprom_core_t, rgblight))
-#define EECONFIG_UNICODEMODE (uint8_t *)(offsetof(eeprom_core_t, unicode))
-#define EECONFIG_STENOMODE (uint8_t *)(offsetof(eeprom_core_t, steno))
-#define EECONFIG_HANDEDNESS (uint8_t *)(offsetof(eeprom_core_t, handedness))
-#define EECONFIG_KEYBOARD (uint32_t *)(offsetof(eeprom_core_t, keyboard))
-#define EECONFIG_USER (uint32_t *)(offsetof(eeprom_core_t, user))
-#define EECONFIG_LED_MATRIX (uint32_t *)(offsetof(eeprom_core_t, led_matrix))
-#define EECONFIG_RGB_MATRIX (uint64_t *)(offsetof(eeprom_core_t, rgb_matrix))
-#define EECONFIG_HAPTIC (uint32_t *)(offsetof(eeprom_core_t, haptic))
-#define EECONFIG_RGBLIGHT_EXTENDED (uint8_t *)(offsetof(eeprom_core_t, rgblight_ext))
-
-// Size of EEPROM being used for core data storage
-#define EECONFIG_BASE_SIZE ((uint8_t)sizeof(eeprom_core_t))
-
 // Size of EEPROM dedicated to keyboard- and user-specific data
 #ifndef EECONFIG_KB_DATA_SIZE
 #    define EECONFIG_KB_DATA_SIZE 0
@@ -86,12 +36,6 @@ typedef struct PACKED {
 #    define EECONFIG_USER_DATA_VERSION (EECONFIG_USER_DATA_SIZE)
 #endif
 
-#define EECONFIG_KB_DATABLOCK ((uint8_t *)(EECONFIG_BASE_SIZE))
-#define EECONFIG_USER_DATABLOCK ((uint8_t *)((EECONFIG_BASE_SIZE) + (EECONFIG_KB_DATA_SIZE)))
-
-// Size of EEPROM being used, other code can refer to this for available EEPROM
-#define EECONFIG_SIZE ((EECONFIG_BASE_SIZE) + (EECONFIG_KB_DATA_SIZE) + (EECONFIG_USER_DATA_SIZE))
-
 /* debug bit */
 #define EECONFIG_DEBUG_ENABLE (1 << 0)
 #define EECONFIG_DEBUG_MATRIX (1 << 1)
@@ -117,22 +61,59 @@ void eeconfig_init_kb(void);
 void eeconfig_init_user(void);
 
 void eeconfig_enable(void);
-
 void eeconfig_disable(void);
 
-uint8_t eeconfig_read_debug(void);
-void    eeconfig_update_debug(uint8_t val);
+typedef union debug_config_t debug_config_t;
+void                         eeconfig_read_debug(debug_config_t *debug_config) __attribute__((nonnull));
+void                         eeconfig_update_debug(const debug_config_t *debug_config) __attribute__((nonnull));
 
 layer_state_t eeconfig_read_default_layer(void);
-void          eeconfig_update_default_layer(layer_state_t val);
+void          eeconfig_update_default_layer(layer_state_t state);
 
-uint16_t eeconfig_read_keymap(void);
-void     eeconfig_update_keymap(uint16_t val);
+typedef union keymap_config_t keymap_config_t;
+void                          eeconfig_read_keymap(keymap_config_t *keymap_config) __attribute__((nonnull));
+void                          eeconfig_update_keymap(const keymap_config_t *keymap_config) __attribute__((nonnull));
 
 #ifdef AUDIO_ENABLE
-uint8_t eeconfig_read_audio(void);
-void    eeconfig_update_audio(uint8_t val);
-#endif
+typedef union audio_config_t audio_config_t;
+void                         eeconfig_read_audio(audio_config_t *audio_config) __attribute__((nonnull));
+void                         eeconfig_update_audio(const audio_config_t *audio_config) __attribute__((nonnull));
+#endif // AUDIO_ENABLE
+
+#ifdef UNICODE_COMMON_ENABLE
+typedef union unicode_config_t unicode_config_t;
+void                           eeconfig_read_unicode_mode(unicode_config_t *unicode_config) __attribute__((nonnull));
+void                           eeconfig_update_unicode_mode(const unicode_config_t *unicode_config) __attribute__((nonnull));
+#endif // UNICODE_COMMON_ENABLE
+
+#ifdef BACKLIGHT_ENABLE
+typedef union backlight_config_t backlight_config_t;
+void                             eeconfig_read_backlight(backlight_config_t *backlight_config) __attribute__((nonnull));
+void                             eeconfig_update_backlight(const backlight_config_t *backlight_config) __attribute__((nonnull));
+#endif // BACKLIGHT_ENABLE
+
+#ifdef STENO_ENABLE
+uint8_t eeconfig_read_steno_mode(void);
+void    eeconfig_update_steno_mode(uint8_t val);
+#endif // STENO_ENABLE
+
+#ifdef RGB_MATRIX_ENABLE
+typedef union rgb_config_t rgb_config_t;
+void                       eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config) __attribute__((nonnull));
+void                       eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config) __attribute__((nonnull));
+#endif // RGB_MATRIX_ENABLE
+
+#ifdef LED_MATRIX_ENABLE
+typedef union led_eeconfig_t led_eeconfig_t;
+void                         eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config) __attribute__((nonnull));
+void                         eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config) __attribute__((nonnull));
+#endif // LED_MATRIX_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+typedef union rgblight_config_t rgblight_config_t;
+void                            eeconfig_read_rgblight(rgblight_config_t *rgblight_config) __attribute__((nonnull));
+void                            eeconfig_update_rgblight(const rgblight_config_t *rgblight_config) __attribute__((nonnull));
+#endif // RGBLIGHT_ENABLE
 
 #if (EECONFIG_KB_DATA_SIZE) == 0
 uint32_t eeconfig_read_kb(void);
@@ -145,31 +126,36 @@ void     eeconfig_update_user(uint32_t val);
 #endif // (EECONFIG_USER_DATA_SIZE) == 0
 
 #ifdef HAPTIC_ENABLE
-uint32_t eeconfig_read_haptic(void);
-void     eeconfig_update_haptic(uint32_t val);
+typedef union haptic_config_t haptic_config_t;
+void                          eeconfig_read_haptic(haptic_config_t *haptic_config) __attribute__((nonnull));
+void                          eeconfig_update_haptic(const haptic_config_t *haptic_config) __attribute__((nonnull));
 #endif
 
 bool eeconfig_read_handedness(void);
 void eeconfig_update_handedness(bool val);
 
 #if (EECONFIG_KB_DATA_SIZE) > 0
-bool eeconfig_is_kb_datablock_valid(void);
-void eeconfig_read_kb_datablock(void *data);
-void eeconfig_update_kb_datablock(const void *data);
-void eeconfig_init_kb_datablock(void);
+bool     eeconfig_is_kb_datablock_valid(void);
+uint32_t eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));
+uint32_t eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));
+void     eeconfig_init_kb_datablock(void);
+#    define eeconfig_read_kb_datablock_field(__object, __field) eeconfig_read_kb_datablock(&(__object.__field), offsetof(typeof(__object), __field), sizeof(__object.__field))
+#    define eeconfig_update_kb_datablock_field(__object, __field) eeconfig_update_kb_datablock(&(__object.__field), offsetof(typeof(__object), __field), sizeof(__object.__field))
 #endif // (EECONFIG_KB_DATA_SIZE) > 0
 
 #if (EECONFIG_USER_DATA_SIZE) > 0
-bool eeconfig_is_user_datablock_valid(void);
-void eeconfig_read_user_datablock(void *data);
-void eeconfig_update_user_datablock(const void *data);
-void eeconfig_init_user_datablock(void);
+bool     eeconfig_is_user_datablock_valid(void);
+uint32_t eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));
+uint32_t eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));
+void     eeconfig_init_user_datablock(void);
+#    define eeconfig_read_user_datablock_field(__object, __field) eeconfig_read_user_datablock(&(__object.__field), offsetof(__object, __field), sizeof(__object.__field))
+#    define eeconfig_update_user_datablock_field(__object, __field) eeconfig_update_user_datablock(&(__object.__field), offsetof(__object, __field), sizeof(__object.__field))
 #endif // (EECONFIG_USER_DATA_SIZE) > 0
 
 // Any "checked" debounce variant used requires implementation of:
 //    -- bool eeconfig_check_valid_##name(void)
 //    -- void eeconfig_post_flush_##name(void)
-#define EECONFIG_DEBOUNCE_HELPER_CHECKED(name, offset, config)          \
+#define EECONFIG_DEBOUNCE_HELPER_CHECKED(name, config)                  \
     static uint8_t dirty_##name = false;                                \
                                                                         \
     bool eeconfig_check_valid_##name(void);                             \
@@ -178,13 +164,13 @@ void eeconfig_init_user_datablock(void);
     static inline void eeconfig_init_##name(void) {                     \
         dirty_##name = true;                                            \
         if (eeconfig_check_valid_##name()) {                            \
-            eeprom_read_block(&config, offset, sizeof(config));         \
+            eeconfig_read_##name(&config);                              \
             dirty_##name = false;                                       \
         }                                                               \
     }                                                                   \
     static inline void eeconfig_flush_##name(bool force) {              \
         if (force || dirty_##name) {                                    \
-            eeprom_update_block(&config, offset, sizeof(config));       \
+            eeconfig_update_##name(&config);                            \
             eeconfig_post_flush_##name();                               \
             dirty_##name = false;                                       \
         }                                                               \
@@ -206,10 +192,10 @@ void eeconfig_init_user_datablock(void);
         }                                                               \
     }
 
-#define EECONFIG_DEBOUNCE_HELPER(name, offset, config)     \
-    EECONFIG_DEBOUNCE_HELPER_CHECKED(name, offset, config) \
-                                                           \
-    bool eeconfig_check_valid_##name(void) {               \
-        return true;                                       \
-    }                                                      \
+#define EECONFIG_DEBOUNCE_HELPER(name, config)     \
+    EECONFIG_DEBOUNCE_HELPER_CHECKED(name, config) \
+                                                   \
+    bool eeconfig_check_valid_##name(void) {       \
+        return true;                               \
+    }                                              \
     void eeconfig_post_flush_##name(void) {}

+ 13 - 13
quantum/haptic.c

@@ -67,7 +67,7 @@ void haptic_init(void) {
     if (!eeconfig_is_enabled()) {
         eeconfig_init();
     }
-    haptic_config.raw = eeconfig_read_haptic();
+    eeconfig_read_haptic(&haptic_config);
 #ifdef HAPTIC_SOLENOID
     solenoid_set_dwell(haptic_config.dwell);
 #endif
@@ -122,13 +122,13 @@ void eeconfig_debug_haptic(void) {
 void haptic_enable(void) {
     set_haptic_config_enable(true);
     dprintf("haptic_config.enable = %u\n", haptic_config.enable);
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
 }
 
 void haptic_disable(void) {
     set_haptic_config_enable(false);
     dprintf("haptic_config.enable = %u\n", haptic_config.enable);
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
 }
 
 void haptic_toggle(void) {
@@ -137,14 +137,14 @@ void haptic_toggle(void) {
     } else {
         haptic_enable();
     }
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
 }
 
 void haptic_feedback_toggle(void) {
     haptic_config.feedback++;
     if (haptic_config.feedback >= HAPTIC_FEEDBACK_MAX) haptic_config.feedback = KEY_PRESS;
     dprintf("haptic_config.feedback = %u\n", !haptic_config.feedback);
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
 }
 
 void haptic_buzz_toggle(void) {
@@ -225,26 +225,26 @@ void haptic_reset(void) {
     haptic_config.dwell = 0;
     haptic_config.buzz  = 0;
 #endif
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
     dprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
     dprintf("haptic_config.mode = %u\n", haptic_config.mode);
 }
 
 void haptic_set_feedback(uint8_t feedback) {
     haptic_config.feedback = feedback;
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
     dprintf("haptic_config.feedback = %u\n", haptic_config.feedback);
 }
 
 void haptic_set_mode(uint8_t mode) {
     haptic_config.mode = mode;
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
     dprintf("haptic_config.mode = %u\n", haptic_config.mode);
 }
 
 void haptic_set_amplitude(uint8_t amp) {
     haptic_config.amplitude = amp;
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
     dprintf("haptic_config.amplitude = %u\n", haptic_config.amplitude);
 #ifdef HAPTIC_DRV2605L
     drv2605l_amplitude(amp);
@@ -253,13 +253,13 @@ void haptic_set_amplitude(uint8_t amp) {
 
 void haptic_set_buzz(uint8_t buzz) {
     haptic_config.buzz = buzz;
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
     dprintf("haptic_config.buzz = %u\n", haptic_config.buzz);
 }
 
 void haptic_set_dwell(uint8_t dwell) {
     haptic_config.dwell = dwell;
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
     dprintf("haptic_config.dwell = %u\n", haptic_config.dwell);
 }
 
@@ -291,7 +291,7 @@ uint8_t haptic_get_dwell(void) {
 void haptic_enable_continuous(void) {
     haptic_config.cont = 1;
     dprintf("haptic_config.cont = %u\n", haptic_config.cont);
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
 #ifdef HAPTIC_DRV2605L
     drv2605l_rtp_init();
 #endif
@@ -300,7 +300,7 @@ void haptic_enable_continuous(void) {
 void haptic_disable_continuous(void) {
     haptic_config.cont = 0;
     dprintf("haptic_config.cont = %u\n", haptic_config.cont);
-    eeconfig_update_haptic(haptic_config.raw);
+    eeconfig_update_haptic(&haptic_config);
 #ifdef HAPTIC_DRV2605L
     drv2605l_write(DRV2605L_REG_MODE, 0x00);
 #endif

+ 1 - 1
quantum/haptic.h

@@ -28,7 +28,7 @@
 #endif
 
 /* EEPROM config settings */
-typedef union {
+typedef union haptic_config_t {
     uint32_t raw;
     struct {
         bool    enable : 1;

+ 3 - 3
quantum/keyboard.c

@@ -432,8 +432,8 @@ void quantum_init(void) {
     }
 
     /* init globals */
-    debug_config.raw  = eeconfig_read_debug();
-    keymap_config.raw = eeconfig_read_keymap();
+    eeconfig_read_debug(&debug_config);
+    eeconfig_read_keymap(&keymap_config);
 
 #ifdef BOOTMAGIC_ENABLE
     bootmagic();
@@ -504,7 +504,7 @@ void keyboard_init(void) {
 #endif
 #if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
     keymap_config.nkro = 1;
-    eeconfig_update_keymap(keymap_config.raw);
+    eeconfig_update_keymap(&keymap_config);
 #endif
 #ifdef DIP_SWITCH_ENABLE
     dip_switch_init();

+ 1 - 1
quantum/keycode_config.h

@@ -28,7 +28,7 @@ uint16_t keycode_config(uint16_t keycode);
 uint8_t  mod_config(uint8_t mod);
 
 /* NOTE: Not portable. Bit field order depends on implementation */
-typedef union {
+typedef union keymap_config_t {
     uint16_t raw;
     struct {
         bool swap_control_capslock : 1;

+ 2 - 2
quantum/led_matrix/led_matrix.c

@@ -86,9 +86,9 @@ static last_hit_t last_hit_buffer;
 const uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;
 #endif
 
-EECONFIG_DEBOUNCE_HELPER(led_matrix, EECONFIG_LED_MATRIX, led_matrix_eeconfig);
+EECONFIG_DEBOUNCE_HELPER(led_matrix, led_matrix_eeconfig);
 
-void eeconfig_update_led_matrix(void) {
+void eeconfig_force_flush_led_matrix(void) {
     eeconfig_flush_led_matrix(true);
 }
 

+ 1 - 1
quantum/led_matrix/led_matrix.h

@@ -115,7 +115,7 @@ enum led_matrix_effects {
 };
 
 void eeconfig_update_led_matrix_default(void);
-void eeconfig_update_led_matrix(void);
+void eeconfig_force_flush_led_matrix(void);
 void eeconfig_debug_led_matrix(void);
 
 uint8_t led_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i);

+ 1 - 1
quantum/led_matrix/led_matrix_types.h

@@ -71,7 +71,7 @@ typedef struct PACKED {
     uint8_t     flags[LED_MATRIX_LED_COUNT];
 } led_config_t;
 
-typedef union {
+typedef union led_eeconfig_t {
     uint32_t raw;
     struct PACKED {
         uint8_t     enable : 2;

+ 1 - 1
quantum/logging/debug.h

@@ -28,7 +28,7 @@ extern "C" {
 /*
  * Debug output control
  */
-typedef union {
+typedef union debug_config_t {
     struct {
         bool    enable : 1;
         bool    matrix : 1;

+ 193 - 0
quantum/nvm/eeprom/nvm_dynamic_keymap.c

@@ -0,0 +1,193 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "keycodes.h"
+#include "eeprom.h"
+#include "dynamic_keymap.h"
+#include "nvm_dynamic_keymap.h"
+#include "nvm_eeprom_eeconfig_internal.h"
+#include "nvm_eeprom_via_internal.h"
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+#ifdef ENCODER_ENABLE
+#    include "encoder.h"
+#endif
+
+#ifdef VIA_ENABLE
+#    include "via.h"
+#    define DYNAMIC_KEYMAP_EEPROM_START (VIA_EEPROM_CONFIG_END)
+#else
+#    define DYNAMIC_KEYMAP_EEPROM_START (EECONFIG_SIZE)
+#endif
+
+#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
+#    define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR (TOTAL_EEPROM_BYTE_COUNT - 1)
+#endif
+
+_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");
+
+// Due to usage of uint16_t check for max 65535
+_Static_assert(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR <= 65535, "DYNAMIC_KEYMAP_EEPROM_MAX_ADDR must be less than 65536");
+
+// If DYNAMIC_KEYMAP_EEPROM_ADDR not explicitly defined in config.h,
+#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR
+#    define DYNAMIC_KEYMAP_EEPROM_ADDR DYNAMIC_KEYMAP_EEPROM_START
+#endif
+
+// Dynamic encoders starts after dynamic keymaps
+#ifndef DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR
+#    define DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2))
+#endif
+
+// Dynamic macro starts after dynamic encoders, but only when using ENCODER_MAP
+#ifdef ENCODER_MAP_ENABLE
+#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
+#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * NUM_ENCODERS * 2 * 2))
+#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
+#else      // ENCODER_MAP_ENABLE
+#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
+#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR)
+#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
+#endif     // ENCODER_MAP_ENABLE
+
+// Sanity check that dynamic keymaps fit in available EEPROM
+// If there's not 100 bytes available for macros, then something is wrong.
+// The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,
+// or DYNAMIC_KEYMAP_EEPROM_MAX_ADDR to increase it, *only if* the microcontroller has
+// more than the default.
+_Static_assert((DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) >= 100, "Dynamic keymaps are configured to use more EEPROM than is available.");
+
+#ifndef TOTAL_EEPROM_BYTE_COUNT
+#    error Unknown total EEPROM size. Cannot derive maximum for dynamic keymaps.
+#endif
+// Dynamic macros are stored after the keymaps and use what is available
+// up to and including DYNAMIC_KEYMAP_EEPROM_MAX_ADDR.
+#ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE
+#    define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + 1)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void nvm_dynamic_keymap_erase(void) {
+    // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.
+}
+
+void nvm_dynamic_keymap_macro_erase(void) {
+    // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.
+}
+
+static inline void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column) {
+    return ((void *)DYNAMIC_KEYMAP_EEPROM_ADDR) + (layer * MATRIX_ROWS * MATRIX_COLS * 2) + (row * MATRIX_COLS * 2) + (column * 2);
+}
+
+uint16_t nvm_dynamic_keymap_read_keycode(uint8_t layer, uint8_t row, uint8_t column) {
+    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return KC_NO;
+    void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
+    // Big endian, so we can read/write EEPROM directly from host if we want
+    uint16_t keycode = eeprom_read_byte(address) << 8;
+    keycode |= eeprom_read_byte(address + 1);
+    return keycode;
+}
+
+void nvm_dynamic_keymap_update_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) {
+    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return;
+    void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
+    // Big endian, so we can read/write EEPROM directly from host if we want
+    eeprom_update_byte(address, (uint8_t)(keycode >> 8));
+    eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));
+}
+
+#ifdef ENCODER_MAP_ENABLE
+static void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t encoder_id) {
+    return ((void *)DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) + (layer * NUM_ENCODERS * 2 * 2) + (encoder_id * 2 * 2);
+}
+
+uint16_t nvm_dynamic_keymap_read_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) {
+    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return KC_NO;
+    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
+    // Big endian, so we can read/write EEPROM directly from host if we want
+    uint16_t keycode = ((uint16_t)eeprom_read_byte(address + (clockwise ? 0 : 2))) << 8;
+    keycode |= eeprom_read_byte(address + (clockwise ? 0 : 2) + 1);
+    return keycode;
+}
+
+void nvm_dynamic_keymap_update_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) {
+    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return;
+    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);
+    // Big endian, so we can read/write EEPROM directly from host if we want
+    eeprom_update_byte(address + (clockwise ? 0 : 2), (uint8_t)(keycode >> 8));
+    eeprom_update_byte(address + (clockwise ? 0 : 2) + 1, (uint8_t)(keycode & 0xFF));
+}
+#endif // ENCODER_MAP_ENABLE
+
+void nvm_dynamic_keymap_read_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
+    uint32_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
+    void *   source                     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
+    uint8_t *target                     = data;
+    for (uint32_t i = 0; i < size; i++) {
+        if (offset + i < dynamic_keymap_eeprom_size) {
+            *target = eeprom_read_byte(source);
+        } else {
+            *target = 0x00;
+        }
+        source++;
+        target++;
+    }
+}
+
+void nvm_dynamic_keymap_update_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
+    uint32_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
+    void *   target                     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);
+    uint8_t *source                     = data;
+    for (uint32_t i = 0; i < size; i++) {
+        if (offset + i < dynamic_keymap_eeprom_size) {
+            eeprom_update_byte(target, *source);
+        }
+        source++;
+        target++;
+    }
+}
+
+uint32_t nvm_dynamic_keymap_macro_size(void) {
+    return DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE;
+}
+
+void nvm_dynamic_keymap_macro_read_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
+    void *   source = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
+    uint8_t *target = data;
+    for (uint16_t i = 0; i < size; i++) {
+        if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
+            *target = eeprom_read_byte(source);
+        } else {
+            *target = 0x00;
+        }
+        source++;
+        target++;
+    }
+}
+
+void nvm_dynamic_keymap_macro_update_buffer(uint32_t offset, uint32_t size, uint8_t *data) {
+    void *   target = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);
+    uint8_t *source = data;
+    for (uint16_t i = 0; i < size; i++) {
+        if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {
+            eeprom_update_byte(target, *source);
+        }
+        source++;
+        target++;
+    }
+}
+
+void nvm_dynamic_keymap_macro_reset(void) {
+    void *  start     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
+    void *  end       = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
+    long    remaining = end - start;
+    uint8_t dummy[16] = {0};
+    for (int i = 0; i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE; i += sizeof(dummy)) {
+        int this_loop = remaining < sizeof(dummy) ? remaining : sizeof(dummy);
+        eeprom_update_block(dummy, start, this_loop);
+        start += this_loop;
+        remaining -= this_loop;
+    }
+}

+ 292 - 0
quantum/nvm/eeprom/nvm_eeconfig.c

@@ -0,0 +1,292 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <string.h>
+#include "nvm_eeconfig.h"
+#include "nvm_eeprom_eeconfig_internal.h"
+#include "util.h"
+#include "eeconfig.h"
+#include "debug.h"
+#include "eeprom.h"
+#include "keycode_config.h"
+
+#ifdef EEPROM_DRIVER
+#    include "eeprom_driver.h"
+#endif
+
+#ifdef AUDIO_ENABLE
+#    include "audio.h"
+#endif
+
+#ifdef BACKLIGHT_ENABLE
+#    include "backlight.h"
+#endif
+
+#ifdef RGBLIGHT_ENABLE
+#    include "rgblight.h"
+#endif
+
+#ifdef RGB_MATRIX_ENABLE
+#    include "rgb_matrix_types.h"
+#endif
+
+#ifdef LED_MATRIX_ENABLE
+#    include "led_matrix_types.h"
+#endif
+
+#ifdef UNICODE_COMMON_ENABLE
+#    include "unicode.h"
+#endif
+
+#ifdef HAPTIC_ENABLE
+#    include "haptic.h"
+#endif
+
+void nvm_eeconfig_erase(void) {
+#ifdef EEPROM_DRIVER
+    eeprom_driver_format(false);
+#endif // EEPROM_DRIVER
+}
+
+bool nvm_eeconfig_is_enabled(void) {
+    return eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER;
+}
+
+bool nvm_eeconfig_is_disabled(void) {
+    return eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF;
+}
+
+void nvm_eeconfig_enable(void) {
+    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);
+}
+
+void nvm_eeconfig_disable(void) {
+#if defined(EEPROM_DRIVER)
+    eeprom_driver_format(false);
+#endif
+    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF);
+}
+
+void nvm_eeconfig_read_debug(debug_config_t *debug_config) {
+    debug_config->raw = eeprom_read_byte(EECONFIG_DEBUG);
+}
+void nvm_eeconfig_update_debug(const debug_config_t *debug_config) {
+    eeprom_update_byte(EECONFIG_DEBUG, debug_config->raw);
+}
+
+layer_state_t nvm_eeconfig_read_default_layer(void) {
+    uint8_t val = eeprom_read_byte(EECONFIG_DEFAULT_LAYER);
+#ifdef DEFAULT_LAYER_STATE_IS_VALUE_NOT_BITMASK
+    // stored as a layer number, so convert back to bitmask
+    return (layer_state_t)1 << val;
+#else
+    // stored as 8-bit-wide bitmask, so read the value directly - handling padding to 16/32 bit layer_state_t
+    return (layer_state_t)val;
+#endif
+}
+void nvm_eeconfig_update_default_layer(layer_state_t state) {
+#ifdef DEFAULT_LAYER_STATE_IS_VALUE_NOT_BITMASK
+    // stored as a layer number, so only store the highest layer
+    uint8_t val = get_highest_layer(state);
+#else
+    // stored as 8-bit-wide bitmask, so write the value directly - handling truncation from 16/32 bit layer_state_t
+    uint8_t val = (uint8_t)state;
+#endif
+    eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val);
+}
+
+void nvm_eeconfig_read_keymap(keymap_config_t *keymap_config) {
+    keymap_config->raw = eeprom_read_word(EECONFIG_KEYMAP);
+}
+void nvm_eeconfig_update_keymap(const keymap_config_t *keymap_config) {
+    eeprom_update_word(EECONFIG_KEYMAP, keymap_config->raw);
+}
+
+#ifdef AUDIO_ENABLE
+void nvm_eeconfig_read_audio(audio_config_t *audio_config) {
+    audio_config->raw = eeprom_read_byte(EECONFIG_AUDIO);
+}
+void nvm_eeconfig_update_audio(const audio_config_t *audio_config) {
+    eeprom_update_byte(EECONFIG_AUDIO, audio_config->raw);
+}
+#endif // AUDIO_ENABLE
+
+#ifdef UNICODE_COMMON_ENABLE
+void nvm_eeconfig_read_unicode_mode(unicode_config_t *unicode_config) {
+    unicode_config->raw = eeprom_read_byte(EECONFIG_UNICODEMODE);
+}
+void nvm_eeconfig_update_unicode_mode(const unicode_config_t *unicode_config) {
+    eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config->raw);
+}
+#endif // UNICODE_COMMON_ENABLE
+
+#ifdef BACKLIGHT_ENABLE
+void nvm_eeconfig_read_backlight(backlight_config_t *backlight_config) {
+    backlight_config->raw = eeprom_read_byte(EECONFIG_BACKLIGHT);
+}
+void nvm_eeconfig_update_backlight(const backlight_config_t *backlight_config) {
+    eeprom_update_byte(EECONFIG_BACKLIGHT, backlight_config->raw);
+}
+#endif // BACKLIGHT_ENABLE
+
+#ifdef STENO_ENABLE
+uint8_t nvm_eeconfig_read_steno_mode(void) {
+    return eeprom_read_byte(EECONFIG_STENOMODE);
+}
+void nvm_eeconfig_update_steno_mode(uint8_t val) {
+    eeprom_update_byte(EECONFIG_STENOMODE, val);
+}
+#endif // STENO_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+#endif // RGBLIGHT_ENABLE
+
+#ifdef RGB_MATRIX_ENABLE
+void nvm_eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config) {
+    eeprom_read_block(rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_config_t));
+}
+void nvm_eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config) {
+    eeprom_update_block(rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_config_t));
+}
+#endif // RGB_MATRIX_ENABLE
+
+#ifdef LED_MATRIX_ENABLE
+void nvm_eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config) {
+    eeprom_read_block(led_matrix_config, EECONFIG_LED_MATRIX, sizeof(led_eeconfig_t));
+}
+void nvm_eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config) {
+    eeprom_update_block(led_matrix_config, EECONFIG_LED_MATRIX, sizeof(led_eeconfig_t));
+}
+#endif // LED_MATRIX_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+void nvm_eeconfig_read_rgblight(rgblight_config_t *rgblight_config) {
+    rgblight_config->raw = eeprom_read_dword(EECONFIG_RGBLIGHT);
+    rgblight_config->raw |= ((uint64_t)eeprom_read_byte(EECONFIG_RGBLIGHT_EXTENDED) << 32);
+}
+void nvm_eeconfig_update_rgblight(const rgblight_config_t *rgblight_config) {
+    eeprom_update_dword(EECONFIG_RGBLIGHT, rgblight_config->raw & 0xFFFFFFFF);
+    eeprom_update_byte(EECONFIG_RGBLIGHT_EXTENDED, (rgblight_config->raw >> 32) & 0xFF);
+}
+#endif // RGBLIGHT_ENABLE
+
+#if (EECONFIG_KB_DATA_SIZE) == 0
+uint32_t nvm_eeconfig_read_kb(void) {
+    return eeprom_read_dword(EECONFIG_KEYBOARD);
+}
+void nvm_eeconfig_update_kb(uint32_t val) {
+    eeprom_update_dword(EECONFIG_KEYBOARD, val);
+}
+#endif // (EECONFIG_KB_DATA_SIZE) == 0
+
+#if (EECONFIG_USER_DATA_SIZE) == 0
+uint32_t nvm_eeconfig_read_user(void) {
+    return eeprom_read_dword(EECONFIG_USER);
+}
+void nvm_eeconfig_update_user(uint32_t val) {
+    eeprom_update_dword(EECONFIG_USER, val);
+}
+#endif // (EECONFIG_USER_DATA_SIZE) == 0
+
+#ifdef HAPTIC_ENABLE
+void nvm_eeconfig_read_haptic(haptic_config_t *haptic_config) {
+    haptic_config->raw = eeprom_read_dword(EECONFIG_HAPTIC);
+}
+void nvm_eeconfig_update_haptic(const haptic_config_t *haptic_config) {
+    eeprom_update_dword(EECONFIG_HAPTIC, haptic_config->raw);
+}
+#endif // HAPTIC_ENABLE
+
+bool nvm_eeconfig_read_handedness(void) {
+    return !!eeprom_read_byte(EECONFIG_HANDEDNESS);
+}
+void nvm_eeconfig_update_handedness(bool val) {
+    eeprom_update_byte(EECONFIG_HANDEDNESS, !!val);
+}
+
+#if (EECONFIG_KB_DATA_SIZE) > 0
+
+bool nvm_eeconfig_is_kb_datablock_valid(void) {
+    return eeprom_read_dword(EECONFIG_KEYBOARD) == (EECONFIG_KB_DATA_VERSION);
+}
+
+uint32_t nvm_eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length) {
+    if (eeconfig_is_kb_datablock_valid()) {
+        void *ee_start = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + offset);
+        void *ee_end   = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + MIN(EECONFIG_KB_DATA_SIZE, offset + length));
+        eeprom_read_block(data, ee_start, ee_end - ee_start);
+        return ee_end - ee_start;
+    } else {
+        memset(data, 0, length);
+        return length;
+    }
+}
+
+uint32_t nvm_eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length) {
+    eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));
+
+    void *ee_start = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + offset);
+    void *ee_end   = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + MIN(EECONFIG_KB_DATA_SIZE, offset + length));
+    eeprom_update_block(data, ee_start, ee_end - ee_start);
+    return ee_end - ee_start;
+}
+
+void nvm_eeconfig_init_kb_datablock(void) {
+    eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));
+
+    void *  start     = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK);
+    void *  end       = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + EECONFIG_KB_DATA_SIZE);
+    long    remaining = end - start;
+    uint8_t dummy[16] = {0};
+    for (int i = 0; i < EECONFIG_KB_DATA_SIZE; i += sizeof(dummy)) {
+        int this_loop = remaining < sizeof(dummy) ? remaining : sizeof(dummy);
+        eeprom_update_block(dummy, start, this_loop);
+        start += this_loop;
+        remaining -= this_loop;
+    }
+}
+
+#endif // (EECONFIG_KB_DATA_SIZE) > 0
+
+#if (EECONFIG_USER_DATA_SIZE) > 0
+
+bool nvm_eeconfig_is_user_datablock_valid(void) {
+    return eeprom_read_dword(EECONFIG_USER) == (EECONFIG_USER_DATA_VERSION);
+}
+
+uint32_t nvm_eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length) {
+    if (eeconfig_is_user_datablock_valid()) {
+        void *ee_start = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + offset);
+        void *ee_end   = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + MIN(EECONFIG_USER_DATA_SIZE, offset + length));
+        eeprom_read_block(data, ee_start, ee_end - ee_start);
+        return ee_end - ee_start;
+    } else {
+        memset(data, 0, length);
+        return length;
+    }
+}
+
+uint32_t nvm_eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length) {
+    eeprom_update_dword(EECONFIG_USER, (EECONFIG_USER_DATA_VERSION));
+
+    void *ee_start = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + offset);
+    void *ee_end   = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + MIN(EECONFIG_USER_DATA_SIZE, offset + length));
+    eeprom_update_block(data, ee_start, ee_end - ee_start);
+    return ee_end - ee_start;
+}
+
+void nvm_eeconfig_init_user_datablock(void) {
+    eeprom_update_dword(EECONFIG_USER, (EECONFIG_USER_DATA_VERSION));
+
+    void *  start     = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK);
+    void *  end       = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + EECONFIG_USER_DATA_SIZE);
+    long    remaining = end - start;
+    uint8_t dummy[16] = {0};
+    for (int i = 0; i < EECONFIG_USER_DATA_SIZE; i += sizeof(dummy)) {
+        int this_loop = remaining < sizeof(dummy) ? remaining : sizeof(dummy);
+        eeprom_update_block(dummy, start, this_loop);
+        start += this_loop;
+        remaining -= this_loop;
+    }
+}
+
+#endif // (EECONFIG_USER_DATA_SIZE) > 0

+ 59 - 0
quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h

@@ -0,0 +1,59 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h> // offsetof
+#include "eeconfig.h"
+#include "util.h"
+
+// Dummy struct only used to calculate offsets
+typedef struct PACKED {
+    uint16_t magic;
+    uint8_t  debug;
+    uint8_t  default_layer;
+    uint16_t keymap;
+    uint8_t  backlight;
+    uint8_t  audio;
+    uint32_t rgblight;
+    uint8_t  unicode;
+    uint8_t  steno;
+    uint8_t  handedness;
+    uint32_t keyboard;
+    uint32_t user;
+    union { // Mutually exclusive
+        uint32_t led_matrix;
+        uint64_t rgb_matrix;
+    };
+    uint32_t haptic;
+    uint8_t  rgblight_ext;
+} eeprom_core_t;
+
+/* EEPROM parameter address */
+#define EECONFIG_MAGIC (uint16_t *)(offsetof(eeprom_core_t, magic))
+#define EECONFIG_DEBUG (uint8_t *)(offsetof(eeprom_core_t, debug))
+#define EECONFIG_DEFAULT_LAYER (uint8_t *)(offsetof(eeprom_core_t, default_layer))
+#define EECONFIG_KEYMAP (uint16_t *)(offsetof(eeprom_core_t, keymap))
+#define EECONFIG_BACKLIGHT (uint8_t *)(offsetof(eeprom_core_t, backlight))
+#define EECONFIG_AUDIO (uint8_t *)(offsetof(eeprom_core_t, audio))
+#define EECONFIG_RGBLIGHT (uint32_t *)(offsetof(eeprom_core_t, rgblight))
+#define EECONFIG_UNICODEMODE (uint8_t *)(offsetof(eeprom_core_t, unicode))
+#define EECONFIG_STENOMODE (uint8_t *)(offsetof(eeprom_core_t, steno))
+#define EECONFIG_HANDEDNESS (uint8_t *)(offsetof(eeprom_core_t, handedness))
+#define EECONFIG_KEYBOARD (uint32_t *)(offsetof(eeprom_core_t, keyboard))
+#define EECONFIG_USER (uint32_t *)(offsetof(eeprom_core_t, user))
+#define EECONFIG_LED_MATRIX (uint32_t *)(offsetof(eeprom_core_t, led_matrix))
+#define EECONFIG_RGB_MATRIX (uint64_t *)(offsetof(eeprom_core_t, rgb_matrix))
+#define EECONFIG_HAPTIC (uint32_t *)(offsetof(eeprom_core_t, haptic))
+#define EECONFIG_RGBLIGHT_EXTENDED (uint8_t *)(offsetof(eeprom_core_t, rgblight_ext))
+
+// Size of EEPROM being used for core data storage
+#define EECONFIG_BASE_SIZE ((uint8_t)sizeof(eeprom_core_t))
+
+#define EECONFIG_KB_DATABLOCK ((uint8_t *)(EECONFIG_BASE_SIZE))
+#define EECONFIG_USER_DATABLOCK ((uint8_t *)((EECONFIG_BASE_SIZE) + (EECONFIG_KB_DATA_SIZE)))
+
+// Size of EEPROM being used, other code can refer to this for available EEPROM
+#define EECONFIG_SIZE ((EECONFIG_BASE_SIZE) + (EECONFIG_KB_DATA_SIZE) + (EECONFIG_USER_DATA_SIZE))
+
+_Static_assert((intptr_t)EECONFIG_HANDEDNESS == 14, "EEPROM handedness offset is incorrect");

+ 22 - 0
quantum/nvm/eeprom/nvm_eeprom_via_internal.h

@@ -0,0 +1,22 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+// Keyboard level code can change where VIA stores the magic.
+// The magic is the build date YYMMDD encoded as BCD in 3 bytes,
+// thus installing firmware built on a different date to the one
+// already installed can be detected and the EEPROM data is reset.
+// The only reason this is important is in case EEPROM usage changes
+// and the EEPROM was not explicitly reset by bootmagic lite.
+#ifndef VIA_EEPROM_MAGIC_ADDR
+#    define VIA_EEPROM_MAGIC_ADDR (EECONFIG_SIZE)
+#endif
+
+#define VIA_EEPROM_LAYOUT_OPTIONS_ADDR (VIA_EEPROM_MAGIC_ADDR + 3)
+
+// The end of the EEPROM memory used by VIA
+// By default, dynamic keymaps will start at this if there is no
+// custom config
+#define VIA_EEPROM_CUSTOM_CONFIG_ADDR (VIA_EEPROM_LAYOUT_OPTIONS_ADDR + VIA_EEPROM_LAYOUT_OPTIONS_SIZE)
+
+#define VIA_EEPROM_CONFIG_END (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE)

+ 77 - 0
quantum/nvm/eeprom/nvm_via.c

@@ -0,0 +1,77 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "eeprom.h"
+#include "util.h"
+#include "via.h"
+#include "nvm_via.h"
+#include "nvm_eeprom_eeconfig_internal.h"
+#include "nvm_eeprom_via_internal.h"
+
+void nvm_via_erase(void) {
+    // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.
+}
+
+void nvm_via_read_magic(uint8_t *magic0, uint8_t *magic1, uint8_t *magic2) {
+    if (magic0) {
+        *magic0 = eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 0);
+    }
+
+    if (magic1) {
+        *magic1 = eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 1);
+    }
+
+    if (magic2) {
+        *magic2 = eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2);
+    }
+}
+
+void nvm_via_update_magic(uint8_t magic0, uint8_t magic1, uint8_t magic2) {
+    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 0, magic0);
+    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 1, magic1);
+    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2, magic2);
+}
+
+uint32_t nvm_via_read_layout_options(void) {
+    uint32_t value = 0;
+    // Start at the most significant byte
+    void *source = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR);
+    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {
+        value = value << 8;
+        value |= eeprom_read_byte(source);
+        source++;
+    }
+    return value;
+}
+
+void nvm_via_update_layout_options(uint32_t val) {
+    // Start at the least significant byte
+    void *target = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR + VIA_EEPROM_LAYOUT_OPTIONS_SIZE - 1);
+    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {
+        eeprom_update_byte(target, val & 0xFF);
+        val = val >> 8;
+        target--;
+    }
+}
+
+uint32_t nvm_via_read_custom_config(void *buf, uint32_t offset, uint32_t length) {
+#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0
+    void *ee_start = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + offset);
+    void *ee_end   = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + MIN(VIA_EEPROM_CUSTOM_CONFIG_SIZE, offset + length));
+    eeprom_read_block(buf, ee_start, ee_end - ee_start);
+    return ee_end - ee_start;
+#else
+    return 0;
+#endif
+}
+
+uint32_t nvm_via_update_custom_config(const void *buf, uint32_t offset, uint32_t length) {
+#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0
+    void *ee_start = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + offset);
+    void *ee_end   = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + MIN(VIA_EEPROM_CUSTOM_CONFIG_SIZE, offset + length));
+    eeprom_update_block(buf, ee_start, ee_end - ee_start);
+    return ee_end - ee_start;
+#else
+    return 0;
+#endif
+}

+ 27 - 0
quantum/nvm/nvm_dynamic_keymap.h

@@ -0,0 +1,27 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void nvm_dynamic_keymap_erase(void);
+void nvm_dynamic_keymap_macro_erase(void);
+
+uint16_t nvm_dynamic_keymap_read_keycode(uint8_t layer, uint8_t row, uint8_t column);
+void     nvm_dynamic_keymap_update_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode);
+
+#ifdef ENCODER_MAP_ENABLE
+uint16_t nvm_dynamic_keymap_read_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise);
+void     nvm_dynamic_keymap_update_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode);
+#endif // ENCODER_MAP_ENABLE
+
+void nvm_dynamic_keymap_read_buffer(uint32_t offset, uint32_t size, uint8_t *data);
+void nvm_dynamic_keymap_update_buffer(uint32_t offset, uint32_t size, uint8_t *data);
+
+uint32_t nvm_dynamic_keymap_macro_size(void);
+
+void nvm_dynamic_keymap_macro_read_buffer(uint32_t offset, uint32_t size, uint8_t *data);
+void nvm_dynamic_keymap_macro_update_buffer(uint32_t offset, uint32_t size, uint8_t *data);
+
+void nvm_dynamic_keymap_macro_reset(void);

+ 105 - 0
quantum/nvm/nvm_eeconfig.h

@@ -0,0 +1,105 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "action_layer.h" // layer_state_t
+
+#ifndef EECONFIG_MAGIC_NUMBER
+#    define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE3 // When changing, decrement this value to avoid future re-init issues
+#endif
+#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF
+
+void nvm_eeconfig_erase(void);
+
+bool nvm_eeconfig_is_enabled(void);
+bool nvm_eeconfig_is_disabled(void);
+
+void nvm_eeconfig_enable(void);
+void nvm_eeconfig_disable(void);
+
+typedef union debug_config_t debug_config_t;
+void                         nvm_eeconfig_read_debug(debug_config_t *debug_config);
+void                         nvm_eeconfig_update_debug(const debug_config_t *debug_config);
+
+layer_state_t nvm_eeconfig_read_default_layer(void);
+void          nvm_eeconfig_update_default_layer(layer_state_t state);
+
+typedef union keymap_config_t keymap_config_t;
+void                          nvm_eeconfig_read_keymap(keymap_config_t *keymap_config);
+void                          nvm_eeconfig_update_keymap(const keymap_config_t *keymap_config);
+
+#ifdef AUDIO_ENABLE
+typedef union audio_config_t audio_config_t;
+void                         nvm_eeconfig_read_audio(audio_config_t *audio_config);
+void                         nvm_eeconfig_update_audio(const audio_config_t *audio_config);
+#endif // AUDIO_ENABLE
+
+#ifdef UNICODE_COMMON_ENABLE
+typedef union unicode_config_t unicode_config_t;
+void                           nvm_eeconfig_read_unicode_mode(unicode_config_t *unicode_config);
+void                           nvm_eeconfig_update_unicode_mode(const unicode_config_t *unicode_config);
+#endif // UNICODE_COMMON_ENABLE
+
+#ifdef BACKLIGHT_ENABLE
+typedef union backlight_config_t backlight_config_t;
+void                             nvm_eeconfig_read_backlight(backlight_config_t *backlight_config);
+void                             nvm_eeconfig_update_backlight(const backlight_config_t *backlight_config);
+#endif // BACKLIGHT_ENABLE
+
+#ifdef STENO_ENABLE
+uint8_t nvm_eeconfig_read_steno_mode(void);
+void    nvm_eeconfig_update_steno_mode(uint8_t val);
+#endif // STENO_ENABLE
+
+#ifdef RGB_MATRIX_ENABLE
+typedef union rgb_config_t rgb_config_t;
+void                       nvm_eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config);
+void                       nvm_eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config);
+#endif
+
+#ifdef LED_MATRIX_ENABLE
+typedef union led_eeconfig_t led_eeconfig_t;
+void                         nvm_eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config);
+void                         nvm_eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config);
+#endif // LED_MATRIX_ENABLE
+
+#ifdef RGBLIGHT_ENABLE
+typedef union rgblight_config_t rgblight_config_t;
+void                            nvm_eeconfig_read_rgblight(rgblight_config_t *rgblight_config);
+void                            nvm_eeconfig_update_rgblight(const rgblight_config_t *rgblight_config);
+#endif // RGBLIGHT_ENABLE
+
+#if (EECONFIG_KB_DATA_SIZE) == 0
+uint32_t nvm_eeconfig_read_kb(void);
+void     nvm_eeconfig_update_kb(uint32_t val);
+#endif // (EECONFIG_KB_DATA_SIZE) == 0
+
+#if (EECONFIG_USER_DATA_SIZE) == 0
+uint32_t nvm_eeconfig_read_user(void);
+void     nvm_eeconfig_update_user(uint32_t val);
+#endif // (EECONFIG_USER_DATA_SIZE) == 0
+
+#ifdef HAPTIC_ENABLE
+typedef union haptic_config_t haptic_config_t;
+void                          nvm_eeconfig_read_haptic(haptic_config_t *haptic_config);
+void                          nvm_eeconfig_update_haptic(const haptic_config_t *haptic_config);
+#endif // HAPTIC_ENABLE
+
+bool nvm_eeconfig_read_handedness(void);
+void nvm_eeconfig_update_handedness(bool val);
+
+#if (EECONFIG_KB_DATA_SIZE) > 0
+bool     nvm_eeconfig_is_kb_datablock_valid(void);
+uint32_t nvm_eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length);
+uint32_t nvm_eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length);
+void     nvm_eeconfig_init_kb_datablock(void);
+#endif // (EECONFIG_KB_DATA_SIZE) > 0
+
+#if (EECONFIG_USER_DATA_SIZE) > 0
+bool     nvm_eeconfig_is_user_datablock_valid(void);
+uint32_t nvm_eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length);
+uint32_t nvm_eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length);
+void     nvm_eeconfig_init_user_datablock(void);
+#endif // (EECONFIG_USER_DATA_SIZE) > 0

+ 17 - 0
quantum/nvm/nvm_via.h

@@ -0,0 +1,17 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+void nvm_via_erase(void);
+
+void nvm_via_read_magic(uint8_t *magic0, uint8_t *magic1, uint8_t *magic2);
+void nvm_via_update_magic(uint8_t magic0, uint8_t magic1, uint8_t magic2);
+
+uint32_t nvm_via_read_layout_options(void);
+void     nvm_via_update_layout_options(uint32_t val);
+
+uint32_t nvm_via_read_custom_config(void *buf, uint32_t offset, uint32_t length);
+uint32_t nvm_via_update_custom_config(const void *buf, uint32_t offset, uint32_t length);

+ 30 - 0
quantum/nvm/readme.md

@@ -0,0 +1,30 @@
+# Non-volatile Memory - Data Repositories
+
+This area is intentionally structured in the following way:
+
+```
+╰- quantum
+   ╰- nvm
+      ├- readme.md
+      ├- rules.mk
+      |
+      ├- nvm_eeconfig.h
+      ├- nvm_<<system>>.h
+      |
+      ├- eeprom
+      |  ├- nvm_eeconfig.c
+      |  ├- nvm_<<system>>.c
+      |  ╰- ...
+      |
+      ├- <<another provider>>
+      |  ├- nvm_eeconfig.c
+      |  ├- nvm_<<system>>.c
+      |  ╰- ...
+      ╰- ...
+```
+
+At the base `nvm` level, for every QMK core system which requires persistence there must be a corresponding `nvm_<<system>>.h` header file. This provides the data repository API to the "owner" system, and allows the underlying data persistence mechanism to be abstracted away from upper code. Any conversion to/from a `.raw` field should occur inside the `nvm_<<system>>.c` layer, with the API using values, such as structs or unions exposed to the rest of QMK.
+
+Each `nvm` "provider" is a corresponding child directory consisting of its name, such as `eeprom`, and corresponding `nvm_<<system>>.c` implementation files which provide the concrete implementation of the upper `nvm_<<system>>.h`.
+
+New systems requiring persistence can add the corresponding `nvm_<<system>>.h` file, and in most circumstances must also implement equivalent `nvm_<<system>>.c` files for every `nvm` provider. If persistence is not possible for that system, a `nvm_<<system>>.c` file with simple stubs which ignore writes and provide sane defaults must be used instead.

+ 31 - 0
quantum/nvm/rules.mk

@@ -0,0 +1,31 @@
+# Copyright 2024 Nick Brassel (@tzarc)
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+VPATH += $(QUANTUM_DIR)/nvm
+
+VALID_NVM_DRIVERS := eeprom custom none
+
+NVM_DRIVER ?= eeprom
+
+ifeq ($(filter $(NVM_DRIVER),$(VALID_NVM_DRIVERS)),)
+    $(call CATASTROPHIC_ERROR,Invalid NVM_DRIVER,NVM_DRIVER="$(NVM_DRIVER)" is not a valid NVM driver)
+else
+
+    # If we don't want one, fake it with transient eeprom.
+    ifeq ($(NVM_DRIVER),none)
+        NVM_DRIVER := eeprom
+        EEPROM_DRIVER := transient
+    endif
+
+    NVM_DRIVER_UPPER := $(shell echo $(NVM_DRIVER) | tr '[:lower:]' '[:upper:]')
+    NVM_DRIVER_LOWER := $(shell echo $(NVM_DRIVER) | tr '[:upper:]' '[:lower:]')
+
+    OPT_DEFS += -DNVM_DRIVER_$(NVM_DRIVER_UPPER) -DNVM_DRIVER="$(NVM_DRIVER)"
+
+    ifneq ("$(wildcard $(QUANTUM_DIR)/nvm/$(NVM_DRIVER_LOWER))","")
+        COMMON_VPATH += $(QUANTUM_DIR)/nvm/$(NVM_DRIVER_LOWER)
+    endif
+
+    QUANTUM_SRC += nvm_eeconfig.c
+
+endif

+ 3 - 0
quantum/os_detection.h

@@ -43,6 +43,9 @@ void slave_update_detected_host_os(os_variant_t os);
 #endif
 
 #ifdef OS_DETECTION_DEBUG_ENABLE
+#    if defined(DYNAMIC_KEYMAP_ENABLE) || defined(VIA_ENABLE)
+#        error Cannot enable OS Detection debug mode simultaneously with DYNAMIC_KEYMAP or VIA
+#    endif
 void print_stored_setups(void);
 void store_setups_in_eeprom(void);
 #endif

+ 3 - 3
quantum/process_keycode/process_autocorrect.c

@@ -38,7 +38,7 @@ bool autocorrect_is_enabled(void) {
  */
 void autocorrect_enable(void) {
     keymap_config.autocorrect_enable = true;
-    eeconfig_update_keymap(keymap_config.raw);
+    eeconfig_update_keymap(&keymap_config);
 }
 
 /**
@@ -48,7 +48,7 @@ void autocorrect_enable(void) {
 void autocorrect_disable(void) {
     keymap_config.autocorrect_enable = false;
     typo_buffer_size                 = 0;
-    eeconfig_update_keymap(keymap_config.raw);
+    eeconfig_update_keymap(&keymap_config);
 }
 
 /**
@@ -58,7 +58,7 @@ void autocorrect_disable(void) {
 void autocorrect_toggle(void) {
     keymap_config.autocorrect_enable = !keymap_config.autocorrect_enable;
     typo_buffer_size                 = 0;
-    eeconfig_update_keymap(keymap_config.raw);
+    eeconfig_update_keymap(&keymap_config);
 }
 
 /**

+ 3 - 3
quantum/process_keycode/process_clicky.c

@@ -66,17 +66,17 @@ void clicky_freq_reset(void) {
 
 void clicky_toggle(void) {
     audio_config.clicky_enable ^= 1;
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
 }
 
 void clicky_on(void) {
     audio_config.clicky_enable = 1;
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
 }
 
 void clicky_off(void) {
     audio_config.clicky_enable = 0;
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
 }
 
 bool is_clicky_on(void) {

+ 2 - 2
quantum/process_keycode/process_magic.c

@@ -47,7 +47,7 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
     if (record->event.pressed) {
         if (IS_MAGIC_KEYCODE(keycode)) {
             /* keymap config */
-            keymap_config.raw = eeconfig_read_keymap();
+            eeconfig_read_keymap(&keymap_config);
             switch (keycode) {
                 case QK_MAGIC_SWAP_CONTROL_CAPS_LOCK:
                     keymap_config.swap_control_capslock = true;
@@ -187,7 +187,7 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
                     break;
             }
 
-            eeconfig_update_keymap(keymap_config.raw);
+            eeconfig_update_keymap(&keymap_config);
             clear_keyboard(); // clear to prevent stuck keys
 
             return false;

+ 2 - 5
quantum/process_keycode/process_steno.c

@@ -20,9 +20,6 @@
 #ifdef VIRTSER_ENABLE
 #    include "virtser.h"
 #endif
-#ifdef STENO_ENABLE_ALL
-#    include "eeprom.h"
-#endif
 
 // All steno keys that have been pressed to form this chord,
 // stored in MAX_STROKE_SIZE groups of 8-bit arrays.
@@ -128,13 +125,13 @@ static const uint16_t combinedmap_second[] PROGMEM = {STN_S2, STN_KL, STN_WL, ST
 
 #ifdef STENO_ENABLE_ALL
 void steno_init(void) {
-    mode = eeprom_read_byte(EECONFIG_STENOMODE);
+    mode = eeconfig_read_steno_mode();
 }
 
 void steno_set_mode(steno_mode_t new_mode) {
     steno_clear_chord();
     mode = new_mode;
-    eeprom_update_byte(EECONFIG_STENOMODE, mode);
+    eeconfig_update_steno_mode(mode);
 }
 #endif // STENO_ENABLE_ALL
 

+ 2 - 2
quantum/rgb_matrix/rgb_matrix.c

@@ -88,9 +88,9 @@ static last_hit_t last_hit_buffer;
 const uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT;
 #endif
 
-EECONFIG_DEBOUNCE_HELPER(rgb_matrix, EECONFIG_RGB_MATRIX, rgb_matrix_config);
+EECONFIG_DEBOUNCE_HELPER(rgb_matrix, rgb_matrix_config);
 
-void eeconfig_update_rgb_matrix(void) {
+void eeconfig_force_flush_rgb_matrix(void) {
     eeconfig_flush_rgb_matrix(true);
 }
 

+ 2 - 2
quantum/rgb_matrix/rgb_matrix.h

@@ -140,7 +140,7 @@ enum rgb_matrix_effects {
 };
 
 void eeconfig_update_rgb_matrix_default(void);
-void eeconfig_update_rgb_matrix(void);
+void eeconfig_force_flush_rgb_matrix(void);
 
 uint8_t rgb_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i);
 uint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i);
@@ -215,7 +215,7 @@ void        rgb_matrix_set_flags_noeeprom(led_flags_t flags);
 void        rgb_matrix_update_pwm_buffers(void);
 
 #ifndef RGBLIGHT_ENABLE
-#    define eeconfig_update_rgblight_current eeconfig_update_rgb_matrix
+#    define eeconfig_update_rgblight_current eeconfig_force_flush_rgb_matrix
 #    define rgblight_reload_from_eeprom rgb_matrix_reload_from_eeprom
 #    define rgblight_toggle rgb_matrix_toggle
 #    define rgblight_toggle_noeeprom rgb_matrix_toggle_noeeprom

+ 1 - 1
quantum/rgb_matrix/rgb_matrix_types.h

@@ -73,7 +73,7 @@ typedef struct PACKED {
     uint8_t     flags[RGB_MATRIX_LED_COUNT];
 } led_config_t;
 
-typedef union {
+typedef union rgb_config_t {
     uint64_t raw;
     struct PACKED {
         uint8_t     enable : 2;

+ 14 - 31
quantum/rgblight/rgblight.c

@@ -24,9 +24,7 @@
 #include "util.h"
 #include "led_tables.h"
 #include <lib/lib8tion/lib8tion.h>
-#ifdef EEPROM_ENABLE
-#    include "eeprom.h"
-#endif
+#include "eeconfig.h"
 
 #ifdef RGBLIGHT_SPLIT
 /* for split keyboard */
@@ -176,24 +174,9 @@ void rgblight_check_config(void) {
     }
 }
 
-uint64_t eeconfig_read_rgblight(void) {
-#ifdef EEPROM_ENABLE
-    return (uint64_t)((eeprom_read_dword(EECONFIG_RGBLIGHT)) | ((uint64_t)eeprom_read_byte(EECONFIG_RGBLIGHT_EXTENDED) << 32));
-#else
-    return 0;
-#endif
-}
-
-void eeconfig_update_rgblight(uint64_t val) {
-#ifdef EEPROM_ENABLE
-    rgblight_check_config();
-    eeprom_update_dword(EECONFIG_RGBLIGHT, val & 0xFFFFFFFF);
-    eeprom_update_byte(EECONFIG_RGBLIGHT_EXTENDED, (val >> 32) & 0xFF);
-#endif
-}
-
 void eeconfig_update_rgblight_current(void) {
-    eeconfig_update_rgblight(rgblight_config.raw);
+    rgblight_check_config();
+    eeconfig_update_rgblight(&rgblight_config);
 }
 
 void eeconfig_update_rgblight_default(void) {
@@ -205,7 +188,7 @@ void eeconfig_update_rgblight_default(void) {
     rgblight_config.val       = RGBLIGHT_DEFAULT_VAL;
     rgblight_config.speed     = RGBLIGHT_DEFAULT_SPD;
     RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
-    eeconfig_update_rgblight(rgblight_config.raw);
+    eeconfig_update_rgblight(&rgblight_config);
 }
 
 void eeconfig_debug_rgblight(void) {
@@ -228,12 +211,12 @@ void rgblight_init(void) {
     }
 
     dprintf("rgblight_init start!\n");
-    rgblight_config.raw = eeconfig_read_rgblight();
+    eeconfig_read_rgblight(&rgblight_config);
     RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
     if (!rgblight_config.mode) {
         dprintf("rgblight_init rgblight_config.mode = 0. Write default values to EEPROM.\n");
         eeconfig_update_rgblight_default();
-        rgblight_config.raw = eeconfig_read_rgblight();
+        eeconfig_read_rgblight(&rgblight_config);
     }
     rgblight_check_config();
 
@@ -252,7 +235,7 @@ void rgblight_init(void) {
 
 void rgblight_reload_from_eeprom(void) {
     /* Reset back to what we have in eeprom */
-    rgblight_config.raw = eeconfig_read_rgblight();
+    eeconfig_read_rgblight(&rgblight_config);
     RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;
     rgblight_check_config();
     eeconfig_debug_rgblight(); // display current eeprom values
@@ -341,7 +324,7 @@ void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
     }
     RGBLIGHT_SPLIT_SET_CHANGE_MODE;
     if (write_to_eeprom) {
-        eeconfig_update_rgblight(rgblight_config.raw);
+        eeconfig_update_rgblight(&rgblight_config);
         dprintf("rgblight mode [EEPROM]: %u\n", rgblight_config.mode);
     } else {
         dprintf("rgblight mode [NOEEPROM]: %u\n", rgblight_config.mode);
@@ -386,7 +369,7 @@ void rgblight_toggle_noeeprom(void) {
 void rgblight_enable(void) {
     rgblight_config.enable = 1;
     // No need to update EEPROM here. rgblight_mode() will do that, actually
-    // eeconfig_update_rgblight(rgblight_config.raw);
+    // eeconfig_update_rgblight(&rgblight_config);
     dprintf("rgblight enable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
     rgblight_mode(rgblight_config.mode);
 }
@@ -399,7 +382,7 @@ void rgblight_enable_noeeprom(void) {
 
 void rgblight_disable(void) {
     rgblight_config.enable = 0;
-    eeconfig_update_rgblight(rgblight_config.raw);
+    eeconfig_update_rgblight(&rgblight_config);
     dprintf("rgblight disable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
     rgblight_timer_disable();
     RGBLIGHT_SPLIT_SET_CHANGE_MODE;
@@ -487,7 +470,7 @@ void rgblight_increase_speed_helper(bool write_to_eeprom) {
     if (rgblight_config.speed < 3) rgblight_config.speed++;
     // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED?
     if (write_to_eeprom) {
-        eeconfig_update_rgblight(rgblight_config.raw);
+        eeconfig_update_rgblight(&rgblight_config);
     }
 }
 void rgblight_increase_speed(void) {
@@ -501,7 +484,7 @@ void rgblight_decrease_speed_helper(bool write_to_eeprom) {
     if (rgblight_config.speed > 0) rgblight_config.speed--;
     // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED??
     if (write_to_eeprom) {
-        eeconfig_update_rgblight(rgblight_config.raw);
+        eeconfig_update_rgblight(&rgblight_config);
     }
 }
 void rgblight_decrease_speed(void) {
@@ -585,7 +568,7 @@ void rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool w
         rgblight_config.sat = sat;
         rgblight_config.val = val;
         if (write_to_eeprom) {
-            eeconfig_update_rgblight(rgblight_config.raw);
+            eeconfig_update_rgblight(&rgblight_config);
             dprintf("rgblight set hsv [EEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
         } else {
             dprintf("rgblight set hsv [NOEEPROM]: %u,%u,%u\n", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
@@ -608,7 +591,7 @@ uint8_t rgblight_get_speed(void) {
 void rgblight_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {
     rgblight_config.speed = speed;
     if (write_to_eeprom) {
-        eeconfig_update_rgblight(rgblight_config.raw);
+        eeconfig_update_rgblight(&rgblight_config);
         dprintf("rgblight set speed [EEPROM]: %u\n", rgblight_config.speed);
     } else {
         dprintf("rgblight set speed [NOEEPROM]: %u\n", rgblight_config.speed);

+ 1 - 4
quantum/rgblight/rgblight.h

@@ -168,7 +168,6 @@ enum RGBLIGHT_EFFECT_MODE {
 #include <stdbool.h>
 #include "rgblight_drivers.h"
 #include "progmem.h"
-#include "eeconfig.h"
 #include "color.h"
 
 #ifdef RGBLIGHT_LAYERS
@@ -248,7 +247,7 @@ extern const uint16_t RGBLED_RGBTEST_INTERVALS[1] PROGMEM;
 extern const uint8_t  RGBLED_TWINKLE_INTERVALS[3] PROGMEM;
 extern bool           is_rgblight_initialized;
 
-typedef union {
+typedef union rgblight_config_t {
     uint64_t raw;
     struct {
         bool    enable : 1;
@@ -370,8 +369,6 @@ void     rgblight_suspend(void);
 void     rgblight_wakeup(void);
 uint64_t rgblight_read_qword(void);
 void     rgblight_update_qword(uint64_t qword);
-uint64_t eeconfig_read_rgblight(void);
-void     eeconfig_update_rgblight(uint64_t val);
 void     eeconfig_update_rgblight_current(void);
 void     eeconfig_update_rgblight_default(void);
 void     eeconfig_debug_rgblight(void);

+ 2 - 2
quantum/unicode/unicode.c

@@ -136,7 +136,7 @@ static void unicode_play_song(uint8_t mode) {
 #endif
 
 void unicode_input_mode_init(void) {
-    unicode_config.raw = eeprom_read_byte(EECONFIG_UNICODEMODE);
+    eeconfig_read_unicode_mode(&unicode_config);
 #if UNICODE_SELECTED_MODES != -1
 #    if UNICODE_CYCLE_PERSIST
     // Find input_mode in selected modes
@@ -165,7 +165,7 @@ uint8_t get_unicode_input_mode(void) {
 }
 
 static void persist_unicode_input_mode(void) {
-    eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode);
+    eeconfig_update_unicode_mode(&unicode_config);
 }
 
 void set_unicode_input_mode(uint8_t mode) {

+ 1 - 1
quantum/unicode/unicode.h

@@ -26,7 +26,7 @@
  * \{
  */
 
-typedef union {
+typedef union unicode_config_t {
     uint8_t raw;
     struct {
         uint8_t input_mode : 8;

+ 32 - 28
quantum/via.c

@@ -32,6 +32,7 @@
 #include "timer.h"
 #include "wait.h"
 #include "version.h" // for QMK_BUILDDATE used in EEPROM magic
+#include "nvm_via.h"
 
 #if defined(AUDIO_ENABLE)
 #    include "audio.h"
@@ -65,20 +66,26 @@ bool via_eeprom_is_valid(void) {
     uint8_t magic1 = ((p[5] & 0x0F) << 4) | (p[6] & 0x0F);
     uint8_t magic2 = ((p[8] & 0x0F) << 4) | (p[9] & 0x0F);
 
-    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);
+    uint8_t ee_magic0;
+    uint8_t ee_magic1;
+    uint8_t ee_magic2;
+    nvm_via_read_magic(&ee_magic0, &ee_magic1, &ee_magic2);
+
+    return ee_magic0 == magic0 && ee_magic1 == magic1 && ee_magic2 == magic2;
 }
 
 // Sets VIA/keyboard level usage of EEPROM to valid/invalid
 // Keyboard level code (eg. via_init_kb()) should not call this
 void via_eeprom_set_valid(bool valid) {
-    char *  p      = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
-    uint8_t magic0 = ((p[2] & 0x0F) << 4) | (p[3] & 0x0F);
-    uint8_t magic1 = ((p[5] & 0x0F) << 4) | (p[6] & 0x0F);
-    uint8_t magic2 = ((p[8] & 0x0F) << 4) | (p[9] & 0x0F);
-
-    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 0, valid ? magic0 : 0xFF);
-    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 1, valid ? magic1 : 0xFF);
-    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2, valid ? magic2 : 0xFF);
+    if (valid) {
+        char *  p      = QMK_BUILDDATE; // e.g. "2019-11-05-11:29:54"
+        uint8_t magic0 = ((p[2] & 0x0F) << 4) | (p[3] & 0x0F);
+        uint8_t magic1 = ((p[5] & 0x0F) << 4) | (p[6] & 0x0F);
+        uint8_t magic2 = ((p[8] & 0x0F) << 4) | (p[9] & 0x0F);
+        nvm_via_update_magic(magic0, magic1, magic2);
+    } else {
+        nvm_via_update_magic(0xFF, 0xFF, 0xFF);
+    }
 }
 
 // Override this at the keyboard code level to check
@@ -104,6 +111,8 @@ void via_init(void) {
 }
 
 void eeconfig_init_via(void) {
+    // Erase any NVM storage if necessary
+    nvm_via_erase();
     // set the magic number to false, in case this gets interrupted
     via_eeprom_set_valid(false);
     // This resets the layout options
@@ -119,29 +128,24 @@ void eeconfig_init_via(void) {
 // This is generalized so the layout options EEPROM usage can be
 // variable, between 1 and 4 bytes.
 uint32_t via_get_layout_options(void) {
-    uint32_t value = 0;
-    // Start at the most significant byte
-    void *source = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR);
-    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {
-        value = value << 8;
-        value |= eeprom_read_byte(source);
-        source++;
-    }
-    return value;
+    return nvm_via_read_layout_options();
 }
 
 __attribute__((weak)) void via_set_layout_options_kb(uint32_t value) {}
 
 void via_set_layout_options(uint32_t value) {
     via_set_layout_options_kb(value);
-    // Start at the least significant byte
-    void *target = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR + VIA_EEPROM_LAYOUT_OPTIONS_SIZE - 1);
-    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {
-        eeprom_update_byte(target, value & 0xFF);
-        value = value >> 8;
-        target--;
-    }
+    nvm_via_update_layout_options(value);
+}
+
+#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0
+uint32_t via_read_custom_config(void *buf, uint32_t offset, uint32_t length) {
+    return nvm_via_read_custom_config(buf, offset, length);
+}
+uint32_t via_update_custom_config(const void *buf, uint32_t offset, uint32_t length) {
+    return nvm_via_update_custom_config(buf, offset, length);
 }
+#endif
 
 #if defined(AUDIO_ENABLE)
 float via_device_indication_song[][2] = SONG(STARTUP_SOUND);
@@ -715,7 +719,7 @@ void via_qmk_rgb_matrix_set_value(uint8_t *data) {
 }
 
 void via_qmk_rgb_matrix_save(void) {
-    eeconfig_update_rgb_matrix();
+    eeconfig_force_flush_rgb_matrix();
 }
 
 #endif // RGB_MATRIX_ENABLE
@@ -794,7 +798,7 @@ void via_qmk_led_matrix_set_value(uint8_t *data) {
 }
 
 void via_qmk_led_matrix_save(void) {
-    eeconfig_update_led_matrix();
+    eeconfig_force_flush_led_matrix();
 }
 
 #endif // LED_MATRIX_ENABLE
@@ -861,7 +865,7 @@ void via_qmk_audio_set_value(uint8_t *data) {
 }
 
 void via_qmk_audio_save(void) {
-    eeconfig_update_audio(audio_config.raw);
+    eeconfig_update_audio(&audio_config);
 }
 
 #endif // QMK_AUDIO_ENABLE

+ 5 - 20
quantum/via.h

@@ -16,21 +16,8 @@
 
 #pragma once
 
-#include "eeconfig.h" // for EECONFIG_SIZE
 #include "action.h"
 
-// Keyboard level code can change where VIA stores the magic.
-// The magic is the build date YYMMDD encoded as BCD in 3 bytes,
-// thus installing firmware built on a different date to the one
-// already installed can be detected and the EEPROM data is reset.
-// The only reason this is important is in case EEPROM usage changes
-// and the EEPROM was not explicitly reset by bootmagic lite.
-#ifndef VIA_EEPROM_MAGIC_ADDR
-#    define VIA_EEPROM_MAGIC_ADDR (EECONFIG_SIZE)
-#endif
-
-#define VIA_EEPROM_LAYOUT_OPTIONS_ADDR (VIA_EEPROM_MAGIC_ADDR + 3)
-
 // Changing the layout options size after release will invalidate EEPROM,
 // but this is something that should be set correctly on initial implementation.
 // 1 byte is enough for most uses (i.e. 8 binary states, or 6 binary + 1 ternary/quaternary )
@@ -46,17 +33,10 @@
 #    define VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT 0x00000000
 #endif
 
-// The end of the EEPROM memory used by VIA
-// By default, dynamic keymaps will start at this if there is no
-// custom config
-#define VIA_EEPROM_CUSTOM_CONFIG_ADDR (VIA_EEPROM_LAYOUT_OPTIONS_ADDR + VIA_EEPROM_LAYOUT_OPTIONS_SIZE)
-
 #ifndef VIA_EEPROM_CUSTOM_CONFIG_SIZE
 #    define VIA_EEPROM_CUSTOM_CONFIG_SIZE 0
 #endif
 
-#define VIA_EEPROM_CONFIG_END (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE)
-
 // This is changed only when the command IDs change,
 // so VIA Configurator can detect compatible firmware.
 #define VIA_PROTOCOL_VERSION 0x000C
@@ -160,6 +140,11 @@ uint32_t via_get_layout_options(void);
 void     via_set_layout_options(uint32_t value);
 void     via_set_layout_options_kb(uint32_t value);
 
+#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0
+uint32_t via_read_custom_config(void *buf, uint32_t offset, uint32_t length);
+uint32_t via_update_custom_config(const void *buf, uint32_t offset, uint32_t length);
+#endif
+
 // Used by VIA to tell a device to flash LEDs (or do something else) when that
 // device becomes the active device being configured, on startup or switching
 // between devices.

+ 1 - 1
tests/test_common/test_fixture.cpp

@@ -46,7 +46,7 @@ void TestFixture::SetUpTestCase() {
 
     // The following is enough to bootstrap the values set in main
     eeconfig_init_quantum();
-    eeconfig_update_debug(debug_config.raw);
+    eeconfig_update_debug(&debug_config);
 
     TestDriver driver;
     keyboard_init();