aboutsummaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/action.c20
-rw-r--r--quantum/action.h6
-rw-r--r--quantum/action_layer.c19
-rw-r--r--quantum/action_layer.h3
-rw-r--r--quantum/action_tapping.c212
-rw-r--r--quantum/action_tapping.h60
-rw-r--r--quantum/action_util.c11
-rw-r--r--quantum/audio/audio.c12
-rw-r--r--quantum/audio/audio.h6
-rw-r--r--quantum/backlight/backlight.c31
-rw-r--r--quantum/backlight/backlight.h12
-rw-r--r--quantum/command.c8
-rw-r--r--quantum/compiler_support.h15
-rw-r--r--quantum/connection/connection.c141
-rw-r--r--quantum/connection/connection.h112
-rw-r--r--quantum/dynamic_keymap.c205
-rw-r--r--quantum/dynamic_keymap.h9
-rw-r--r--quantum/eeconfig.c472
-rw-r--r--quantum/eeconfig.h162
-rw-r--r--quantum/haptic.c26
-rw-r--r--quantum/haptic.h6
-rw-r--r--quantum/keyboard.c23
-rw-r--r--quantum/keycode_config.h8
-rw-r--r--quantum/keycode_string.c564
-rw-r--r--quantum/keycode_string.h134
-rw-r--r--quantum/keycodes.h6
-rw-r--r--quantum/keymap_common.c2
-rw-r--r--quantum/keymap_extras/keymap_belgian.h6
-rw-r--r--quantum/keymap_extras/keymap_bepo.h6
-rw-r--r--quantum/keymap_extras/keymap_brazilian_abnt2.h6
-rw-r--r--quantum/keymap_extras/keymap_canadian_french.h6
-rw-r--r--quantum/keymap_extras/keymap_canadian_multilingual.h6
-rw-r--r--quantum/keymap_extras/keymap_colemak.h6
-rw-r--r--quantum/keymap_extras/keymap_croatian.h6
-rw-r--r--quantum/keymap_extras/keymap_czech.h6
-rw-r--r--quantum/keymap_extras/keymap_czech_mac_ansi.h6
-rw-r--r--quantum/keymap_extras/keymap_czech_mac_iso.h6
-rw-r--r--quantum/keymap_extras/keymap_danish.h6
-rw-r--r--quantum/keymap_extras/keymap_dvorak.h6
-rw-r--r--quantum/keymap_extras/keymap_dvorak_fr.h6
-rw-r--r--quantum/keymap_extras/keymap_dvorak_programmer.h6
-rw-r--r--quantum/keymap_extras/keymap_estonian.h6
-rw-r--r--quantum/keymap_extras/keymap_eurkey.h6
-rw-r--r--quantum/keymap_extras/keymap_farsi.h6
-rw-r--r--quantum/keymap_extras/keymap_finnish.h6
-rw-r--r--quantum/keymap_extras/keymap_french.h6
-rw-r--r--quantum/keymap_extras/keymap_french_afnor.h6
-rw-r--r--quantum/keymap_extras/keymap_french_mac_iso.h6
-rw-r--r--quantum/keymap_extras/keymap_german.h6
-rw-r--r--quantum/keymap_extras/keymap_german_mac_iso.h6
-rw-r--r--quantum/keymap_extras/keymap_greek.h6
-rw-r--r--quantum/keymap_extras/keymap_hebrew.h6
-rw-r--r--quantum/keymap_extras/keymap_hungarian.h6
-rw-r--r--quantum/keymap_extras/keymap_icelandic.h6
-rw-r--r--quantum/keymap_extras/keymap_irish.h6
-rw-r--r--quantum/keymap_extras/keymap_italian.h6
-rw-r--r--quantum/keymap_extras/keymap_italian_mac_ansi.h6
-rw-r--r--quantum/keymap_extras/keymap_italian_mac_iso.h6
-rw-r--r--quantum/keymap_extras/keymap_japanese.h6
-rw-r--r--quantum/keymap_extras/keymap_korean.h6
-rw-r--r--quantum/keymap_extras/keymap_latvian.h6
-rw-r--r--quantum/keymap_extras/keymap_lithuanian_azerty.h6
-rw-r--r--quantum/keymap_extras/keymap_lithuanian_qwerty.h6
-rw-r--r--quantum/keymap_extras/keymap_neo2.h6
-rw-r--r--quantum/keymap_extras/keymap_nordic.h6
-rw-r--r--quantum/keymap_extras/keymap_norman.h6
-rw-r--r--quantum/keymap_extras/keymap_norwegian.h6
-rw-r--r--quantum/keymap_extras/keymap_plover.h6
-rw-r--r--quantum/keymap_extras/keymap_plover_dvorak.h6
-rw-r--r--quantum/keymap_extras/keymap_polish.h6
-rw-r--r--quantum/keymap_extras/keymap_portuguese.h6
-rw-r--r--quantum/keymap_extras/keymap_portuguese_mac_iso.h6
-rw-r--r--quantum/keymap_extras/keymap_romanian.h6
-rw-r--r--quantum/keymap_extras/keymap_russian.h6
-rw-r--r--quantum/keymap_extras/keymap_russian_typewriter.h6
-rw-r--r--quantum/keymap_extras/keymap_serbian.h6
-rw-r--r--quantum/keymap_extras/keymap_serbian_latin.h6
-rw-r--r--quantum/keymap_extras/keymap_slovak.h6
-rw-r--r--quantum/keymap_extras/keymap_slovenian.h6
-rw-r--r--quantum/keymap_extras/keymap_spanish.h6
-rw-r--r--quantum/keymap_extras/keymap_spanish_dvorak.h6
-rw-r--r--quantum/keymap_extras/keymap_spanish_latin_america.h6
-rw-r--r--quantum/keymap_extras/keymap_swedish.h6
-rw-r--r--quantum/keymap_extras/keymap_swedish_mac_ansi.h6
-rw-r--r--quantum/keymap_extras/keymap_swedish_mac_iso.h6
-rw-r--r--quantum/keymap_extras/keymap_swedish_pro_mac_ansi.h6
-rw-r--r--quantum/keymap_extras/keymap_swedish_pro_mac_iso.h6
-rw-r--r--quantum/keymap_extras/keymap_swiss_de.h6
-rw-r--r--quantum/keymap_extras/keymap_swiss_fr.h6
-rw-r--r--quantum/keymap_extras/keymap_turkish_f.h6
-rw-r--r--quantum/keymap_extras/keymap_turkish_q.h6
-rw-r--r--quantum/keymap_extras/keymap_uk.h6
-rw-r--r--quantum/keymap_extras/keymap_ukrainian.h6
-rw-r--r--quantum/keymap_extras/keymap_us.h6
-rw-r--r--quantum/keymap_extras/keymap_us_extended.h6
-rw-r--r--quantum/keymap_extras/keymap_us_international.h6
-rw-r--r--quantum/keymap_extras/keymap_us_international_linux.h6
-rw-r--r--quantum/keymap_extras/keymap_workman.h6
-rw-r--r--quantum/keymap_extras/keymap_workman_zxcvm.h6
-rw-r--r--quantum/keymap_introspection.c13
-rw-r--r--quantum/led_matrix/led_matrix.c82
-rw-r--r--quantum/led_matrix/led_matrix.h12
-rw-r--r--quantum/led_matrix/led_matrix_types.h6
-rw-r--r--quantum/logging/debug.h2
-rw-r--r--quantum/nvm/eeprom/nvm_dynamic_keymap.c194
-rw-r--r--quantum/nvm/eeprom/nvm_eeconfig.c305
-rw-r--r--quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h63
-rw-r--r--quantum/nvm/eeprom/nvm_eeprom_via_internal.h22
-rw-r--r--quantum/nvm/eeprom/nvm_via.c77
-rw-r--r--quantum/nvm/nvm_dynamic_keymap.h27
-rw-r--r--quantum/nvm/nvm_eeconfig.h111
-rw-r--r--quantum/nvm/nvm_via.h17
-rw-r--r--quantum/nvm/readme.md30
-rw-r--r--quantum/nvm/rules.mk31
-rw-r--r--quantum/os_detection.c26
-rw-r--r--quantum/os_detection.h3
-rw-r--r--quantum/painter/qff.h9
-rw-r--r--quantum/painter/qgf.h17
-rw-r--r--quantum/painter/qp_draw_core.c3
-rw-r--r--quantum/painter/qp_internal.c4
-rw-r--r--quantum/painter/qp_internal_formats.h3
-rw-r--r--quantum/pointing_device/pointing_device.c44
-rw-r--r--quantum/pointing_device/pointing_device.h15
-rw-r--r--quantum/process_keycode/process_autocorrect.c6
-rw-r--r--quantum/process_keycode/process_caps_word.c7
-rw-r--r--quantum/process_keycode/process_clicky.c6
-rw-r--r--quantum/process_keycode/process_connection.c24
-rw-r--r--quantum/process_keycode/process_leader.c6
-rw-r--r--quantum/process_keycode/process_magic.c4
-rw-r--r--quantum/process_keycode/process_oneshot.c25
-rw-r--r--quantum/process_keycode/process_oneshot.h10
-rw-r--r--quantum/process_keycode/process_quantum.c76
-rw-r--r--quantum/process_keycode/process_quantum.h10
-rw-r--r--quantum/process_keycode/process_repeat_key.c5
-rw-r--r--quantum/process_keycode/process_steno.c7
-rw-r--r--quantum/process_keycode/process_underglow.c5
-rw-r--r--quantum/quantum.c99
-rw-r--r--quantum/quantum.h2
-rw-r--r--quantum/raw_hid.c15
-rw-r--r--quantum/rgb_matrix/animations/pixel_flow_anim.h4
-rw-r--r--quantum/rgb_matrix/animations/rgb_matrix_effects.inc3
-rw-r--r--quantum/rgb_matrix/animations/starlight_smooth_anim.h26
-rw-r--r--quantum/rgb_matrix/rgb_matrix.c83
-rw-r--r--quantum/rgb_matrix/rgb_matrix.h14
-rw-r--r--quantum/rgb_matrix/rgb_matrix_types.h6
-rw-r--r--quantum/rgblight/rgblight.c53
-rw-r--r--quantum/rgblight/rgblight.h9
-rw-r--r--quantum/split_common/split_util.c8
-rw-r--r--quantum/split_common/split_util.h2
-rw-r--r--quantum/split_common/transaction_id_define.h4
-rw-r--r--quantum/split_common/transport.c3
-rw-r--r--quantum/unicode/unicode.c5
-rw-r--r--quantum/unicode/unicode.h6
-rw-r--r--quantum/via.c61
-rw-r--r--quantum/via.h27
-rw-r--r--quantum/wear_leveling/tests/rules.mk2
-rw-r--r--quantum/wear_leveling/wear_leveling.c1
-rw-r--r--quantum/wear_leveling/wear_leveling_drivers.h13
-rw-r--r--quantum/wear_leveling/wear_leveling_internal.h14
159 files changed, 3616 insertions, 868 deletions
diff --git a/quantum/action.c b/quantum/action.c
index be85192d25..dd82c9ec99 100644
--- a/quantum/action.c
+++ b/quantum/action.c
@@ -281,6 +281,9 @@ void process_record(keyrecord_t *record) {
if (IS_NOEVENT(record->event)) {
return;
}
+#ifdef FLOW_TAP_TERM
+ flow_tap_update_last_event(record);
+#endif // FLOW_TAP_TERM
if (!process_record_quantum(record)) {
#ifndef NO_ACTION_ONESHOT
@@ -1183,6 +1186,23 @@ bool is_tap_action(action_t action) {
return false;
}
+uint16_t get_tap_keycode(uint16_t keycode) {
+ switch (keycode) {
+ case QK_MOD_TAP ... QK_MOD_TAP_MAX:
+ return QK_MOD_TAP_GET_TAP_KEYCODE(keycode);
+ case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
+ return QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
+ case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
+ // IS_SWAP_HANDS_KEYCODE() tests for the special action keycodes
+ // like SH_TOGG, SH_TT, ..., which overlap the SH_T(kc) range.
+ if (!IS_SWAP_HANDS_KEYCODE(keycode)) {
+ return QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode);
+ }
+ break;
+ }
+ return keycode;
+}
+
/** \brief Debug print (FIXME: Needs better description)
*
* FIXME: Needs documentation.
diff --git a/quantum/action.h b/quantum/action.h
index 7596688f31..7616486c6d 100644
--- a/quantum/action.h
+++ b/quantum/action.h
@@ -128,6 +128,12 @@ void layer_switch(uint8_t new_layer);
bool is_tap_record(keyrecord_t *record);
bool is_tap_action(action_t action);
+/**
+ * Given an MT or LT keycode, returns the tap keycode. Otherwise returns the
+ * original keycode unchanged.
+ */
+uint16_t get_tap_keycode(uint16_t keycode);
+
#ifndef NO_ACTION_TAPPING
void process_record_tap_hint(keyrecord_t *record);
#endif
diff --git a/quantum/action_layer.c b/quantum/action_layer.c
index 7c09a5bd1e..5828dcb824 100644
--- a/quantum/action_layer.c
+++ b/quantum/action_layer.c
@@ -27,11 +27,20 @@ __attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t sta
return default_layer_state_set_user(state);
}
+/** \brief Default Layer State Set At Module Level
+ *
+ * Run module code on default layer state change
+ */
+__attribute__((weak)) layer_state_t default_layer_state_set_modules(layer_state_t state) {
+ return state;
+}
+
/** \brief Default Layer State Set
*
* Static function to set the default layer state, prints debug info and clears keys
*/
static void default_layer_state_set(layer_state_t state) {
+ state = default_layer_state_set_modules(state);
state = default_layer_state_set_kb(state);
ac_dprintf("default_layer_state: ");
default_layer_debug();
@@ -107,11 +116,21 @@ __attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) {
return layer_state_set_user(state);
}
+/** \brief Layer state set modules
+ *
+ * Runs module code on layer state change
+ */
+
+__attribute__((weak)) layer_state_t layer_state_set_modules(layer_state_t state) {
+ return state;
+}
+
/** \brief Layer state set
*
* Sets the layer to match the specified state (a bitmask)
*/
void layer_state_set(layer_state_t state) {
+ state = layer_state_set_modules(state);
state = layer_state_set_kb(state);
ac_dprintf("layer_state: ");
layer_debug();
diff --git a/quantum/action_layer.h b/quantum/action_layer.h
index a2410d49a5..067e33cdb5 100644
--- a/quantum/action_layer.h
+++ b/quantum/action_layer.h
@@ -78,6 +78,7 @@ extern layer_state_t default_layer_state;
void default_layer_debug(void);
void default_layer_set(layer_state_t state);
+__attribute__((weak)) layer_state_t default_layer_state_set_modules(layer_state_t state);
__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state);
__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state);
@@ -114,6 +115,7 @@ void layer_and(layer_state_t state);
void layer_xor(layer_state_t state);
layer_state_t layer_state_set_user(layer_state_t state);
layer_state_t layer_state_set_kb(layer_state_t state);
+layer_state_t layer_state_set_modules(layer_state_t state);
/**
* @brief Applies the tri layer to global layer state. Not be used in layer_state_set_(kb|user) functions.
@@ -149,6 +151,7 @@ layer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_
# define layer_or(state) (void)state
# define layer_and(state) (void)state
# define layer_xor(state) (void)state
+# define layer_state_set_modules(state) (void)state
# define layer_state_set_kb(state) (void)state
# define layer_state_set_user(state) (void)state
# define update_tri_layer(layer1, layer2, layer3)
diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c
index e42a98554d..b105cd60a9 100644
--- a/quantum/action_tapping.c
+++ b/quantum/action_tapping.c
@@ -4,7 +4,9 @@
#include "action.h"
#include "action_layer.h"
#include "action_tapping.h"
+#include "action_util.h"
#include "keycode.h"
+#include "quantum_keycodes.h"
#include "timer.h"
#ifndef NO_ACTION_TAPPING
@@ -49,9 +51,7 @@ __attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *re
}
# endif
-# if defined(CHORDAL_HOLD)
-extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;
-
+# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
# define REGISTERED_TAPS_SIZE 8
// Array of tap-hold keys that have been settled as tapped but not yet released.
static keypos_t registered_taps[REGISTERED_TAPS_SIZE] = {};
@@ -66,6 +66,14 @@ static void registered_taps_del_index(uint8_t i);
/** Logs the registered_taps array for debugging. */
static void debug_registered_taps(void);
+static bool is_mt_or_lt(uint16_t keycode) {
+ return IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode);
+}
+# endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
+
+# if defined(CHORDAL_HOLD)
+extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;
+
/** \brief Finds which queued events should be held according to Chordal Hold.
*
* In a situation with multiple unsettled tap-hold key presses, scan the queue
@@ -82,10 +90,6 @@ static void waiting_buffer_chordal_hold_taps_until(keypos_t key);
/** \brief Processes and pops buffered events until the first tap-hold event. */
static void waiting_buffer_process_regular(void);
-
-static bool is_mt_or_lt(uint16_t keycode) {
- return IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode);
-}
# endif // CHORDAL_HOLD
# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY
@@ -98,6 +102,14 @@ __attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyreco
# include "process_auto_shift.h"
# endif
+# if defined(FLOW_TAP_TERM)
+static uint16_t flow_tap_prev_keycode = KC_NO;
+static uint16_t flow_tap_prev_time = 0;
+static bool flow_tap_expired = true;
+
+static bool flow_tap_key_if_within_term(keyrecord_t *record, uint16_t prev_time);
+# endif // defined(FLOW_TAP_TERM)
+
static keyrecord_t tapping_key = {};
static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};
static uint8_t waiting_buffer_head = 0;
@@ -148,6 +160,12 @@ void action_tapping_process(keyrecord_t record) {
}
if (IS_EVENT(record.event)) {
ac_dprintf("\n");
+ } else {
+# ifdef FLOW_TAP_TERM
+ if (!flow_tap_expired && TIMER_DIFF_16(record.event.time, flow_tap_prev_time) >= INT16_MAX / 2) {
+ flow_tap_expired = true;
+ }
+# endif // FLOW_TAP_TERM
}
}
@@ -205,7 +223,7 @@ void action_tapping_process(keyrecord_t record) {
bool process_tapping(keyrecord_t *keyp) {
const keyevent_t event = keyp->event;
-# if defined(CHORDAL_HOLD)
+# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
if (!event.pressed) {
const int8_t i = registered_tap_find(event.key);
if (i != -1) {
@@ -217,7 +235,7 @@ bool process_tapping(keyrecord_t *keyp) {
debug_registered_taps();
}
}
-# endif // CHORDAL_HOLD
+# endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
// state machine is in the "reset" state, no tapping key is to be
// processed
@@ -227,6 +245,13 @@ bool process_tapping(keyrecord_t *keyp) {
} else if (event.pressed && is_tap_record(keyp)) {
// the currently pressed key is a tapping key, therefore transition
// into the "pressed" tapping key state
+
+# if defined(FLOW_TAP_TERM)
+ if (flow_tap_key_if_within_term(keyp, flow_tap_prev_time)) {
+ return true;
+ }
+# endif // defined(FLOW_TAP_TERM)
+
ac_dprintf("Tapping: Start(Press tap key).\n");
tapping_key = *keyp;
process_record_tap_hint(&tapping_key);
@@ -263,6 +288,27 @@ bool process_tapping(keyrecord_t *keyp) {
// copy tapping state
keyp->tap = tapping_key.tap;
+
+# if defined(FLOW_TAP_TERM)
+ // Now that tapping_key has settled as tapped, check whether
+ // Flow Tap applies to following yet-unsettled keys.
+ uint16_t prev_time = tapping_key.event.time;
+ for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {
+ keyrecord_t *record = &waiting_buffer[waiting_buffer_tail];
+ if (!record->event.pressed) {
+ break;
+ }
+ const int16_t next_time = record->event.time;
+ if (!is_tap_record(record)) {
+ process_record(record);
+ } else if (!flow_tap_key_if_within_term(record, prev_time)) {
+ break;
+ }
+ prev_time = next_time;
+ }
+ debug_waiting_buffer();
+# endif // defined(FLOW_TAP_TERM)
+
// enqueue
return false;
}
@@ -538,6 +584,13 @@ bool process_tapping(keyrecord_t *keyp) {
return true;
} else if (is_tap_record(keyp)) {
// Sequential tap can be interfered with other tap key.
+# if defined(FLOW_TAP_TERM)
+ if (flow_tap_key_if_within_term(keyp, flow_tap_prev_time)) {
+ tapping_key = (keyrecord_t){0};
+ debug_tapping_key();
+ return true;
+ }
+# endif // defined(FLOW_TAP_TERM)
ac_dprintf("Tapping: Start with interfering other tap.\n");
tapping_key = *keyp;
waiting_buffer_scan_tap();
@@ -655,28 +708,7 @@ void waiting_buffer_scan_tap(void) {
}
}
-# ifdef CHORDAL_HOLD
-__attribute__((weak)) bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, uint16_t other_keycode, keyrecord_t *other_record) {
- return get_chordal_hold_default(tap_hold_record, other_record);
-}
-
-bool get_chordal_hold_default(keyrecord_t *tap_hold_record, keyrecord_t *other_record) {
- if (tap_hold_record->event.type != KEY_EVENT || other_record->event.type != KEY_EVENT) {
- return true; // Return true on combos or other non-key events.
- }
-
- char tap_hold_hand = chordal_hold_handedness(tap_hold_record->event.key);
- if (tap_hold_hand == '*') {
- return true;
- }
- char other_hand = chordal_hold_handedness(other_record->event.key);
- return other_hand == '*' || tap_hold_hand != other_hand;
-}
-
-__attribute__((weak)) char chordal_hold_handedness(keypos_t key) {
- return (char)pgm_read_byte(&chordal_hold_layout[key.row][key.col]);
-}
-
+# if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
static void registered_taps_add(keypos_t key) {
if (num_registered_taps >= REGISTERED_TAPS_SIZE) {
ac_dprintf("TAPS OVERFLOW: CLEAR ALL STATES\n");
@@ -714,6 +746,30 @@ static void debug_registered_taps(void) {
ac_dprintf("}\n");
}
+# endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)
+
+# ifdef CHORDAL_HOLD
+__attribute__((weak)) bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, uint16_t other_keycode, keyrecord_t *other_record) {
+ return get_chordal_hold_default(tap_hold_record, other_record);
+}
+
+bool get_chordal_hold_default(keyrecord_t *tap_hold_record, keyrecord_t *other_record) {
+ if (tap_hold_record->event.type != KEY_EVENT || other_record->event.type != KEY_EVENT) {
+ return true; // Return true on combos or other non-key events.
+ }
+
+ char tap_hold_hand = chordal_hold_handedness(tap_hold_record->event.key);
+ if (tap_hold_hand == '*') {
+ return true;
+ }
+ char other_hand = chordal_hold_handedness(other_record->event.key);
+ return other_hand == '*' || tap_hold_hand != other_hand;
+}
+
+__attribute__((weak)) char chordal_hold_handedness(keypos_t key) {
+ return (char)pgm_read_byte(&chordal_hold_layout[key.row][key.col]);
+}
+
static uint8_t waiting_buffer_find_chordal_hold_tap(void) {
keyrecord_t *prev = &tapping_key;
uint16_t prev_keycode = get_record_keycode(&tapping_key, false);
@@ -761,6 +817,100 @@ static void waiting_buffer_process_regular(void) {
}
# endif // CHORDAL_HOLD
+# ifdef FLOW_TAP_TERM
+void flow_tap_update_last_event(keyrecord_t *record) {
+ const uint16_t keycode = get_record_keycode(record, false);
+ // Don't update while a tap-hold key is unsettled.
+ if (record->tap.count == 0 && (waiting_buffer_tail != waiting_buffer_head || (tapping_key.event.pressed && tapping_key.tap.count == 0))) {
+ return;
+ }
+ // Ignore releases of modifiers and held layer switches.
+ if (!record->event.pressed) {
+ switch (keycode) {
+ case MODIFIER_KEYCODE_RANGE:
+ case QK_MOMENTARY ... QK_MOMENTARY_MAX:
+ case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
+# ifndef NO_ACTION_ONESHOT // Ignore one-shot keys.
+ case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
+ case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
+# endif // NO_ACTION_ONESHOT
+# ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.
+ case QK_TRI_LAYER_LOWER:
+ case QK_TRI_LAYER_UPPER:
+# endif // TRI_LAYER_ENABLE
+ return;
+ case QK_MODS ... QK_MODS_MAX:
+ if (QK_MODS_GET_BASIC_KEYCODE(keycode) == KC_NO) {
+ return;
+ }
+ break;
+ case QK_MOD_TAP ... QK_MOD_TAP_MAX:
+ case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
+ if (record->tap.count == 0) {
+ return;
+ }
+ break;
+ }
+ }
+
+ flow_tap_prev_keycode = keycode;
+ flow_tap_prev_time = record->event.time;
+ flow_tap_expired = false;
+}
+
+static bool flow_tap_key_if_within_term(keyrecord_t *record, uint16_t prev_time) {
+ const uint16_t idle_time = TIMER_DIFF_16(record->event.time, prev_time);
+ if (flow_tap_expired || idle_time >= 500) {
+ return false;
+ }
+
+ const uint16_t keycode = get_record_keycode(record, false);
+ if (is_mt_or_lt(keycode)) {
+ uint16_t term = get_flow_tap_term(keycode, record, flow_tap_prev_keycode);
+ if (term > 500) {
+ term = 500;
+ }
+ if (idle_time < term) {
+ debug_event(record->event);
+ ac_dprintf(" within flow tap term (%u < %u) considered a tap\n", idle_time, term);
+ record->tap.count = 1;
+ registered_taps_add(record->event.key);
+ debug_registered_taps();
+ process_record(record);
+ return true;
+ }
+ }
+ return false;
+}
+
+// By default, enable Flow Tap for the keys in the main alphas area and Space.
+// This should work reasonably even if the layout is remapped on the host to an
+// alt layout or international layout (e.g. Dvorak or AZERTY), where these same
+// key positions are mostly used for typing letters.
+__attribute__((weak)) bool is_flow_tap_key(uint16_t keycode) {
+ if ((get_mods() & (MOD_MASK_CG | MOD_BIT_LALT)) != 0) {
+ return false; // Disable Flow Tap on hotkeys.
+ }
+ switch (get_tap_keycode(keycode)) {
+ case KC_SPC:
+ case KC_A ... KC_Z:
+ case KC_DOT:
+ case KC_COMM:
+ case KC_SCLN:
+ case KC_SLSH:
+ return true;
+ }
+ return false;
+}
+
+__attribute__((weak)) uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode) {
+ if (is_flow_tap_key(keycode) && is_flow_tap_key(prev_keycode)) {
+ return FLOW_TAP_TERM;
+ }
+ return 0;
+}
+# endif // FLOW_TAP_TERM
+
/** \brief Logs tapping key if ACTION_DEBUG is enabled. */
static void debug_tapping_key(void) {
ac_dprintf("TAPPING_KEY=");
diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h
index c3c7b999ec..0cf4aa1200 100644
--- a/quantum/action_tapping.h
+++ b/quantum/action_tapping.h
@@ -111,6 +111,66 @@ char chordal_hold_handedness(keypos_t key);
extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;
#endif
+#ifdef FLOW_TAP_TERM
+/**
+ * Callback to specify the keys where Flow Tap is enabled.
+ *
+ * Flow Tap is constrained to certain keys by the following rule: this callback
+ * is called for both the tap-hold key *and* the key press immediately preceding
+ * it. If the callback returns true for both keycodes, Flow Tap is enabled.
+ *
+ * The default implementation of this callback corresponds to
+ *
+ * bool is_flow_tap_key(uint16_t keycode) {
+ * switch (get_tap_keycode(keycode)) {
+ * case KC_SPC:
+ * case KC_A ... KC_Z:
+ * case KC_DOT:
+ * case KC_COMM:
+ * case KC_SCLN:
+ * case KC_SLSH:
+ * return true;
+ * }
+ * return false;
+ * }
+ *
+ * @param keycode Keycode of the key.
+ * @return Whether to enable Flow Tap for this key.
+ */
+bool is_flow_tap_key(uint16_t keycode);
+
+/**
+ * Callback to customize Flow Tap filtering.
+ *
+ * Flow Tap acts only when key events are closer together than this time.
+ *
+ * Return a time of 0 to disable filtering. In this way, Flow Tap may be
+ * disabled for certain tap-hold keys, or when following certain previous keys.
+ *
+ * The default implementation of this callback is
+ *
+ * uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t* record,
+ * uint16_t prev_keycode) {
+ * if (is_flow_tap_key(keycode) && is_flow_tap_key(prev_keycode)) {
+ * return g_flow_tap_term;
+ * }
+ * return 0;
+ * }
+ *
+ * NOTE: If both `is_flow_tap_key()` and `get_flow_tap_term()` are defined, then
+ * `get_flow_tap_term()` takes precedence.
+ *
+ * @param keycode Keycode of the tap-hold key.
+ * @param record keyrecord_t of the tap-hold event.
+ * @param prev_keycode Keycode of the previously pressed key.
+ * @return Time in milliseconds.
+ */
+uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode);
+
+/** Updates the Flow Tap last key and timer. */
+void flow_tap_update_last_event(keyrecord_t *record);
+#endif // FLOW_TAP_TERM
+
#ifdef DYNAMIC_TAPPING_TERM_ENABLE
extern uint16_t g_tapping_term;
#endif
diff --git a/quantum/action_util.c b/quantum/action_util.c
index c0dc4f3822..e821e113ef 100644
--- a/quantum/action_util.c
+++ b/quantum/action_util.c
@@ -21,7 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "action_layer.h"
#include "timer.h"
#include "keycode_config.h"
-#include "usb_device_state.h"
#include <string.h>
extern keymap_config_t keymap_config;
@@ -220,7 +219,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);
}
@@ -319,14 +318,12 @@ void send_nkro_report(void) {
*/
void send_keyboard_report(void) {
#ifdef NKRO_ENABLE
- if (usb_device_state_get_protocol() == USB_PROTOCOL_REPORT && keymap_config.nkro) {
+ if (host_can_send_nkro() && keymap_config.nkro) {
send_nkro_report();
- } else {
- send_6kro_report();
+ return;
}
-#else
- send_6kro_report();
#endif
+ send_6kro_report();
}
/** \brief Get mods
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
index 2cc3c2d661..4e223f0c9a 100644
--- a/quantum/audio/audio.c
+++ b/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) {
diff --git a/quantum/audio/audio.h b/quantum/audio/audio.h
index 054331d6f3..647744a686 100644
--- a/quantum/audio/audio.h
+++ b/quantum/audio/audio.h
@@ -18,6 +18,8 @@
#include <stdint.h>
#include <stdbool.h>
+
+#include "compiler_support.h"
#include "musical_notes.h"
#include "song_list.h"
#include "voices.h"
@@ -28,7 +30,7 @@
# include "audio_dac.h"
#endif
-typedef union {
+typedef union audio_config_t {
uint8_t raw;
struct {
bool enable : 1;
@@ -38,7 +40,7 @@ typedef union {
};
} audio_config_t;
-_Static_assert(sizeof(audio_config_t) == sizeof(uint8_t), "Audio EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(audio_config_t) == sizeof(uint8_t), "Audio EECONFIG out of spec.");
/*
* a 'musical note' is represented by pitch and duration; a 'musical tone' adds intensity and timbre
diff --git a/quantum/backlight/backlight.c b/quantum/backlight/backlight.c
index eb64dd71e8..a7d2a45a51 100644
--- a/quantum/backlight/backlight.c
+++ b/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();
}
diff --git a/quantum/backlight/backlight.h b/quantum/backlight/backlight.h
index c34fb5858d..2faa8fc4f2 100644
--- a/quantum/backlight/backlight.h
+++ b/quantum/backlight/backlight.h
@@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <stdint.h>
#include <stdbool.h>
+#include "compiler_support.h"
+
#ifndef BACKLIGHT_LEVELS
# define BACKLIGHT_LEVELS 3
#elif BACKLIGHT_LEVELS > 31
@@ -34,7 +36,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;
@@ -44,7 +46,7 @@ typedef union {
};
} backlight_config_t;
-_Static_assert(sizeof(backlight_config_t) == sizeof(uint8_t), "Backlight EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(backlight_config_t) == sizeof(uint8_t), "Backlight EECONFIG out of spec.");
void backlight_init(void);
void backlight_toggle(void);
@@ -58,10 +60,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);
diff --git a/quantum/command.c b/quantum/command.c
index 024d96917d..998d9b9aa1 100644
--- a/quantum/command.c
+++ b/quantum/command.c
@@ -243,10 +243,10 @@ static void print_status(void) {
#if !defined(NO_PRINT) && !defined(USER_PRINT)
static void print_eeconfig(void) {
- xprintf("eeconfig:\ndefault_layer: %u\n", eeconfig_read_default_layer());
+ xprintf("eeconfig:\ndefault_layer: %" PRIu32 "\n", (uint32_t)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"
diff --git a/quantum/compiler_support.h b/quantum/compiler_support.h
new file mode 100644
index 0000000000..5c0c4d2835
--- /dev/null
+++ b/quantum/compiler_support.h
@@ -0,0 +1,15 @@
+// Copyright 2025 QMK Contributors
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/**
+ * @brief Perfom an assertion at compile time.
+ *
+ * `_Static_assert` is C<23, while `static_assert` is C++/C23.
+ */
+#if !defined(STATIC_ASSERT)
+# ifdef __cplusplus
+# define STATIC_ASSERT static_assert
+# else
+# define STATIC_ASSERT _Static_assert
+# endif
+#endif
diff --git a/quantum/connection/connection.c b/quantum/connection/connection.c
new file mode 100644
index 0000000000..1ff8876876
--- /dev/null
+++ b/quantum/connection/connection.c
@@ -0,0 +1,141 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "connection.h"
+#include "eeconfig.h"
+#include "usb_util.h"
+#include "util.h"
+
+#ifdef BLUETOOTH_ENABLE
+# include "bluetooth.h"
+#endif
+
+// ======== DEPRECATED DEFINES - DO NOT USE ========
+#ifdef OUTPUT_DEFAULT
+# undef CONNECTION_HOST_DEFAULT
+# define CONNECTION_HOST_DEFAULT OUTPUT_DEFAULT
+#endif
+
+__attribute__((weak)) void set_output_user(uint8_t output) {}
+// ========
+
+#define CONNECTION_HOST_INVALID 0xFF
+
+#ifndef CONNECTION_HOST_DEFAULT
+# define CONNECTION_HOST_DEFAULT CONNECTION_HOST_AUTO
+#endif
+
+static const connection_host_t host_candidates[] = {
+ CONNECTION_HOST_AUTO,
+ CONNECTION_HOST_USB,
+#ifdef BLUETOOTH_ENABLE
+ CONNECTION_HOST_BLUETOOTH,
+#endif
+#if 0
+ CONNECTION_HOST_2P4GHZ,
+#endif
+};
+
+#define HOST_CANDIDATES_COUNT ARRAY_SIZE(host_candidates)
+
+static connection_config_t config = {.desired_host = CONNECTION_HOST_INVALID};
+
+void eeconfig_update_connection_default(void) {
+ config.desired_host = CONNECTION_HOST_DEFAULT;
+
+ eeconfig_update_connection(&config);
+}
+
+void connection_init(void) {
+ eeconfig_read_connection(&config);
+ if (config.desired_host == CONNECTION_HOST_INVALID) {
+ eeconfig_update_connection_default();
+ }
+}
+
+__attribute__((weak)) void connection_host_changed_user(connection_host_t host) {}
+__attribute__((weak)) void connection_host_changed_kb(connection_host_t host) {}
+
+static void handle_host_changed(void) {
+ connection_host_changed_user(config.desired_host);
+ connection_host_changed_kb(config.desired_host);
+
+ // TODO: Remove deprecated callback
+ set_output_user(config.desired_host);
+}
+
+void connection_set_host_noeeprom(connection_host_t host) {
+ if (config.desired_host == host) {
+ return;
+ }
+
+ config.desired_host = host;
+
+ handle_host_changed();
+}
+
+void connection_set_host(connection_host_t host) {
+ connection_set_host_noeeprom(host);
+
+ eeconfig_update_connection(&config);
+}
+
+void connection_next_host_noeeprom(void) {
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < HOST_CANDIDATES_COUNT; i++) {
+ if (host_candidates[i] == config.desired_host) {
+ next = i == HOST_CANDIDATES_COUNT - 1 ? 0 : i + 1;
+ break;
+ }
+ }
+
+ connection_set_host_noeeprom(host_candidates[next]);
+}
+
+void connection_next_host(void) {
+ connection_next_host_noeeprom();
+
+ eeconfig_update_connection(&config);
+}
+
+void connection_prev_host_noeeprom(void) {
+ uint8_t next = 0;
+ for (uint8_t i = 0; i < HOST_CANDIDATES_COUNT; i++) {
+ if (host_candidates[i] == config.desired_host) {
+ next = i == 0 ? HOST_CANDIDATES_COUNT - 1 : i - 1;
+ break;
+ }
+ }
+
+ connection_set_host_noeeprom(host_candidates[next]);
+}
+
+void connection_prev_host(void) {
+ connection_prev_host_noeeprom();
+
+ eeconfig_update_connection(&config);
+}
+
+connection_host_t connection_get_host_raw(void) {
+ return config.desired_host;
+}
+
+connection_host_t connection_auto_detect_host(void) {
+ if (usb_connected_state()) {
+ return CONNECTION_HOST_USB;
+ }
+
+#ifdef BLUETOOTH_ENABLE
+ if (bluetooth_is_connected()) {
+ return CONNECTION_HOST_BLUETOOTH;
+ }
+#endif
+
+ return CONNECTION_HOST_NONE;
+}
+
+connection_host_t connection_get_host(void) {
+ if (config.desired_host == CONNECTION_HOST_AUTO) {
+ return connection_auto_detect_host();
+ }
+ return config.desired_host;
+}
diff --git a/quantum/connection/connection.h b/quantum/connection/connection.h
new file mode 100644
index 0000000000..b25160c759
--- /dev/null
+++ b/quantum/connection/connection.h
@@ -0,0 +1,112 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <stdint.h>
+
+#include "compiler_support.h"
+#include "util.h"
+
+/**
+ * \enum connection_host_t
+ *
+ * An enumeration of the possible hosts.
+ */
+typedef enum connection_host_t {
+ CONNECTION_HOST_AUTO,
+
+ CONNECTION_HOST_NONE,
+ CONNECTION_HOST_USB,
+ CONNECTION_HOST_BLUETOOTH,
+ CONNECTION_HOST_2P4GHZ
+} connection_host_t;
+
+/**
+ * \union connection_config_t
+ *
+ * Configuration structure for the connection subsystem.
+ */
+typedef union connection_config_t {
+ uint8_t raw;
+ connection_host_t desired_host : 8;
+} PACKED connection_config_t;
+
+STATIC_ASSERT(sizeof(connection_config_t) == sizeof(uint8_t), "Connection EECONFIG out of spec.");
+
+/**
+ * \brief Initialize the subsystem.
+ *
+ * This function must be called only once, before any of the below functions can be called.
+ */
+void connection_init(void);
+
+/**
+ * \brief Get currently configured host. Does not resolve 'CONNECTION_HOST_AUTO'.
+ *
+ * \return 'connection_host_t' of the configured host.
+ */
+connection_host_t connection_get_host_raw(void);
+
+/**
+ * \brief Get current active host.
+ *
+ * \return 'connection_host_t' of the configured host.
+ */
+connection_host_t connection_auto_detect_host(void);
+
+/**
+ * \brief Get currently configured host. Resolves 'CONNECTION_HOST_AUTO' using 'connection_auto_detect_host()'.
+ *
+ * \return 'connection_host_t' of the configured host.
+ */
+connection_host_t connection_get_host(void);
+
+/**
+ * \brief Get current host. New state is not written to EEPROM.
+ *
+ * \param host The host to configure.
+ */
+void connection_set_host_noeeprom(connection_host_t host);
+
+/**
+ * \brief Get current host.
+ *
+ * \param host The host to configure.
+ */
+void connection_set_host(connection_host_t host);
+
+/**
+ * \brief Move to the next potential host. New state is not written to EEPROM.
+ *
+ */
+void connection_next_host_noeeprom(void);
+
+/**
+ * \brief Move to the next potential host.
+ *
+ */
+void connection_next_host(void);
+
+/**
+ * \brief Move to the previous potential host. New state is not written to EEPROM.
+ *
+ */
+void connection_prev_host_noeeprom(void);
+
+/**
+ * \brief Move to the previous potential host.
+ *
+ */
+void connection_prev_host(void);
+
+/**
+ * \brief user hook called when changing configured host
+ *
+ */
+void connection_host_changed_user(connection_host_t host);
+
+/**
+ * \brief keyboard hook called when changing configured host
+ *
+ */
+void connection_host_changed_kb(connection_host_t host);
diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index beb7f9d18f..25029e39c4 100644
--- a/quantum/dynamic_keymap.c
+++ b/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
@@ -17,18 +18,9 @@
#include "dynamic_keymap.h"
#include "keymap_introspection.h"
#include "action.h"
-#include "eeprom.h"
-#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 +28,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 +36,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 +75,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 +103,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 +146,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 = offset};
+ send_string_with_delay_impl(send_string_get_next_nvm, &state, DYNAMIC_KEYMAP_MACRO_DELAY);
}
diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h
index 806342efa3..f1d8077b12 100644
--- a/quantum/dynamic_keymap.h
+++ b/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
diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c
index e27f604f12..f14e6ddf97 100644
--- a/quantum/eeconfig.c
+++ b/quantum/eeconfig.c
@@ -1,355 +1,359 @@
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
-#include "eeprom.h"
+#include "debug.h"
#include "eeconfig.h"
+#include "action_layer.h"
+#include "nvm_eeconfig.h"
+#include "keycode_config.h"
-#if defined(EEPROM_DRIVER)
-# include "eeprom_driver.h"
-#endif
+#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
+
+#ifdef LED_MATRIX_ENABLE
+# include "led_matrix_types.h"
+#endif // LED_MATRIX_ENABLE
-#if defined(HAPTIC_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 CONNECTION_ENABLE
+# include "connection.h"
+#endif // CONNECTION_ENABLE
+
+#ifdef VIA_ENABLE
bool via_eeprom_is_valid(void);
void via_eeprom_set_valid(bool valid);
void eeconfig_init_via(void);
-#endif
+#else
+void dynamic_keymap_reset(void);
+#endif // VIA_ENABLE
-_Static_assert((intptr_t)EECONFIG_HANDEDNESS == 14, "EEPROM handedness offset is incorrect");
+#ifndef NKRO_DEFAULT_ON
+# define NKRO_DEFAULT_ON false
+#endif
-/** \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 = NKRO_DEFAULT_ON,
+ .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
+ extern void eeconfig_update_rgblight_default(void);
+ eeconfig_update_rgblight_default();
+#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
+ extern void eeconfig_update_rgb_matrix_default(void);
+ eeconfig_update_rgb_matrix_default();
#endif
+#ifdef LED_MATRIX_ENABLE
+ extern void eeconfig_update_led_matrix_default(void);
+ eeconfig_update_led_matrix_default();
+#endif // LED_MATRIX_ENABLE
+
+#ifdef HAPTIC_ENABLE
+ haptic_config_t haptic_config = {0};
+ eeconfig_update_haptic(&haptic_config);
+ haptic_reset();
+#endif // HAPTIC_ENABLE
+
+#ifdef CONNECTION_ENABLE
+ extern void eeconfig_update_connection_default(void);
+ eeconfig_update_connection_default();
+#endif // CONNECTION_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();
+
+#ifdef RGB_MATRIX_ENABLE
+ extern void eeconfig_force_flush_rgb_matrix(void);
+ eeconfig_force_flush_rgb_matrix();
+#endif // RGB_MATRIX_ENABLE
+#ifdef LED_MATRIX_ENABLE
+ extern void eeconfig_force_flush_led_matrix(void);
+ eeconfig_force_flush_led_matrix();
+#endif // LED_MATRIX_ENABLE
}
-/** \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);
+}
- eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val);
+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
+
+#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
-/** \brief eeconfig read keymap
- *
- * FIXME: needs doc
- */
-uint16_t eeconfig_read_keymap(void) {
- return eeprom_read_word(EECONFIG_KEYMAP);
+#ifdef STENO_ENABLE
+uint8_t eeconfig_read_steno_mode(void) {
+ return nvm_eeconfig_read_steno_mode();
}
-/** \brief eeconfig update keymap
- *
- * FIXME: needs doc
- */
-void eeconfig_update_keymap(uint16_t val) {
- eeprom_update_word(EECONFIG_KEYMAP, val);
+void eeconfig_update_steno_mode(uint8_t val) {
+ nvm_eeconfig_update_steno_mode(val);
}
+#endif // STENO_ENABLE
-/** \brief eeconfig read audio
- *
- * FIXME: needs doc
- */
-uint8_t eeconfig_read_audio(void) {
- return eeprom_read_byte(EECONFIG_AUDIO);
+#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 audio
- *
- * FIXME: needs doc
- */
-void eeconfig_update_audio(uint8_t val) {
- eeprom_update_byte(EECONFIG_AUDIO, 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
+
+#ifdef LED_MATRIX_ENABLE
+void eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config) {
+ nvm_eeconfig_read_led_matrix(led_matrix_config);
+}
+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
+
+#ifdef CONNECTION_ENABLE
+void eeconfig_read_connection(connection_config_t *config) {
+ nvm_eeconfig_read_connection(config);
+}
+void eeconfig_update_connection(const connection_config_t *config) {
+ nvm_eeconfig_update_connection(config);
+}
+#endif // CONNECTION_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
diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h
index 11cf1ccbca..d4d8d957be 100644
--- a/quantum/eeconfig.h
+++ b/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,42 @@ 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
+
+#ifdef CONNECTION_ENABLE
+typedef union connection_config_t connection_config_t;
+void eeconfig_read_connection(connection_config_t *config);
+void eeconfig_update_connection(const connection_config_t *config);
#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 +170,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 +198,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) {}
diff --git a/quantum/haptic.c b/quantum/haptic.c
index 81bad469b3..8a743480ff 100644
--- a/quantum/haptic.c
+++ b/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
diff --git a/quantum/haptic.h b/quantum/haptic.h
index b283d5d268..f854c75ec3 100644
--- a/quantum/haptic.h
+++ b/quantum/haptic.h
@@ -20,6 +20,8 @@
#include <stdint.h>
#include <stdbool.h>
+#include "compiler_support.h"
+
#ifndef HAPTIC_DEFAULT_FEEDBACK
# define HAPTIC_DEFAULT_FEEDBACK 0
#endif
@@ -28,7 +30,7 @@
#endif
/* EEPROM config settings */
-typedef union {
+typedef union haptic_config_t {
uint32_t raw;
struct {
bool enable : 1;
@@ -42,7 +44,7 @@ typedef union {
};
} haptic_config_t;
-_Static_assert(sizeof(haptic_config_t) == sizeof(uint32_t), "Haptic EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(haptic_config_t) == sizeof(uint32_t), "Haptic EECONFIG out of spec.");
typedef enum HAPTIC_FEEDBACK {
KEY_PRESS,
diff --git a/quantum/keyboard.c b/quantum/keyboard.c
index ad740de4b3..c1a6d444a5 100644
--- a/quantum/keyboard.c
+++ b/quantum/keyboard.c
@@ -122,6 +122,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef SPLIT_KEYBOARD
# include "split_util.h"
#endif
+#ifdef BATTERY_DRIVER
+# include "battery.h"
+#endif
#ifdef BLUETOOTH_ENABLE
# include "bluetooth.h"
#endif
@@ -143,6 +146,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef LAYER_LOCK_ENABLE
# include "layer_lock.h"
#endif
+#ifdef CONNECTION_ENABLE
+# include "connection.h"
+#endif
static uint32_t last_input_modification_time = 0;
uint32_t last_input_activity_time(void) {
@@ -429,8 +435,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();
@@ -462,6 +468,9 @@ void keyboard_init(void) {
#endif
matrix_init();
quantum_init();
+#ifdef CONNECTION_ENABLE
+ connection_init();
+#endif
led_init_ports();
#ifdef BACKLIGHT_ENABLE
backlight_init_ports();
@@ -500,8 +509,9 @@ void keyboard_init(void) {
steno_init();
#endif
#if defined(NKRO_ENABLE) && defined(FORCE_NKRO)
+# pragma message "FORCE_NKRO option is now deprecated - Please migrate to NKRO_DEFAULT_ON instead."
keymap_config.nkro = 1;
- eeconfig_update_keymap(keymap_config.raw);
+ eeconfig_update_keymap(&keymap_config);
#endif
#ifdef DIP_SWITCH_ENABLE
dip_switch_init();
@@ -522,6 +532,9 @@ void keyboard_init(void) {
// init after split init
pointing_device_init();
#endif
+#ifdef BATTERY_DRIVER
+ battery_init();
+#endif
#ifdef BLUETOOTH_ENABLE
bluetooth_init();
#endif
@@ -782,6 +795,10 @@ void keyboard_task(void) {
joystick_task();
#endif
+#ifdef BATTERY_DRIVER
+ battery_task();
+#endif
+
#ifdef BLUETOOTH_ENABLE
bluetooth_task();
#endif
diff --git a/quantum/keycode_config.h b/quantum/keycode_config.h
index d1352c302e..804f1381d0 100644
--- a/quantum/keycode_config.h
+++ b/quantum/keycode_config.h
@@ -16,9 +16,7 @@
#pragma once
-#ifdef __cplusplus
-# define _Static_assert static_assert
-#endif
+#include "compiler_support.h"
#include "eeconfig.h"
#include "keycode.h"
@@ -28,7 +26,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;
@@ -47,6 +45,6 @@ typedef union {
};
} keymap_config_t;
-_Static_assert(sizeof(keymap_config_t) == sizeof(uint16_t), "Keycode (magic) EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(keymap_config_t) == sizeof(uint16_t), "Keycode (magic) EECONFIG out of spec.");
extern keymap_config_t keymap_config;
diff --git a/quantum/keycode_string.c b/quantum/keycode_string.c
new file mode 100644
index 0000000000..18044f2ef6
--- /dev/null
+++ b/quantum/keycode_string.c
@@ -0,0 +1,564 @@
+// Copyright 2024-2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "keycode_string.h"
+
+#include <string.h>
+#include "bitwise.h"
+#include "keycode.h"
+#include "progmem.h"
+#include "quantum_keycodes.h"
+#include "util.h"
+
+typedef int_fast8_t index_t;
+
+// clang-format off
+/** Packs a 7-char keycode name, ignoring the third char, as 3 words. */
+#define KEYCODE_NAME7(c0, c1, unused_c2, c3, c4, c5, c6) \
+ ((uint16_t)c0) | (((uint16_t)c1) << 8), \
+ ((uint16_t)c3) | (((uint16_t)c4) << 8), \
+ ((uint16_t)c5) | (((uint16_t)c6) << 8)
+
+/**
+ * @brief Names of some common keycodes.
+ *
+ * Each (keycode, name) entry is stored flat in 8 bytes in PROGMEM. Names in
+ * this table must be at most 7 chars long and have an underscore '_' for the
+ * third char. This underscore is assumed and not actually stored.
+ *
+ * To save memory, feature-specific key entries are ifdef'd to include them only
+ * when their feature is enabled.
+ */
+static const uint16_t common_names[] PROGMEM = {
+ KC_TRNS, KEYCODE_NAME7('K', 'C', '_', 'T', 'R', 'N', 'S'),
+ KC_ENT , KEYCODE_NAME7('K', 'C', '_', 'E', 'N', 'T', 0 ),
+ KC_ESC , KEYCODE_NAME7('K', 'C', '_', 'E', 'S', 'C', 0 ),
+ KC_BSPC, KEYCODE_NAME7('K', 'C', '_', 'B', 'S', 'P', 'C'),
+ KC_TAB , KEYCODE_NAME7('K', 'C', '_', 'T', 'A', 'B', 0 ),
+ KC_SPC , KEYCODE_NAME7('K', 'C', '_', 'S', 'P', 'C', 0 ),
+ KC_MINS, KEYCODE_NAME7('K', 'C', '_', 'M', 'I', 'N', 'S'),
+ KC_EQL , KEYCODE_NAME7('K', 'C', '_', 'E', 'Q', 'L', 0 ),
+ KC_LBRC, KEYCODE_NAME7('K', 'C', '_', 'L', 'B', 'R', 'C'),
+ KC_RBRC, KEYCODE_NAME7('K', 'C', '_', 'R', 'B', 'R', 'C'),
+ KC_BSLS, KEYCODE_NAME7('K', 'C', '_', 'B', 'S', 'L', 'S'),
+ KC_NUHS, KEYCODE_NAME7('K', 'C', '_', 'N', 'U', 'H', 'S'),
+ KC_SCLN, KEYCODE_NAME7('K', 'C', '_', 'S', 'C', 'L', 'N'),
+ KC_QUOT, KEYCODE_NAME7('K', 'C', '_', 'Q', 'U', 'O', 'T'),
+ KC_GRV , KEYCODE_NAME7('K', 'C', '_', 'G', 'R', 'V', 0 ),
+ KC_COMM, KEYCODE_NAME7('K', 'C', '_', 'C', 'O', 'M', 'M'),
+ KC_DOT , KEYCODE_NAME7('K', 'C', '_', 'D', 'O', 'T', 0 ),
+ KC_SLSH, KEYCODE_NAME7('K', 'C', '_', 'S', 'L', 'S', 'H'),
+ KC_CAPS, KEYCODE_NAME7('K', 'C', '_', 'C', 'A', 'P', 'S'),
+ KC_PSCR, KEYCODE_NAME7('K', 'C', '_', 'P', 'S', 'C', 'R'),
+ KC_PAUS, KEYCODE_NAME7('K', 'C', '_', 'P', 'A', 'U', 'S'),
+ KC_INS , KEYCODE_NAME7('K', 'C', '_', 'I', 'N', 'S', 0 ),
+ KC_HOME, KEYCODE_NAME7('K', 'C', '_', 'H', 'O', 'M', 'E'),
+ KC_PGUP, KEYCODE_NAME7('K', 'C', '_', 'P', 'G', 'U', 'P'),
+ KC_DEL , KEYCODE_NAME7('K', 'C', '_', 'D', 'E', 'L', 0 ),
+ KC_END , KEYCODE_NAME7('K', 'C', '_', 'E', 'N', 'D', 0 ),
+ KC_PGDN, KEYCODE_NAME7('K', 'C', '_', 'P', 'G', 'D', 'N'),
+ KC_RGHT, KEYCODE_NAME7('K', 'C', '_', 'R', 'G', 'H', 'T'),
+ KC_LEFT, KEYCODE_NAME7('K', 'C', '_', 'L', 'E', 'F', 'T'),
+ KC_DOWN, KEYCODE_NAME7('K', 'C', '_', 'D', 'O', 'W', 'N'),
+ KC_UP , KEYCODE_NAME7('K', 'C', '_', 'U', 'P', 0 , 0 ),
+ KC_NUBS, KEYCODE_NAME7('K', 'C', '_', 'N', 'U', 'B', 'S'),
+ KC_HYPR, KEYCODE_NAME7('K', 'C', '_', 'H', 'Y', 'P', 'R'),
+ KC_MEH , KEYCODE_NAME7('K', 'C', '_', 'M', 'E', 'H', 0 ),
+#ifdef EXTRAKEY_ENABLE
+ KC_WHOM, KEYCODE_NAME7('K', 'C', '_', 'W', 'H', 'O', 'M'),
+ KC_WBAK, KEYCODE_NAME7('K', 'C', '_', 'W', 'B', 'A', 'K'),
+ KC_WFWD, KEYCODE_NAME7('K', 'C', '_', 'W', 'F', 'W', 'D'),
+ KC_WSTP, KEYCODE_NAME7('K', 'C', '_', 'W', 'S', 'T', 'P'),
+ KC_WREF, KEYCODE_NAME7('K', 'C', '_', 'W', 'R', 'E', 'F'),
+ KC_MNXT, KEYCODE_NAME7('K', 'C', '_', 'M', 'N', 'X', 'T'),
+ KC_MPRV, KEYCODE_NAME7('K', 'C', '_', 'M', 'P', 'R', 'V'),
+ KC_MPLY, KEYCODE_NAME7('K', 'C', '_', 'M', 'P', 'L', 'Y'),
+ KC_MUTE, KEYCODE_NAME7('K', 'C', '_', 'M', 'U', 'T', 'E'),
+ KC_VOLU, KEYCODE_NAME7('K', 'C', '_', 'V', 'O', 'L', 'U'),
+ KC_VOLD, KEYCODE_NAME7('K', 'C', '_', 'V', 'O', 'L', 'D'),
+#endif // EXTRAKEY_ENABLE
+#ifdef MOUSEKEY_ENABLE
+ MS_LEFT, KEYCODE_NAME7('M', 'S', '_', 'L', 'E', 'F', 'T'),
+ MS_RGHT, KEYCODE_NAME7('M', 'S', '_', 'R', 'G', 'H', 'T'),
+ MS_UP , KEYCODE_NAME7('M', 'S', '_', 'U', 'P', 0 , 0 ),
+ MS_DOWN, KEYCODE_NAME7('M', 'S', '_', 'D', 'O', 'W', 'N'),
+ MS_WHLL, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'L'),
+ MS_WHLR, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'R'),
+ MS_WHLU, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'U'),
+ MS_WHLD, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'D'),
+#endif // MOUSEKEY_ENABLE
+#ifdef SWAP_HANDS_ENABLE
+ SH_ON , KEYCODE_NAME7('S', 'H', '_', 'O', 'N', 0 , 0 ),
+ SH_OFF , KEYCODE_NAME7('S', 'H', '_', 'O', 'F', 'F', 0 ),
+ SH_MON , KEYCODE_NAME7('S', 'H', '_', 'M', 'O', 'N', 0 ),
+ SH_MOFF, KEYCODE_NAME7('S', 'H', '_', 'M', 'O', 'F', 'F'),
+ SH_TOGG, KEYCODE_NAME7('S', 'H', '_', 'T', 'O', 'G', 'G'),
+ SH_TT , KEYCODE_NAME7('S', 'H', '_', 'T', 'T', 0 , 0 ),
+# if !defined(NO_ACTION_ONESHOT)
+ SH_OS , KEYCODE_NAME7('S', 'H', '_', 'O', 'S', 0 , 0 ),
+# endif // !defined(NO_ACTION_ONESHOT)
+#endif // SWAP_HANDS_ENABLE
+#ifdef LEADER_ENABLE
+ QK_LEAD, KEYCODE_NAME7('Q', 'K', '_', 'L', 'E', 'A', 'D'),
+#endif // LEADER_ENABLE
+#ifdef KEY_LOCK_ENABLE
+ QK_LOCK, KEYCODE_NAME7('Q', 'K', '_', 'L', 'O', 'C', 'K'),
+#endif // KEY_LOCK_ENABLE
+#ifdef TRI_LAYER_ENABLE
+ TL_LOWR, KEYCODE_NAME7('T', 'L', '_', 'L', 'O', 'W', 'R'),
+ TL_UPPR, KEYCODE_NAME7('T', 'L', '_', 'U', 'P', 'P', 'R'),
+#endif // TRI_LAYER_ENABLE
+#ifdef GRAVE_ESC_ENABLE
+ QK_GESC, KEYCODE_NAME7('Q', 'K', '_', 'G', 'E', 'S', 'C'),
+#endif // GRAVE_ESC_ENABLE
+#ifdef CAPS_WORD_ENABLE
+ CW_TOGG, KEYCODE_NAME7('C', 'W', '_', 'T', 'O', 'G', 'G'),
+#endif // CAPS_WORD_ENABLE
+#ifdef SECURE_ENABLE
+ SE_LOCK, KEYCODE_NAME7('S', 'E', '_', 'L', 'O', 'C', 'K'),
+ SE_UNLK, KEYCODE_NAME7('S', 'E', '_', 'U', 'N', 'L', 'K'),
+ SE_TOGG, KEYCODE_NAME7('S', 'E', '_', 'T', 'O', 'G', 'G'),
+ SE_REQ , KEYCODE_NAME7('S', 'E', '_', 'R', 'E', 'Q', 0 ),
+#endif // SECURE_ENABLE
+#ifdef LAYER_LOCK_ENABLE
+ QK_LLCK, KEYCODE_NAME7('Q', 'K', '_', 'L', 'L', 'C', 'K'),
+#endif // LAYER_LOCK_ENABLE
+ EE_CLR , KEYCODE_NAME7('E', 'E', '_', 'C', 'L', 'R', 0 ),
+ QK_BOOT, KEYCODE_NAME7('Q', 'K', '_', 'B', 'O', 'O', 'T'),
+ DB_TOGG, KEYCODE_NAME7('D', 'B', '_', 'T', 'O', 'G', 'G'),
+};
+// clang-format on
+
+/** Users can override this to define names of additional keycodes. */
+__attribute__((weak)) const keycode_string_name_t* keycode_string_names_data_user = NULL;
+__attribute__((weak)) uint16_t keycode_string_names_size_user = 0;
+/** Keyboard vendors can override this to define names of additional keycodes. */
+__attribute__((weak)) const keycode_string_name_t* keycode_string_names_data_kb = NULL;
+__attribute__((weak)) uint16_t keycode_string_names_size_kb = 0;
+/** Names of the 4 mods on each hand. */
+static const char mod_names[] PROGMEM = "CTL\0SFT\0ALT\0GUI";
+/** Internal buffer for holding a stringified keycode. */
+static char buffer[32];
+#define BUFFER_MAX_LEN (sizeof(buffer) - 1)
+static index_t buffer_len;
+
+/** Finds the name of a keycode in `common_names` or returns NULL. */
+static const char* search_common_names(uint16_t keycode) {
+ static uint8_t buffer[8];
+
+ for (int_fast16_t offset = 0; offset < ARRAY_SIZE(common_names); offset += 4) {
+ if (keycode == pgm_read_word(common_names + offset)) {
+ const uint16_t w0 = pgm_read_word(common_names + offset + 1);
+ const uint16_t w1 = pgm_read_word(common_names + offset + 2);
+ const uint16_t w2 = pgm_read_word(common_names + offset + 3);
+ buffer[0] = (uint8_t)w0;
+ buffer[1] = (uint8_t)(w0 >> 8);
+ buffer[2] = '_';
+ buffer[3] = (uint8_t)w1;
+ buffer[4] = (uint8_t)(w1 >> 8);
+ buffer[5] = (uint8_t)w2;
+ buffer[6] = (uint8_t)(w2 >> 8);
+ buffer[7] = 0;
+ return (const char*)buffer;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Finds the name of a keycode in table or returns NULL.
+ *
+ * @param data Pointer to table to be searched.
+ * @param size Numer of entries in the table.
+ * @return Name string for the keycode, or NULL if not found.
+ */
+static const char* search_table(const keycode_string_name_t* data, uint16_t size, uint16_t keycode) {
+ if (data != NULL) {
+ for (uint16_t i = 0; i < size; ++i) {
+ if (data[i].keycode == keycode) {
+ return data[i].name;
+ }
+ }
+ }
+ return NULL;
+}
+
+/** Formats `number` in `base`, either 10 or 16. */
+static char* number_string(uint16_t number, int8_t base) {
+ static char result[7];
+ result[sizeof(result) - 1] = '\0';
+ index_t i = sizeof(result) - 1;
+ do {
+ const uint8_t digit = number % base;
+ number /= base;
+ result[--i] = (digit < 10) ? (char)(digit + UINT8_C('0')) : (char)(digit + (UINT8_C('A') - 10));
+ } while (number > 0 && i > 0);
+
+ if (base == 16 && i >= 2) {
+ result[--i] = 'x';
+ result[--i] = '0';
+ }
+ return result + i;
+}
+
+/** Appends `str` to `buffer`, truncating if the result would overflow. */
+static void append(const char* str) {
+ char* dest = buffer + buffer_len;
+ index_t i;
+ for (i = 0; buffer_len + i < BUFFER_MAX_LEN && str[i]; ++i) {
+ dest[i] = str[i];
+ }
+ buffer_len += i;
+ buffer[buffer_len] = '\0';
+}
+
+/** Same as append(), but where `str` is a PROGMEM string. */
+static void append_P(const char* str) {
+ char* dest = buffer + buffer_len;
+ index_t i;
+ for (i = 0; buffer_len + i < BUFFER_MAX_LEN; ++i) {
+ const char c = pgm_read_byte(&str[i]);
+ if (c == '\0') {
+ break;
+ }
+ dest[i] = c;
+ }
+ buffer_len += i;
+ buffer[buffer_len] = '\0';
+}
+
+/** Appends a single char to `buffer` if there is space. */
+static void append_char(char c) {
+ if (buffer_len < BUFFER_MAX_LEN) {
+ buffer[buffer_len] = c;
+ buffer[++buffer_len] = '\0';
+ }
+}
+
+/** Formats `number` in `base`, either 10 or 16, and appends it to `buffer`. */
+static void append_number(uint16_t number, int8_t base) {
+ append(number_string(number, base));
+}
+
+/** Stringifies 5-bit mods and appends it to `buffer`. */
+static void append_5_bit_mods(uint8_t mods) {
+ const bool is_rhs = mods > 15;
+ const uint8_t csag = mods & 15;
+ if (csag != 0 && (csag & (csag - 1)) == 0) { // One mod is set.
+ append_P(PSTR("MOD_"));
+ append_char(is_rhs ? 'R' : 'L');
+ append_P(&mod_names[4 * biton(csag)]);
+ } else { // Fallback: write the mod as a hex value.
+ append_number(mods, 16);
+ }
+}
+
+/**
+ * @brief Writes a keycode of the format `name` + "(" + `param` + ")".
+ * @note `name` is a PROGMEM string, `param` is not.
+ */
+static void append_unary_keycode(const char* name, const char* param) {
+ append_P(name);
+ append_char('(');
+ append(param);
+ append_char(')');
+}
+
+/**
+ * @brief Writes a keycode of the format `name` + `number`.
+ * @note `name` is a PROGMEM string.
+ */
+static void append_numbered_keycode(const char* name, uint16_t number) {
+ append_P(name);
+ append_number(number, 10);
+}
+
+/** Stringifies `keycode` and appends it to `buffer`. */
+static void append_keycode(uint16_t keycode) {
+ // In case there is overlap among tables, search `keycode_string_names_user`
+ // first so that it takes precedence.
+ const char* keycode_name = search_table(keycode_string_names_data_user, keycode_string_names_size_user, keycode);
+ if (keycode_name) {
+ append(keycode_name);
+ return;
+ }
+ keycode_name = search_table(keycode_string_names_data_kb, keycode_string_names_size_kb, keycode);
+ if (keycode_name) {
+ append(keycode_name);
+ return;
+ }
+ keycode_name = search_common_names(keycode);
+ if (keycode_name) {
+ append(keycode_name);
+ return;
+ }
+
+ if (keycode <= 255) { // Basic keycodes.
+ switch (keycode) {
+ // Modifiers KC_LSFT, KC_RCTL, etc.
+ case MODIFIER_KEYCODE_RANGE: {
+ const uint8_t i = keycode - KC_LCTL;
+ const bool is_rhs = i > 3;
+ append_P(PSTR("KC_"));
+ append_char(is_rhs ? 'R' : 'L');
+ append_P(&mod_names[4 * (i & 3)]);
+ }
+ return;
+
+ // Letters A-Z.
+ case KC_A ... KC_Z:
+ append_P(PSTR("KC_"));
+ append_char((char)(keycode + (UINT8_C('A') - KC_A)));
+ return;
+
+ // Digits 0-9 (NOTE: Unlike the ASCII order, KC_0 comes *after* KC_9.)
+ case KC_1 ... KC_0:
+ append_numbered_keycode(PSTR("KC_"), (keycode - (KC_1 - 1)) % 10);
+ return;
+
+ // Keypad digits.
+ case KC_KP_1 ... KC_KP_0:
+ append_numbered_keycode(PSTR("KC_KP_"), (keycode - (KC_KP_1 - 1)) % 10);
+ return;
+
+ // Function keys. F1-F12 and F13-F24 are coded in separate ranges.
+ case KC_F1 ... KC_F12:
+ append_numbered_keycode(PSTR("KC_F"), keycode - (KC_F1 - 1));
+ return;
+
+ case KC_F13 ... KC_F24:
+ append_numbered_keycode(PSTR("KC_F"), keycode - (KC_F13 - 13));
+ return;
+ }
+ }
+
+ // clang-format off
+ switch (keycode) {
+ // A modified keycode, like S(KC_1) for Shift + 1 = !. This implementation
+ // only covers modified keycodes where one modifier is applied, e.g. a
+ // Ctrl + Shift + kc or Hyper + kc keycode is not formatted.
+ case QK_MODS ... QK_MODS_MAX: {
+ uint8_t mods = QK_MODS_GET_MODS(keycode);
+ const bool is_rhs = mods > 15;
+ mods &= 15;
+ if (mods != 0 && (mods & (mods - 1)) == 0) { // One mod is set.
+ const char* name = &mod_names[4 * biton(mods)];
+ if (is_rhs) {
+ append_char('R');
+ append_P(name);
+ } else {
+ append_char(pgm_read_byte(&name[0]));
+ }
+ append_char('(');
+ append_keycode(QK_MODS_GET_BASIC_KEYCODE(keycode));
+ append_char(')');
+ return;
+ }
+ } break;
+
+#if !defined(NO_ACTION_ONESHOT)
+ case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: // One-shot mod OSM(mod) key.
+ append_P(PSTR("OSM("));
+ append_5_bit_mods(QK_ONE_SHOT_MOD_GET_MODS(keycode));
+ append_char(')');
+ return;
+#endif // !defined(NO_ACTION_ONESHOT)
+
+ // Various layer switch keys.
+ case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: // Layer-tap LT(layer,kc) key.
+ append_P(PSTR("LT("));
+ append_number(QK_LAYER_TAP_GET_LAYER(keycode), 10);
+ append_char(',');
+ append_keycode(QK_LAYER_TAP_GET_TAP_KEYCODE(keycode));
+ append_char(')');
+ return;
+
+ case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: // LM(layer,mod) key.
+ append_P(PSTR("LM("));
+ append_number(QK_LAYER_MOD_GET_LAYER(keycode), 10);
+ append_char(',');
+ append_5_bit_mods(QK_LAYER_MOD_GET_MODS(keycode));
+ append_char(')');
+ return;
+
+ case QK_TO ... QK_TO_MAX: // TO(layer) key.
+ append_unary_keycode(PSTR("TO"), number_string(QK_TO_GET_LAYER(keycode), 10));
+ return;
+
+ case QK_MOMENTARY ... QK_MOMENTARY_MAX: // MO(layer) key.
+ append_unary_keycode(PSTR("MO"), number_string(QK_MOMENTARY_GET_LAYER(keycode), 10));
+ return;
+
+ case QK_DEF_LAYER ... QK_DEF_LAYER_MAX: // DF(layer) key.
+ append_unary_keycode(PSTR("DF"), number_string(QK_DEF_LAYER_GET_LAYER(keycode), 10));
+ return;
+
+ case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: // TG(layer) key.
+ append_unary_keycode(PSTR("TG"), number_string(QK_TOGGLE_LAYER_GET_LAYER(keycode), 10));
+ return;
+
+#if !defined(NO_ACTION_ONESHOT)
+ case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: // OSL(layer) key.
+ append_unary_keycode(PSTR("OSL"), number_string(QK_ONE_SHOT_LAYER_GET_LAYER(keycode), 10));
+ return;
+#endif // !defined(NO_ACTION_ONESHOT)
+
+ case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: // TT(layer) key.
+ append_unary_keycode(PSTR("TT"), number_string(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode), 10));
+ return;
+
+ case QK_PERSISTENT_DEF_LAYER ... QK_PERSISTENT_DEF_LAYER_MAX: // PDF(layer) key.
+ append_unary_keycode(PSTR("PDF"), number_string(QK_PERSISTENT_DEF_LAYER_GET_LAYER(keycode), 10));
+ return;
+
+ // Mod-tap MT(mod,kc) key. This implementation formats the MT keys where
+ // one modifier is applied. For MT keys with multiple modifiers, the mod
+ // arg is written numerically as a hex code.
+ case QK_MOD_TAP ... QK_MOD_TAP_MAX: {
+ uint8_t mods = QK_MOD_TAP_GET_MODS(keycode);
+ const bool is_rhs = mods > 15;
+ const uint8_t csag = mods & 15;
+ if (csag != 0 && (csag & (csag - 1)) == 0) { // One mod is set.
+ append_char(is_rhs ? 'R' : 'L');
+ append_P(&mod_names[4 * biton(csag)]);
+ append_P(PSTR("_T("));
+ } else if (mods == MOD_HYPR) {
+ append_P(PSTR("HYPR_T("));
+ } else if (mods == MOD_MEH) {
+ append_P(PSTR("MEH_T("));
+ } else {
+ append_P(PSTR("MT("));
+ append_number(mods, 16);
+ append_char(',');
+ }
+ append_keycode(QK_MOD_TAP_GET_TAP_KEYCODE(keycode));
+ append_char(')');
+ } return;
+
+ case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: // Tap dance TD(i) key.
+ append_unary_keycode(PSTR("TD"), number_string(QK_TAP_DANCE_GET_INDEX(keycode), 10));
+ return;
+
+#ifdef UNICODE_ENABLE
+ case QK_UNICODE ... QK_UNICODE_MAX: // Unicode UC(codepoint) key.
+ append_unary_keycode(PSTR("UC"), number_string(QK_UNICODE_GET_CODE_POINT(keycode), 16));
+ return;
+#elif defined(UNICODEMAP_ENABLE)
+ case QK_UNICODEMAP ... QK_UNICODEMAP_MAX: // Unicode Map UM(i) key.
+ append_unary_keycode(PSTR("UM"), number_string(QK_UNICODEMAP_GET_INDEX(keycode), 10));
+ return;
+
+ case QK_UNICODEMAP_PAIR ... QK_UNICODEMAP_PAIR_MAX: { // UP(i,j) key.
+ const uint8_t i = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode);
+ const uint8_t j = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode);
+ append_P(PSTR("UP("));
+ append_number(i, 10);
+ append_char(',');
+ append_number(j, 10);
+ append_char(')');
+ } return;
+#endif
+#ifdef MOUSEKEY_ENABLE
+ case MS_BTN1 ... MS_BTN8: // Mouse button keycode.
+ append_numbered_keycode(PSTR("MS_BTN"), keycode - (MS_BTN1 - 1));
+ return;
+#endif // MOUSEKEY_ENABLE
+#ifdef SWAP_HANDS_ENABLE
+ case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: // Swap Hands SH_T(kc) key.
+ if (!IS_SWAP_HANDS_KEYCODE(keycode)) {
+ append_P(PSTR("SH_T("));
+ append_keycode(QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode));
+ append_char(')');
+ return;
+ }
+ break;
+#endif // SWAP_HANDS_ENABLE
+#ifdef JOYSTICK_ENABLE
+ case JOYSTICK_KEYCODE_RANGE: // Joystick JS_ key.
+ append_numbered_keycode(PSTR("JS_"), keycode - JS_0);
+ return;
+#endif // JOYSTICK_ENABLE
+#ifdef PROGRAMMABLE_BUTTON_ENABLE
+ case PROGRAMMABLE_BUTTON_KEYCODE_RANGE: // Programmable button PB_ key.
+ append_numbered_keycode(PSTR("PB_"), keycode - (PB_1 - 1));
+ return;
+#endif // PROGRAMMABLE_BUTTON_ENABLE
+
+ case MACRO_KEYCODE_RANGE: // Macro range MC_ keycode.
+ append_numbered_keycode(PSTR("MC_"), keycode - MC_0);
+ return;
+
+ case KB_KEYCODE_RANGE: // Keyboard range keycode.
+ append_numbered_keycode(PSTR("QK_KB_"), keycode - QK_KB_0);
+ return;
+
+ case USER_KEYCODE_RANGE: // User range keycode.
+ append_numbered_keycode(PSTR("QK_USER_"), keycode - QK_USER_0);
+ return;
+
+ // It would take a nontrivial amount of string data to cover some
+ // feature-specific keycodes, such as those for MIDI and lighting. As a
+ // fallback while still providing some information, we stringify
+ // remaining keys in known code ranges as "QK_<feature>+<number>".
+#ifdef MAGIC_ENABLE
+ case MAGIC_KEYCODE_RANGE:
+ append_numbered_keycode(PSTR("QK_MAGIC+"), keycode - QK_MAGIC);
+ return;
+#endif // MAGIC_ENABLE
+#ifdef MIDI_ENABLE
+ case MIDI_KEYCODE_RANGE:
+ append_numbered_keycode(PSTR("QK_MIDI+"), keycode - QK_MIDI);
+ return;
+#endif // MIDI_ENABLE
+#ifdef SEQUENCER_ENABLE
+ case SEQUENCER_KEYCODE_RANGE:
+ append_numbered_keycode(PSTR("QK_SEQUENCER+"), keycode - QK_SEQUENCER);
+ return;
+#endif // SEQUENCER_ENABLE
+#ifdef AUDIO_ENABLE
+ case AUDIO_KEYCODE_RANGE:
+ append_numbered_keycode(PSTR("QK_AUDIO+"), keycode - QK_AUDIO);
+ return;
+#endif // AUDIO_ENABLE
+#if defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(RGBLIGHT_ENABLED) || defined(RGB_MATRIX_ENABLE) // Lighting-related features.
+ case QK_LIGHTING ... QK_LIGHTING_MAX:
+ append_numbered_keycode(PSTR("QK_LIGHTING+"), keycode - QK_LIGHTING);
+ return;
+#endif // defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(RGBLIGHT_ENABLED) || defined(RGB_MATRIX_ENABLE)
+#ifdef STENO_ENABLE
+ case STENO_KEYCODE_RANGE:
+ append_numbered_keycode(PSTR("QK_STENO+"), keycode - QK_STENO);
+ return;
+#endif // AUDIO_ENABLE
+#ifdef BLUETOOTH_ENABLE
+ case CONNECTION_KEYCODE_RANGE:
+ append_numbered_keycode(PSTR("QK_CONNECTION+"), keycode - QK_CONNECTION);
+ return;
+#endif // BLUETOOTH_ENABLE
+ case QUANTUM_KEYCODE_RANGE:
+ append_numbered_keycode(PSTR("QK_QUANTUM+"), keycode - QK_QUANTUM);
+ return;
+ }
+ // clang-format on
+
+ append_number(keycode, 16); // Fallback: write keycode as hex value.
+}
+
+const char* get_keycode_string(uint16_t keycode) {
+ buffer_len = 0;
+ buffer[0] = '\0';
+ append_keycode(keycode);
+ return buffer;
+}
diff --git a/quantum/keycode_string.h b/quantum/keycode_string.h
new file mode 100644
index 0000000000..1315613a80
--- /dev/null
+++ b/quantum/keycode_string.h
@@ -0,0 +1,134 @@
+// Copyright 2024-2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <stdint.h>
+
+#if KEYCODE_STRING_ENABLE
+
+/**
+ * @brief Formats a QMK keycode as a human-readable string.
+ *
+ * Given a keycode, like `KC_A`, this function returns a formatted string, like
+ * "KC_A". This is useful for debugging and diagnostics so that keys are more
+ * easily identified than they would be by raw numerical codes.
+ *
+ * @note The returned char* string should be used right away. The string memory
+ * is reused and will be overwritten by the next call to `keycode_string()`.
+ *
+ * Many common QMK keycodes are understood by this function, but not all.
+ * Recognized keycodes include:
+ *
+ * - Most basic keycodes, including letters `KC_A` - `KC_Z`, digits `KC_0` -
+ * `KC_9`, function keys `KC_F1` - `KC_F24`, and modifiers like `KC_LSFT`.
+ *
+ * - Modified basic keycodes, like `S(KC_1)` (Shift + 1 = !).
+ *
+ * - `MO`, `TO`, `TG`, `OSL`, `LM(layer,mod)`, `LT(layer,kc)` layer switches.
+ *
+ * - One-shot mod `OSM(mod)` keycodes.
+ *
+ * - Mod-tap `MT(mod, kc)` keycodes.
+ *
+ * - Tap dance keycodes `TD(i)`.
+ *
+ * - Swap hands keycodes `SH_T(kc)`, `SH_TOGG`, etc.
+ *
+ * - Joystick keycodes `JS_n`.
+ *
+ * - Programmable button keycodes `PB_n`.
+ *
+ * - Unicode `UC(codepoint)` and Unicode Map `UM(i)` and `UP(i,j)` keycodes.
+ *
+ * - Keyboard range keycodes `QK_KB_*`.
+ *
+ * - User range (SAFE_RANGE) keycodes `QK_USER_*`.
+ *
+ * Keycodes involving mods like `OSM`, `LM`, `MT` are fully supported only where
+ * a single mod is applied.
+ *
+ * Unrecognized keycodes are printed numerically as hex values like `0x1ABC`.
+ *
+ * Optionally, use `keycode_string_names_user` or `keycode_string_names_kb` to
+ * define names for additional keycodes or override how any of the above are
+ * formatted.
+ *
+ * @param keycode QMK keycode.
+ * @return Stringified keycode.
+ */
+const char* get_keycode_string(uint16_t keycode);
+
+/** Defines a human-readable name for a keycode. */
+typedef struct {
+ uint16_t keycode;
+ const char* name;
+} keycode_string_name_t;
+
+// clang-format off
+/**
+ * @brief Defines names for additional keycodes for `get_keycode_string()`.
+ *
+ * Define `KEYCODE_STRING_NAMES_USER` in your keymap.c to add names for
+ * additional keycodes to `keycode_string()`. This table may also be used to
+ * override how `keycode_string()` formats a keycode. For example, supposing
+ * keymap.c defines `MYMACRO1` and `MYMACRO2` as custom keycodes:
+ *
+ * KEYCODE_STRING_NAMES_USER(
+ * KEYCODE_STRING_NAME(MYMACRO1),
+ * KEYCODE_STRING_NAME(MYMACRO2),
+ * KEYCODE_STRING_NAME(KC_EXLM),
+ * );
+ *
+ * The above defines names for `MYMACRO1` and `MYMACRO2`, and overrides
+ * `KC_EXLM` to format as "KC_EXLM" instead of the default "S(KC_1)".
+ */
+# define KEYCODE_STRING_NAMES_USER(...) \
+ static const keycode_string_name_t keycode_string_names_user[] = {__VA_ARGS__}; \
+ uint16_t keycode_string_names_size_user = \
+ sizeof(keycode_string_names_user) / sizeof(keycode_string_name_t); \
+ const keycode_string_name_t* keycode_string_names_data_user = \
+ keycode_string_names_user
+
+/** Same as above, but defines keycode string names at the keyboard level. */
+# define KEYCODE_STRING_NAMES_KB(...) \
+ static const keycode_string_name_t keycode_string_names_kb[] = {__VA_ARGS__}; \
+ uint16_t keycode_string_names_size_kb = \
+ sizeof(keycode_string_names_kb) / sizeof(keycode_string_name_t); \
+ const keycode_string_name_t* keycode_string_names_data_kb = \
+ keycode_string_names_kb
+
+/** Helper to define a keycode_string_name_t. */
+# define KEYCODE_STRING_NAME(kc) \
+ { (kc), #kc }
+// clang-format on
+
+extern const keycode_string_name_t* keycode_string_names_data_user;
+extern uint16_t keycode_string_names_size_user;
+extern const keycode_string_name_t* keycode_string_names_data_kb;
+extern uint16_t keycode_string_names_size_kb;
+
+#else
+
+// When keycode_string is disabled, fall back to printing keycodes numerically
+// as decimal values, using get_u16_str() from quantum.c.
+# define get_keycode_string(kc) get_u16_str(kc, ' ')
+
+const char* get_u16_str(uint16_t curr_num, char curr_pad);
+
+# define KEYCODE_STRING_NAMES_USER(...)
+# define KEYCODE_STRING_NAMES_KB(...)
+# define KEYCODE_STRING_NAME(kc)
+
+#endif // KEYCODE_STRING_ENABLE
diff --git a/quantum/keycodes.h b/quantum/keycodes.h
index b4fc38f5ff..6a59aa376d 100644
--- a/quantum/keycodes.h
+++ b/quantum/keycodes.h
@@ -26,6 +26,12 @@
#pragma once
// clang-format off
+#define QMK_KEYCODES_VERSION "0.0.7"
+#define QMK_KEYCODES_VERSION_BCD 0x00000007
+#define QMK_KEYCODES_VERSION_MAJOR 0
+#define QMK_KEYCODES_VERSION_MINOR 0
+#define QMK_KEYCODES_VERSION_PATCH 7
+
enum qk_keycode_ranges {
// Ranges
QK_BASIC = 0x0000,
diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c
index abdcd5c7ba..9be9bae943 100644
--- a/quantum/keymap_common.c
+++ b/quantum/keymap_common.c
@@ -156,7 +156,7 @@ action_t action_for_keycode(uint16_t keycode) {
case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
mod = mod_config(QK_LAYER_MOD_GET_MODS(keycode));
action_layer = QK_LAYER_MOD_GET_LAYER(keycode);
- action.code = ACTION_LAYER_MODS(action_layer, (mod & 0x10) ? mod << 4 : mod);
+ action.code = ACTION_LAYER_MODS(action_layer, (mod & 0x10) ? (mod & 0xF) << 4 : mod);
break;
#endif // NO_ACTION_LAYER
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
diff --git a/quantum/keymap_extras/keymap_belgian.h b/quantum/keymap_extras/keymap_belgian.h
index 1869e66d9a..b41ed9d20b 100644
--- a/quantum/keymap_extras/keymap_belgian.h
+++ b/quantum/keymap_extras/keymap_belgian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_BELGIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_BELGIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_BELGIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_BELGIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_BELGIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define BE_SUP2 KC_GRV // ²
#define BE_AMPR KC_1 // &
diff --git a/quantum/keymap_extras/keymap_bepo.h b/quantum/keymap_extras/keymap_bepo.h
index f2ddb99180..c0bb703ecc 100644
--- a/quantum/keymap_extras/keymap_bepo.h
+++ b/quantum/keymap_extras/keymap_bepo.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_BEPO_KEYCODES_VERSION "0.0.1"
+#define QMK_BEPO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_BEPO_KEYCODES_VERSION_MAJOR 0
+#define QMK_BEPO_KEYCODES_VERSION_MINOR 0
+#define QMK_BEPO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define BP_DLR KC_GRV // $
#define BP_DQUO KC_1 // "
diff --git a/quantum/keymap_extras/keymap_brazilian_abnt2.h b/quantum/keymap_extras/keymap_brazilian_abnt2.h
index 730fe5069f..267b5490c9 100644
--- a/quantum/keymap_extras/keymap_brazilian_abnt2.h
+++ b/quantum/keymap_extras/keymap_brazilian_abnt2.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION "0.0.1"
+#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_MAJOR 0
+#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_MINOR 0
+#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define BR_QUOT KC_GRV // '
#define BR_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_canadian_french.h b/quantum/keymap_extras/keymap_canadian_french.h
index 5771cf6193..df9c73c016 100644
--- a/quantum/keymap_extras/keymap_canadian_french.h
+++ b/quantum/keymap_extras/keymap_canadian_french.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION "0.0.1"
+#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_MAJOR 0
+#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_MINOR 0
+#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define FR_HASH KC_GRV // #
#define FR_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_canadian_multilingual.h b/quantum/keymap_extras/keymap_canadian_multilingual.h
index 2a4326b406..4b42457396 100644
--- a/quantum/keymap_extras/keymap_canadian_multilingual.h
+++ b/quantum/keymap_extras/keymap_canadian_multilingual.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION "0.0.1"
+#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_MAJOR 0
+#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_MINOR 0
+#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define CA_SLSH KC_GRV // /
#define CA_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_colemak.h b/quantum/keymap_extras/keymap_colemak.h
index 0170339aad..a4fc77e80f 100644
--- a/quantum/keymap_extras/keymap_colemak.h
+++ b/quantum/keymap_extras/keymap_colemak.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_COLEMAK_KEYCODES_VERSION "0.0.1"
+#define QMK_COLEMAK_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_COLEMAK_KEYCODES_VERSION_MAJOR 0
+#define QMK_COLEMAK_KEYCODES_VERSION_MINOR 0
+#define QMK_COLEMAK_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define CM_GRV KC_GRV // `
#define CM_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_croatian.h b/quantum/keymap_extras/keymap_croatian.h
index 7e11a85710..cdc032a9eb 100644
--- a/quantum/keymap_extras/keymap_croatian.h
+++ b/quantum/keymap_extras/keymap_croatian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_CROATIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_CROATIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_CROATIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_CROATIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_CROATIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define HR_CEDL KC_GRV // ¸ (dead)
#define HR_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_czech.h b/quantum/keymap_extras/keymap_czech.h
index 8e65a4ed0a..cae16cdb9f 100644
--- a/quantum/keymap_extras/keymap_czech.h
+++ b/quantum/keymap_extras/keymap_czech.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_CZECH_KEYCODES_VERSION "0.0.1"
+#define QMK_CZECH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_CZECH_KEYCODES_VERSION_MAJOR 0
+#define QMK_CZECH_KEYCODES_VERSION_MINOR 0
+#define QMK_CZECH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define CZ_SCLN KC_GRV // ;
#define CZ_PLUS KC_1 // +
diff --git a/quantum/keymap_extras/keymap_czech_mac_ansi.h b/quantum/keymap_extras/keymap_czech_mac_ansi.h
index ed46a78f1c..bdfda933b0 100644
--- a/quantum/keymap_extras/keymap_czech_mac_ansi.h
+++ b/quantum/keymap_extras/keymap_czech_mac_ansi.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION "0.0.1"
+#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_MAJOR 0
+#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_MINOR 0
+#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define CZ_BSLS KC_GRV // (backslash)
#define CZ_PLUS KC_1 // +
diff --git a/quantum/keymap_extras/keymap_czech_mac_iso.h b/quantum/keymap_extras/keymap_czech_mac_iso.h
index 08285b7952..9c05d8dae0 100644
--- a/quantum/keymap_extras/keymap_czech_mac_iso.h
+++ b/quantum/keymap_extras/keymap_czech_mac_iso.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION "0.0.1"
+#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_MAJOR 0
+#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_MINOR 0
+#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define CZ_PLUS KC_1 // +
#define CZ_ECAR KC_2 // ě
diff --git a/quantum/keymap_extras/keymap_danish.h b/quantum/keymap_extras/keymap_danish.h
index a84923007c..1f1ee90e23 100644
--- a/quantum/keymap_extras/keymap_danish.h
+++ b/quantum/keymap_extras/keymap_danish.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_DANISH_KEYCODES_VERSION "0.0.1"
+#define QMK_DANISH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_DANISH_KEYCODES_VERSION_MAJOR 0
+#define QMK_DANISH_KEYCODES_VERSION_MINOR 0
+#define QMK_DANISH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define DK_HALF KC_GRV // ½
#define DK_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_dvorak.h b/quantum/keymap_extras/keymap_dvorak.h
index a926e48295..5cb2c4564a 100644
--- a/quantum/keymap_extras/keymap_dvorak.h
+++ b/quantum/keymap_extras/keymap_dvorak.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_DVORAK_KEYCODES_VERSION "0.0.1"
+#define QMK_DVORAK_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_DVORAK_KEYCODES_VERSION_MAJOR 0
+#define QMK_DVORAK_KEYCODES_VERSION_MINOR 0
+#define QMK_DVORAK_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define DV_GRV KC_GRV // `
#define DV_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_dvorak_fr.h b/quantum/keymap_extras/keymap_dvorak_fr.h
index 6e6498e598..d01bf7fec7 100644
--- a/quantum/keymap_extras/keymap_dvorak_fr.h
+++ b/quantum/keymap_extras/keymap_dvorak_fr.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_DVORAK_FR_KEYCODES_VERSION "0.0.1"
+#define QMK_DVORAK_FR_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_DVORAK_FR_KEYCODES_VERSION_MAJOR 0
+#define QMK_DVORAK_FR_KEYCODES_VERSION_MINOR 0
+#define QMK_DVORAK_FR_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define DV_LDAQ KC_GRV // «
#define DV_RDAQ KC_1 // »
diff --git a/quantum/keymap_extras/keymap_dvorak_programmer.h b/quantum/keymap_extras/keymap_dvorak_programmer.h
index 43d9a702bb..f17900105f 100644
--- a/quantum/keymap_extras/keymap_dvorak_programmer.h
+++ b/quantum/keymap_extras/keymap_dvorak_programmer.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION "0.0.1"
+#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_MAJOR 0
+#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_MINOR 0
+#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define DP_DLR KC_GRV // $
#define DP_AMPR KC_1 // &
diff --git a/quantum/keymap_extras/keymap_estonian.h b/quantum/keymap_extras/keymap_estonian.h
index 3e87bbc5f8..5fbeedcbe9 100644
--- a/quantum/keymap_extras/keymap_estonian.h
+++ b/quantum/keymap_extras/keymap_estonian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_ESTONIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_ESTONIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_ESTONIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_ESTONIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_ESTONIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define EE_CARN KC_GRV // ˇ (dead)
#define EE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_eurkey.h b/quantum/keymap_extras/keymap_eurkey.h
index 20f7f58683..5a13d48163 100644
--- a/quantum/keymap_extras/keymap_eurkey.h
+++ b/quantum/keymap_extras/keymap_eurkey.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_EURKEY_KEYCODES_VERSION "0.0.1"
+#define QMK_EURKEY_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_EURKEY_KEYCODES_VERSION_MAJOR 0
+#define QMK_EURKEY_KEYCODES_VERSION_MINOR 0
+#define QMK_EURKEY_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define EU_GRV KC_GRV // `
#define EU_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_farsi.h b/quantum/keymap_extras/keymap_farsi.h
index 4f34b08d92..e115d9c1c0 100644
--- a/quantum/keymap_extras/keymap_farsi.h
+++ b/quantum/keymap_extras/keymap_farsi.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_FARSI_KEYCODES_VERSION "0.0.1"
+#define QMK_FARSI_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_FARSI_KEYCODES_VERSION_MAJOR 0
+#define QMK_FARSI_KEYCODES_VERSION_MINOR 0
+#define QMK_FARSI_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define FA_ZWJ KC_GRV // (zero-width joiner)
#define FA_1A KC_1 // ۱
diff --git a/quantum/keymap_extras/keymap_finnish.h b/quantum/keymap_extras/keymap_finnish.h
index 045f7295a0..cb21da9962 100644
--- a/quantum/keymap_extras/keymap_finnish.h
+++ b/quantum/keymap_extras/keymap_finnish.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_FINNISH_KEYCODES_VERSION "0.0.1"
+#define QMK_FINNISH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_FINNISH_KEYCODES_VERSION_MAJOR 0
+#define QMK_FINNISH_KEYCODES_VERSION_MINOR 0
+#define QMK_FINNISH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define FI_SECT KC_GRV // §
#define FI_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_french.h b/quantum/keymap_extras/keymap_french.h
index 9ff3694a73..d4352c6481 100644
--- a/quantum/keymap_extras/keymap_french.h
+++ b/quantum/keymap_extras/keymap_french.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_FRENCH_KEYCODES_VERSION "0.0.1"
+#define QMK_FRENCH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_FRENCH_KEYCODES_VERSION_MAJOR 0
+#define QMK_FRENCH_KEYCODES_VERSION_MINOR 0
+#define QMK_FRENCH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define FR_SUP2 KC_GRV // ²
#define FR_AMPR KC_1 // &
diff --git a/quantum/keymap_extras/keymap_french_afnor.h b/quantum/keymap_extras/keymap_french_afnor.h
index 07506e0f6e..8e6905cc01 100644
--- a/quantum/keymap_extras/keymap_french_afnor.h
+++ b/quantum/keymap_extras/keymap_french_afnor.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_FRENCH_AFNOR_KEYCODES_VERSION "0.0.1"
+#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_MAJOR 0
+#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_MINOR 0
+#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define FR_AT KC_GRV // @
#define FR_AGRV KC_1 // à
diff --git a/quantum/keymap_extras/keymap_french_mac_iso.h b/quantum/keymap_extras/keymap_french_mac_iso.h
index b036ca2009..ad9d280f2a 100644
--- a/quantum/keymap_extras/keymap_french_mac_iso.h
+++ b/quantum/keymap_extras/keymap_french_mac_iso.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION "0.0.1"
+#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_MAJOR 0
+#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_MINOR 0
+#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define FR_AT KC_GRV // @
#define FR_AMPR KC_1 // &
diff --git a/quantum/keymap_extras/keymap_german.h b/quantum/keymap_extras/keymap_german.h
index 7ac807934b..bc98daa2fd 100644
--- a/quantum/keymap_extras/keymap_german.h
+++ b/quantum/keymap_extras/keymap_german.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_GERMAN_KEYCODES_VERSION "0.0.1"
+#define QMK_GERMAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_GERMAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_GERMAN_KEYCODES_VERSION_MINOR 0
+#define QMK_GERMAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define DE_CIRC KC_GRV // ^ (dead)
#define DE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_german_mac_iso.h b/quantum/keymap_extras/keymap_german_mac_iso.h
index 56d077c36c..ba3143c570 100644
--- a/quantum/keymap_extras/keymap_german_mac_iso.h
+++ b/quantum/keymap_extras/keymap_german_mac_iso.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION "0.0.1"
+#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_MAJOR 0
+#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_MINOR 0
+#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define DE_CIRC KC_GRV // ^ (dead)
#define DE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_greek.h b/quantum/keymap_extras/keymap_greek.h
index 0d2e15dfd8..fb2f02a04f 100644
--- a/quantum/keymap_extras/keymap_greek.h
+++ b/quantum/keymap_extras/keymap_greek.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_GREEK_KEYCODES_VERSION "0.0.1"
+#define QMK_GREEK_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_GREEK_KEYCODES_VERSION_MAJOR 0
+#define QMK_GREEK_KEYCODES_VERSION_MINOR 0
+#define QMK_GREEK_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define GR_GRV KC_GRV // `
#define GR_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_hebrew.h b/quantum/keymap_extras/keymap_hebrew.h
index a927364737..5d1d4a29c6 100644
--- a/quantum/keymap_extras/keymap_hebrew.h
+++ b/quantum/keymap_extras/keymap_hebrew.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_HEBREW_KEYCODES_VERSION "0.0.1"
+#define QMK_HEBREW_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_HEBREW_KEYCODES_VERSION_MAJOR 0
+#define QMK_HEBREW_KEYCODES_VERSION_MINOR 0
+#define QMK_HEBREW_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define IL_SCLN KC_GRV // ;
#define IL_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_hungarian.h b/quantum/keymap_extras/keymap_hungarian.h
index 81a842a0fe..27236553e8 100644
--- a/quantum/keymap_extras/keymap_hungarian.h
+++ b/quantum/keymap_extras/keymap_hungarian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_HUNGARIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_HUNGARIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_HUNGARIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_HUNGARIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_HUNGARIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define HU_0 KC_GRV // 0
#define HU_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_icelandic.h b/quantum/keymap_extras/keymap_icelandic.h
index 8e4e04b0f1..409be27b77 100644
--- a/quantum/keymap_extras/keymap_icelandic.h
+++ b/quantum/keymap_extras/keymap_icelandic.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_ICELANDIC_KEYCODES_VERSION "0.0.1"
+#define QMK_ICELANDIC_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_ICELANDIC_KEYCODES_VERSION_MAJOR 0
+#define QMK_ICELANDIC_KEYCODES_VERSION_MINOR 0
+#define QMK_ICELANDIC_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define IS_RNGA KC_GRV // ° (dead)
#define IS_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_irish.h b/quantum/keymap_extras/keymap_irish.h
index 6cb85dc7a7..587467bcbe 100644
--- a/quantum/keymap_extras/keymap_irish.h
+++ b/quantum/keymap_extras/keymap_irish.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_IRISH_KEYCODES_VERSION "0.0.1"
+#define QMK_IRISH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_IRISH_KEYCODES_VERSION_MAJOR 0
+#define QMK_IRISH_KEYCODES_VERSION_MINOR 0
+#define QMK_IRISH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define IE_GRV KC_GRV // `
#define IE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_italian.h b/quantum/keymap_extras/keymap_italian.h
index 57c12ba9b6..9fd7f1b15c 100644
--- a/quantum/keymap_extras/keymap_italian.h
+++ b/quantum/keymap_extras/keymap_italian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_ITALIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_ITALIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_ITALIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_ITALIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_ITALIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define IT_BSLS KC_GRV // (backslash)
#define IT_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_italian_mac_ansi.h b/quantum/keymap_extras/keymap_italian_mac_ansi.h
index 4774c2d5ee..9ef38c0d87 100644
--- a/quantum/keymap_extras/keymap_italian_mac_ansi.h
+++ b/quantum/keymap_extras/keymap_italian_mac_ansi.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION "0.0.1"
+#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_MAJOR 0
+#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_MINOR 0
+#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define IT_LABK KC_GRV // <
#define IT_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_italian_mac_iso.h b/quantum/keymap_extras/keymap_italian_mac_iso.h
index 35ec978ef9..e80cfa450c 100644
--- a/quantum/keymap_extras/keymap_italian_mac_iso.h
+++ b/quantum/keymap_extras/keymap_italian_mac_iso.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION "0.0.1"
+#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_MAJOR 0
+#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_MINOR 0
+#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define IT_BSLS KC_GRV // (backslash)
#define IT_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_japanese.h b/quantum/keymap_extras/keymap_japanese.h
index 3ad9495353..55df86e16d 100644
--- a/quantum/keymap_extras/keymap_japanese.h
+++ b/quantum/keymap_extras/keymap_japanese.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_JAPANESE_KEYCODES_VERSION "0.0.1"
+#define QMK_JAPANESE_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_JAPANESE_KEYCODES_VERSION_MAJOR 0
+#define QMK_JAPANESE_KEYCODES_VERSION_MINOR 0
+#define QMK_JAPANESE_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define JP_ZKHK KC_GRV // Zenkaku ↔ Hankaku ↔ Kanji (半角 ↔ 全角 ↔ 漢字)
#define JP_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_korean.h b/quantum/keymap_extras/keymap_korean.h
index 644837734e..7bf64c4841 100644
--- a/quantum/keymap_extras/keymap_korean.h
+++ b/quantum/keymap_extras/keymap_korean.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_KOREAN_KEYCODES_VERSION "0.0.1"
+#define QMK_KOREAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_KOREAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_KOREAN_KEYCODES_VERSION_MINOR 0
+#define QMK_KOREAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define KR_GRV KC_GRV // `
#define KR_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_latvian.h b/quantum/keymap_extras/keymap_latvian.h
index b1750ed759..4d60c45163 100644
--- a/quantum/keymap_extras/keymap_latvian.h
+++ b/quantum/keymap_extras/keymap_latvian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_LATVIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_LATVIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_LATVIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_LATVIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_LATVIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define LV_GRV KC_GRV // `
#define LV_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_lithuanian_azerty.h b/quantum/keymap_extras/keymap_lithuanian_azerty.h
index 9d6c1b92f0..e88cc75e07 100644
--- a/quantum/keymap_extras/keymap_lithuanian_azerty.h
+++ b/quantum/keymap_extras/keymap_lithuanian_azerty.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION "0.0.1"
+#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_MAJOR 0
+#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_MINOR 0
+#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define LT_GRV KC_GRV // `
#define LT_EXLM KC_1 // !
diff --git a/quantum/keymap_extras/keymap_lithuanian_qwerty.h b/quantum/keymap_extras/keymap_lithuanian_qwerty.h
index 84df4c8bd2..3321615c1e 100644
--- a/quantum/keymap_extras/keymap_lithuanian_qwerty.h
+++ b/quantum/keymap_extras/keymap_lithuanian_qwerty.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION "0.0.1"
+#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_MAJOR 0
+#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_MINOR 0
+#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define LT_GRV KC_GRV // `
#define LT_AOGO KC_1 // Ą
diff --git a/quantum/keymap_extras/keymap_neo2.h b/quantum/keymap_extras/keymap_neo2.h
index ad285f7d18..5d10f19fd5 100644
--- a/quantum/keymap_extras/keymap_neo2.h
+++ b/quantum/keymap_extras/keymap_neo2.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_NEO2_KEYCODES_VERSION "0.0.1"
+#define QMK_NEO2_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_NEO2_KEYCODES_VERSION_MAJOR 0
+#define QMK_NEO2_KEYCODES_VERSION_MINOR 0
+#define QMK_NEO2_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define NE_CIRC KC_GRV // ^ (dead)
#define NE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_nordic.h b/quantum/keymap_extras/keymap_nordic.h
index ff952e6c43..3a11b29fc8 100644
--- a/quantum/keymap_extras/keymap_nordic.h
+++ b/quantum/keymap_extras/keymap_nordic.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_NORDIC_KEYCODES_VERSION "0.0.1"
+#define QMK_NORDIC_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_NORDIC_KEYCODES_VERSION_MAJOR 0
+#define QMK_NORDIC_KEYCODES_VERSION_MINOR 0
+#define QMK_NORDIC_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define NO_HALF KC_GRV
#define NO_PLUS KC_MINS
diff --git a/quantum/keymap_extras/keymap_norman.h b/quantum/keymap_extras/keymap_norman.h
index 166a2f22a1..d47c2ff8a7 100644
--- a/quantum/keymap_extras/keymap_norman.h
+++ b/quantum/keymap_extras/keymap_norman.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_NORMAN_KEYCODES_VERSION "0.0.1"
+#define QMK_NORMAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_NORMAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_NORMAN_KEYCODES_VERSION_MINOR 0
+#define QMK_NORMAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define NM_GRV KC_GRV // `
#define NM_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_norwegian.h b/quantum/keymap_extras/keymap_norwegian.h
index 0b0cf65813..021b8c3b9c 100644
--- a/quantum/keymap_extras/keymap_norwegian.h
+++ b/quantum/keymap_extras/keymap_norwegian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_NORWEGIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_NORWEGIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_NORWEGIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_NORWEGIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_NORWEGIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define NO_PIPE KC_GRV // |
#define NO_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_plover.h b/quantum/keymap_extras/keymap_plover.h
index a01a23c8a3..e7facfd623 100644
--- a/quantum/keymap_extras/keymap_plover.h
+++ b/quantum/keymap_extras/keymap_plover.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_PLOVER_KEYCODES_VERSION "0.0.1"
+#define QMK_PLOVER_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_PLOVER_KEYCODES_VERSION_MAJOR 0
+#define QMK_PLOVER_KEYCODES_VERSION_MINOR 0
+#define QMK_PLOVER_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define PV_NUM KC_1
#define PV_LS KC_Q
diff --git a/quantum/keymap_extras/keymap_plover_dvorak.h b/quantum/keymap_extras/keymap_plover_dvorak.h
index 0ec7b9acd3..5c8a4f9ada 100644
--- a/quantum/keymap_extras/keymap_plover_dvorak.h
+++ b/quantum/keymap_extras/keymap_plover_dvorak.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_PLOVER_DVORAK_KEYCODES_VERSION "0.0.1"
+#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_MAJOR 0
+#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_MINOR 0
+#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define PD_NUM DV_1
#define PD_LS DV_Q
diff --git a/quantum/keymap_extras/keymap_polish.h b/quantum/keymap_extras/keymap_polish.h
index 2448ef1fef..e5e48097f7 100644
--- a/quantum/keymap_extras/keymap_polish.h
+++ b/quantum/keymap_extras/keymap_polish.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_POLISH_KEYCODES_VERSION "0.0.1"
+#define QMK_POLISH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_POLISH_KEYCODES_VERSION_MAJOR 0
+#define QMK_POLISH_KEYCODES_VERSION_MINOR 0
+#define QMK_POLISH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define PL_GRV KC_GRV // `
#define PL_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_portuguese.h b/quantum/keymap_extras/keymap_portuguese.h
index 9f79862679..44abaf6537 100644
--- a/quantum/keymap_extras/keymap_portuguese.h
+++ b/quantum/keymap_extras/keymap_portuguese.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_PORTUGUESE_KEYCODES_VERSION "0.0.1"
+#define QMK_PORTUGUESE_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_PORTUGUESE_KEYCODES_VERSION_MAJOR 0
+#define QMK_PORTUGUESE_KEYCODES_VERSION_MINOR 0
+#define QMK_PORTUGUESE_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define PT_BSLS KC_GRV // (backslash)
#define PT_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_portuguese_mac_iso.h b/quantum/keymap_extras/keymap_portuguese_mac_iso.h
index 5381956b4c..f2d04440fb 100644
--- a/quantum/keymap_extras/keymap_portuguese_mac_iso.h
+++ b/quantum/keymap_extras/keymap_portuguese_mac_iso.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION "0.0.1"
+#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_MAJOR 0
+#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_MINOR 0
+#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define PT_SECT KC_GRV // §
#define PT_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_romanian.h b/quantum/keymap_extras/keymap_romanian.h
index 6410fbbe48..9a7239e032 100644
--- a/quantum/keymap_extras/keymap_romanian.h
+++ b/quantum/keymap_extras/keymap_romanian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_ROMANIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_ROMANIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_ROMANIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_ROMANIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_ROMANIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define RO_DLQU KC_GRV // „
#define RO_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_russian.h b/quantum/keymap_extras/keymap_russian.h
index 364e7aba5c..b756a657df 100644
--- a/quantum/keymap_extras/keymap_russian.h
+++ b/quantum/keymap_extras/keymap_russian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_RUSSIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_RUSSIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_RUSSIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_RUSSIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_RUSSIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define RU_YO KC_GRV // Ё
#define RU_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_russian_typewriter.h b/quantum/keymap_extras/keymap_russian_typewriter.h
index 18157726ad..45fb1ede04 100644
--- a/quantum/keymap_extras/keymap_russian_typewriter.h
+++ b/quantum/keymap_extras/keymap_russian_typewriter.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION "0.0.1"
+#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_MAJOR 0
+#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_MINOR 0
+#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define RU_PIPE KC_GRV // |
#define RU_NUM KC_1 // №
diff --git a/quantum/keymap_extras/keymap_serbian.h b/quantum/keymap_extras/keymap_serbian.h
index 6421577c22..202322b591 100644
--- a/quantum/keymap_extras/keymap_serbian.h
+++ b/quantum/keymap_extras/keymap_serbian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SERBIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_SERBIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SERBIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_SERBIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_SERBIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define RS_GRV KC_GRV // `
#define RS_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_serbian_latin.h b/quantum/keymap_extras/keymap_serbian_latin.h
index 358c6c76ed..e863aa4ed8 100644
--- a/quantum/keymap_extras/keymap_serbian_latin.h
+++ b/quantum/keymap_extras/keymap_serbian_latin.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SERBIAN_LATIN_KEYCODES_VERSION "0.0.1"
+#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_MAJOR 0
+#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_MINOR 0
+#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define RS_SLQU KC_GRV // ‚ (dead)
#define RS_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_slovak.h b/quantum/keymap_extras/keymap_slovak.h
index a777fce16f..f5848a6aaf 100644
--- a/quantum/keymap_extras/keymap_slovak.h
+++ b/quantum/keymap_extras/keymap_slovak.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SLOVAK_KEYCODES_VERSION "0.0.1"
+#define QMK_SLOVAK_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SLOVAK_KEYCODES_VERSION_MAJOR 0
+#define QMK_SLOVAK_KEYCODES_VERSION_MINOR 0
+#define QMK_SLOVAK_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define SK_SCLN KC_GRV // ;
#define SK_PLUS KC_1 // +
diff --git a/quantum/keymap_extras/keymap_slovenian.h b/quantum/keymap_extras/keymap_slovenian.h
index 402a53cd44..502f06ad64 100644
--- a/quantum/keymap_extras/keymap_slovenian.h
+++ b/quantum/keymap_extras/keymap_slovenian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SLOVENIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_SLOVENIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SLOVENIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_SLOVENIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_SLOVENIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define SI_CEDL KC_GRV // ¸ (dead)
#define SI_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_spanish.h b/quantum/keymap_extras/keymap_spanish.h
index 714c8cbb7c..dc23aea639 100644
--- a/quantum/keymap_extras/keymap_spanish.h
+++ b/quantum/keymap_extras/keymap_spanish.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SPANISH_KEYCODES_VERSION "0.0.1"
+#define QMK_SPANISH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SPANISH_KEYCODES_VERSION_MAJOR 0
+#define QMK_SPANISH_KEYCODES_VERSION_MINOR 0
+#define QMK_SPANISH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define ES_MORD KC_GRV // º
#define ES_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_spanish_dvorak.h b/quantum/keymap_extras/keymap_spanish_dvorak.h
index b5a6463452..e19a84b7e5 100644
--- a/quantum/keymap_extras/keymap_spanish_dvorak.h
+++ b/quantum/keymap_extras/keymap_spanish_dvorak.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SPANISH_DVORAK_KEYCODES_VERSION "0.0.1"
+#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_MAJOR 0
+#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_MINOR 0
+#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define DV_MORD KC_GRV // º
#define DV_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_spanish_latin_america.h b/quantum/keymap_extras/keymap_spanish_latin_america.h
index 651212d4bf..57329d20cd 100644
--- a/quantum/keymap_extras/keymap_spanish_latin_america.h
+++ b/quantum/keymap_extras/keymap_spanish_latin_america.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION "0.0.1"
+#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_MAJOR 0
+#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_MINOR 0
+#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define ES_PIPE KC_GRV // |
#define ES_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_swedish.h b/quantum/keymap_extras/keymap_swedish.h
index 23ec4102d8..a50f8af30d 100644
--- a/quantum/keymap_extras/keymap_swedish.h
+++ b/quantum/keymap_extras/keymap_swedish.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SWEDISH_KEYCODES_VERSION "0.0.1"
+#define QMK_SWEDISH_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SWEDISH_KEYCODES_VERSION_MAJOR 0
+#define QMK_SWEDISH_KEYCODES_VERSION_MINOR 0
+#define QMK_SWEDISH_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define SE_SECT KC_GRV // §
#define SE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_swedish_mac_ansi.h b/quantum/keymap_extras/keymap_swedish_mac_ansi.h
index 18061f0be9..642c161dab 100644
--- a/quantum/keymap_extras/keymap_swedish_mac_ansi.h
+++ b/quantum/keymap_extras/keymap_swedish_mac_ansi.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION "0.0.1"
+#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_MAJOR 0
+#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_MINOR 0
+#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define SE_LABK KC_GRV // <
#define SE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_swedish_mac_iso.h b/quantum/keymap_extras/keymap_swedish_mac_iso.h
index 0c68ffcbd4..50387364cf 100644
--- a/quantum/keymap_extras/keymap_swedish_mac_iso.h
+++ b/quantum/keymap_extras/keymap_swedish_mac_iso.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION "0.0.1"
+#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_MAJOR 0
+#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_MINOR 0
+#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define SE_SECT KC_GRV // §
#define SE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_swedish_pro_mac_ansi.h b/quantum/keymap_extras/keymap_swedish_pro_mac_ansi.h
index 043a7c9fc2..be1581bb3d 100644
--- a/quantum/keymap_extras/keymap_swedish_pro_mac_ansi.h
+++ b/quantum/keymap_extras/keymap_swedish_pro_mac_ansi.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION "0.0.1"
+#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_MAJOR 0
+#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_MINOR 0
+#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define SE_LABK KC_GRV // <
#define SE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_swedish_pro_mac_iso.h b/quantum/keymap_extras/keymap_swedish_pro_mac_iso.h
index 1d691feef1..d50213f987 100644
--- a/quantum/keymap_extras/keymap_swedish_pro_mac_iso.h
+++ b/quantum/keymap_extras/keymap_swedish_pro_mac_iso.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION "0.0.1"
+#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_MAJOR 0
+#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_MINOR 0
+#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define SE_SECT KC_GRV // §
#define SE_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_swiss_de.h b/quantum/keymap_extras/keymap_swiss_de.h
index 5411353a5f..4ebdb5c610 100644
--- a/quantum/keymap_extras/keymap_swiss_de.h
+++ b/quantum/keymap_extras/keymap_swiss_de.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SWISS_DE_KEYCODES_VERSION "0.0.1"
+#define QMK_SWISS_DE_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SWISS_DE_KEYCODES_VERSION_MAJOR 0
+#define QMK_SWISS_DE_KEYCODES_VERSION_MINOR 0
+#define QMK_SWISS_DE_KEYCODES_VERSION_PATCH 1
+
#undef CH_H
// Aliases
diff --git a/quantum/keymap_extras/keymap_swiss_fr.h b/quantum/keymap_extras/keymap_swiss_fr.h
index a9a9cab162..11fcc6536d 100644
--- a/quantum/keymap_extras/keymap_swiss_fr.h
+++ b/quantum/keymap_extras/keymap_swiss_fr.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_SWISS_FR_KEYCODES_VERSION "0.0.1"
+#define QMK_SWISS_FR_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_SWISS_FR_KEYCODES_VERSION_MAJOR 0
+#define QMK_SWISS_FR_KEYCODES_VERSION_MINOR 0
+#define QMK_SWISS_FR_KEYCODES_VERSION_PATCH 1
+
#undef CH_H
// Aliases
diff --git a/quantum/keymap_extras/keymap_turkish_f.h b/quantum/keymap_extras/keymap_turkish_f.h
index dcf9e815d7..bbdaa3af25 100644
--- a/quantum/keymap_extras/keymap_turkish_f.h
+++ b/quantum/keymap_extras/keymap_turkish_f.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_TURKISH_F_KEYCODES_VERSION "0.0.1"
+#define QMK_TURKISH_F_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_TURKISH_F_KEYCODES_VERSION_MAJOR 0
+#define QMK_TURKISH_F_KEYCODES_VERSION_MINOR 0
+#define QMK_TURKISH_F_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define TR_PLUS KC_GRV // +
#define TR_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_turkish_q.h b/quantum/keymap_extras/keymap_turkish_q.h
index a86af180cd..d3fa00d7ae 100644
--- a/quantum/keymap_extras/keymap_turkish_q.h
+++ b/quantum/keymap_extras/keymap_turkish_q.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_TURKISH_Q_KEYCODES_VERSION "0.0.1"
+#define QMK_TURKISH_Q_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_TURKISH_Q_KEYCODES_VERSION_MAJOR 0
+#define QMK_TURKISH_Q_KEYCODES_VERSION_MINOR 0
+#define QMK_TURKISH_Q_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define TR_DQUO KC_GRV // "
#define TR_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_uk.h b/quantum/keymap_extras/keymap_uk.h
index 30f9b972db..7490af6cf6 100644
--- a/quantum/keymap_extras/keymap_uk.h
+++ b/quantum/keymap_extras/keymap_uk.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_UK_KEYCODES_VERSION "0.0.1"
+#define QMK_UK_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_UK_KEYCODES_VERSION_MAJOR 0
+#define QMK_UK_KEYCODES_VERSION_MINOR 0
+#define QMK_UK_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define UK_GRV KC_GRV // `
#define UK_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_ukrainian.h b/quantum/keymap_extras/keymap_ukrainian.h
index d3f82a2194..78e39f8c75 100644
--- a/quantum/keymap_extras/keymap_ukrainian.h
+++ b/quantum/keymap_extras/keymap_ukrainian.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_UKRAINIAN_KEYCODES_VERSION "0.0.1"
+#define QMK_UKRAINIAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_UKRAINIAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_UKRAINIAN_KEYCODES_VERSION_MINOR 0
+#define QMK_UKRAINIAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define UA_QUOT KC_GRV // '
#define UA_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_us.h b/quantum/keymap_extras/keymap_us.h
index 74c9b8e7f8..b6e380ab23 100644
--- a/quantum/keymap_extras/keymap_us.h
+++ b/quantum/keymap_extras/keymap_us.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_US_KEYCODES_VERSION "0.0.1"
+#define QMK_US_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_US_KEYCODES_VERSION_MAJOR 0
+#define QMK_US_KEYCODES_VERSION_MINOR 0
+#define QMK_US_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define KC_TILD S(KC_GRAVE) // ~
#define KC_EXLM S(KC_1) // !
diff --git a/quantum/keymap_extras/keymap_us_extended.h b/quantum/keymap_extras/keymap_us_extended.h
index d17d3e603e..4262a7c44e 100644
--- a/quantum/keymap_extras/keymap_us_extended.h
+++ b/quantum/keymap_extras/keymap_us_extended.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_US_EXTENDED_KEYCODES_VERSION "0.0.1"
+#define QMK_US_EXTENDED_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_US_EXTENDED_KEYCODES_VERSION_MAJOR 0
+#define QMK_US_EXTENDED_KEYCODES_VERSION_MINOR 0
+#define QMK_US_EXTENDED_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define US_GRV KC_GRV // `
#define US_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_us_international.h b/quantum/keymap_extras/keymap_us_international.h
index a9617494a8..adc7051fe4 100644
--- a/quantum/keymap_extras/keymap_us_international.h
+++ b/quantum/keymap_extras/keymap_us_international.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_US_INTERNATIONAL_KEYCODES_VERSION "0.0.1"
+#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_MAJOR 0
+#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_MINOR 0
+#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define US_DGRV KC_GRV // ` (dead)
#define US_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_us_international_linux.h b/quantum/keymap_extras/keymap_us_international_linux.h
index b13039be05..db315968d6 100644
--- a/quantum/keymap_extras/keymap_us_international_linux.h
+++ b/quantum/keymap_extras/keymap_us_international_linux.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION "0.0.1"
+#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_MAJOR 0
+#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_MINOR 0
+#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define US_DGRV KC_GRV // ` (dead)
#define US_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_workman.h b/quantum/keymap_extras/keymap_workman.h
index 29396fdec1..727e44512c 100644
--- a/quantum/keymap_extras/keymap_workman.h
+++ b/quantum/keymap_extras/keymap_workman.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_WORKMAN_KEYCODES_VERSION "0.0.1"
+#define QMK_WORKMAN_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_WORKMAN_KEYCODES_VERSION_MAJOR 0
+#define QMK_WORKMAN_KEYCODES_VERSION_MINOR 0
+#define QMK_WORKMAN_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define WK_GRV KC_GRV // `
#define WK_1 KC_1 // 1
diff --git a/quantum/keymap_extras/keymap_workman_zxcvm.h b/quantum/keymap_extras/keymap_workman_zxcvm.h
index f7a5689f0f..4655af60d6 100644
--- a/quantum/keymap_extras/keymap_workman_zxcvm.h
+++ b/quantum/keymap_extras/keymap_workman_zxcvm.h
@@ -27,6 +27,12 @@
#include "keycodes.h"
// clang-format off
+#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION "0.0.1"
+#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_BCD 0x00000001
+#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_MAJOR 0
+#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_MINOR 0
+#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_PATCH 1
+
// Aliases
#define WK_GRV KC_GRV // `
#define WK_1 KC_1 // 1
diff --git a/quantum/keymap_introspection.c b/quantum/keymap_introspection.c
index 23e842353a..99fd3f929e 100644
--- a/quantum/keymap_introspection.c
+++ b/quantum/keymap_introspection.c
@@ -13,6 +13,7 @@
# include INTROSPECTION_KEYMAP_C
#endif // INTROSPECTION_KEYMAP_C
+#include "compiler_support.h"
#include "keymap_introspection.h"
#include "util.h"
@@ -30,9 +31,9 @@ __attribute__((weak)) uint8_t keymap_layer_count(void) {
}
#ifdef DYNAMIC_KEYMAP_ENABLE
-_Static_assert(NUM_KEYMAP_LAYERS_RAW <= MAX_LAYER, "Number of keymap layers exceeds maximum set by DYNAMIC_KEYMAP_LAYER_COUNT");
+STATIC_ASSERT(NUM_KEYMAP_LAYERS_RAW <= MAX_LAYER, "Number of keymap layers exceeds maximum set by DYNAMIC_KEYMAP_LAYER_COUNT");
#else
-_Static_assert(NUM_KEYMAP_LAYERS_RAW <= MAX_LAYER, "Number of keymap layers exceeds maximum set by LAYER_STATE_(8|16|32)BIT");
+STATIC_ASSERT(NUM_KEYMAP_LAYERS_RAW <= MAX_LAYER, "Number of keymap layers exceeds maximum set by LAYER_STATE_(8|16|32)BIT");
#endif
uint16_t keycode_at_keymap_location_raw(uint8_t layer_num, uint8_t row, uint8_t column) {
@@ -61,7 +62,7 @@ __attribute__((weak)) uint8_t encodermap_layer_count(void) {
return encodermap_layer_count_raw();
}
-_Static_assert(NUM_KEYMAP_LAYERS_RAW == NUM_ENCODERMAP_LAYERS_RAW, "Number of encoder_map layers doesn't match the number of keymap layers");
+STATIC_ASSERT(NUM_KEYMAP_LAYERS_RAW == NUM_ENCODERMAP_LAYERS_RAW, "Number of encoder_map layers doesn't match the number of keymap layers");
uint16_t keycode_at_encodermap_location_raw(uint8_t layer_num, uint8_t encoder_idx, bool clockwise) {
if (layer_num < NUM_ENCODERMAP_LAYERS_RAW && encoder_idx < NUM_ENCODERS) {
@@ -106,7 +107,7 @@ __attribute__((weak)) uint16_t combo_count(void) {
return combo_count_raw();
}
-_Static_assert(ARRAY_SIZE(key_combos) <= (QK_KB), "Number of combos is abnormally high. Are you using SAFE_RANGE in an enum for combos?");
+STATIC_ASSERT(ARRAY_SIZE(key_combos) <= (QK_KB), "Number of combos is abnormally high. Are you using SAFE_RANGE in an enum for combos?");
combo_t* combo_get_raw(uint16_t combo_idx) {
if (combo_idx >= combo_count_raw()) {
@@ -133,7 +134,7 @@ __attribute__((weak)) uint16_t tap_dance_count(void) {
return tap_dance_count_raw();
}
-_Static_assert(ARRAY_SIZE(tap_dance_actions) <= (QK_TAP_DANCE_MAX - QK_TAP_DANCE), "Number of tap dance actions exceeds maximum. Are you using SAFE_RANGE in tap dance enum?");
+STATIC_ASSERT(ARRAY_SIZE(tap_dance_actions) <= (QK_TAP_DANCE_MAX - QK_TAP_DANCE), "Number of tap dance actions exceeds maximum. Are you using SAFE_RANGE in tap dance enum?");
tap_dance_action_t* tap_dance_get_raw(uint16_t tap_dance_idx) {
if (tap_dance_idx >= tap_dance_count_raw()) {
@@ -161,7 +162,7 @@ __attribute__((weak)) uint16_t key_override_count(void) {
return key_override_count_raw();
}
-_Static_assert(ARRAY_SIZE(key_overrides) <= (QK_KB), "Number of key overrides is abnormally high. Are you using SAFE_RANGE in an enum for key overrides?");
+STATIC_ASSERT(ARRAY_SIZE(key_overrides) <= (QK_KB), "Number of key overrides is abnormally high. Are you using SAFE_RANGE in an enum for key overrides?");
const key_override_t* key_override_get_raw(uint16_t key_override_idx) {
if (key_override_idx >= key_override_count_raw()) {
diff --git a/quantum/led_matrix/led_matrix.c b/quantum/led_matrix/led_matrix.c
index 58263c62e3..9c8004cc17 100644
--- a/quantum/led_matrix/led_matrix.c
+++ b/quantum/led_matrix/led_matrix.c
@@ -19,7 +19,6 @@
#include "led_matrix.h"
#include "progmem.h"
-#include "eeprom.h"
#include "eeconfig.h"
#include "keyboard.h"
#include "sync_timer.h"
@@ -46,6 +45,9 @@ const led_point_t k_led_matrix_center = LED_MATRIX_CENTER;
#define LED_MATRIX_CUSTOM_EFFECT_IMPLS
#include "led_matrix_effects.inc"
+#ifdef COMMUNITY_MODULES_ENABLE
+# include "led_matrix_community_modules.inc"
+#endif
#ifdef LED_MATRIX_CUSTOM_KB
# include "led_matrix_kb.inc"
#endif
@@ -86,9 +88,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);
}
@@ -105,7 +107,11 @@ void eeconfig_update_led_matrix_default(void) {
void eeconfig_debug_led_matrix(void) {
dprintf("led_matrix_eeconfig EEPROM\n");
dprintf("led_matrix_eeconfig.enable = %d\n", led_matrix_eeconfig.enable);
+#ifdef LED_MATRIX_MODE_NAME_ENABLE
+ dprintf("led_matrix_eeconfig.mode = %d (%s)\n", led_matrix_eeconfig.mode, led_matrix_get_mode_name(led_matrix_eeconfig.mode));
+#else
dprintf("led_matrix_eeconfig.mode = %d\n", led_matrix_eeconfig.mode);
+#endif // LED_MATRIX_MODE_NAME_ENABLE
dprintf("led_matrix_eeconfig.val = %d\n", led_matrix_eeconfig.val);
dprintf("led_matrix_eeconfig.speed = %d\n", led_matrix_eeconfig.speed);
dprintf("led_matrix_eeconfig.flags = %d\n", led_matrix_eeconfig.flags);
@@ -283,6 +289,15 @@ static void led_task_render(uint8_t effect) {
#include "led_matrix_effects.inc"
#undef LED_MATRIX_EFFECT
+#ifdef COMMUNITY_MODULES_ENABLE
+# define LED_MATRIX_EFFECT(name, ...) \
+ case LED_MATRIX_COMMUNITY_MODULE_##name: \
+ rendering = name(&led_effect_params); \
+ break;
+# include "led_matrix_community_modules.inc"
+# undef LED_MATRIX_EFFECT
+#endif
+
#if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)
# define LED_MATRIX_EFFECT(name, ...) \
case LED_MATRIX_CUSTOM_##name: \
@@ -359,7 +374,12 @@ void led_matrix_task(void) {
}
}
+__attribute__((weak)) bool led_matrix_indicators_modules(void) {
+ return true;
+}
+
void led_matrix_indicators(void) {
+ led_matrix_indicators_modules();
led_matrix_indicators_kb();
}
@@ -371,6 +391,10 @@ __attribute__((weak)) bool led_matrix_indicators_user(void) {
return true;
}
+__attribute__((weak)) bool led_matrix_indicators_advanced_modules(uint8_t led_min, uint8_t led_max) {
+ return true;
+}
+
void led_matrix_indicators_advanced(effect_params_t *params) {
/* special handling is needed for "params->iter", since it's already been incremented.
* Could move the invocations to led_task_render, but then it's missing a few checks
@@ -378,6 +402,7 @@ void led_matrix_indicators_advanced(effect_params_t *params) {
* led_task_render, right before the iter++ line.
*/
LED_MATRIX_USE_LIMITS_ITER(min, max, params->iter - 1);
+ led_matrix_indicators_advanced_modules(min, max);
led_matrix_indicators_advanced_kb(min, max);
}
@@ -504,7 +529,11 @@ void led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
}
led_task_state = STARTING;
eeconfig_flag_led_matrix(write_to_eeprom);
- dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", led_matrix_eeconfig.mode);
+#ifdef LED_MATRIX_MODE_NAME_ENABLE
+ dprintf("led matrix mode [%s]: %u (%s)\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", (unsigned)led_matrix_eeconfig.mode, led_matrix_get_mode_name(led_matrix_eeconfig.mode));
+#else
+ dprintf("led matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", (unsigned)led_matrix_eeconfig.mode);
+#endif // LED_MATRIX_MODE_NAME_ENABLE
}
void led_matrix_mode_noeeprom(uint8_t mode) {
led_matrix_mode_eeprom_helper(mode, false);
@@ -631,3 +660,48 @@ void led_matrix_set_flags(led_flags_t flags) {
void led_matrix_set_flags_noeeprom(led_flags_t flags) {
led_matrix_set_flags_eeprom_helper(flags, false);
}
+
+// LED Matrix naming
+#undef LED_MATRIX_EFFECT
+#ifdef LED_MATRIX_MODE_NAME_ENABLE
+const char *led_matrix_get_mode_name(uint8_t mode) {
+ switch (mode) {
+ case LED_MATRIX_NONE:
+ return "NONE";
+
+# define LED_MATRIX_EFFECT(name, ...) \
+ case LED_MATRIX_##name: \
+ return #name;
+# include "led_matrix_effects.inc"
+# undef LED_MATRIX_EFFECT
+
+# ifdef COMMUNITY_MODULES_ENABLE
+# define LED_MATRIX_EFFECT(name, ...) \
+ case LED_MATRIX_COMMUNITY_MODULE_##name: \
+ return #name;
+# include "led_matrix_community_modules.inc"
+# undef LED_MATRIX_EFFECT
+# endif // COMMUNITY_MODULES_ENABLE
+
+# if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)
+# define LED_MATRIX_EFFECT(name, ...) \
+ case LED_MATRIX_CUSTOM_##name: \
+ return #name;
+
+# ifdef LED_MATRIX_CUSTOM_KB
+# include "led_matrix_kb.inc"
+# endif // LED_MATRIX_CUSTOM_KB
+
+# ifdef LED_MATRIX_CUSTOM_USER
+# include "led_matrix_user.inc"
+# endif // LED_MATRIX_CUSTOM_USER
+
+# undef LED_MATRIX_EFFECT
+# endif // LED_MATRIX_CUSTOM_KB || LED_MATRIX_CUSTOM_USER
+
+ default:
+ return "UNKNOWN";
+ }
+}
+# undef LED_MATRIX_EFFECT
+#endif // LED_MATRIX_MODE_NAME_ENABLE
diff --git a/quantum/led_matrix/led_matrix.h b/quantum/led_matrix/led_matrix.h
index a3468a2003..9a49515ab2 100644
--- a/quantum/led_matrix/led_matrix.h
+++ b/quantum/led_matrix/led_matrix.h
@@ -98,6 +98,12 @@ enum led_matrix_effects {
#include "led_matrix_effects.inc"
#undef LED_MATRIX_EFFECT
+#ifdef COMMUNITY_MODULES_ENABLE
+# define LED_MATRIX_EFFECT(name, ...) LED_MATRIX_COMMUNITY_MODULE_##name,
+# include "led_matrix_community_modules.inc"
+# undef LED_MATRIX_EFFECT
+#endif
+
#if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)
# define LED_MATRIX_EFFECT(name, ...) LED_MATRIX_CUSTOM_##name,
# ifdef LED_MATRIX_CUSTOM_KB
@@ -115,7 +121,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);
@@ -178,6 +184,10 @@ led_flags_t led_matrix_get_flags(void);
void led_matrix_set_flags(led_flags_t flags);
void led_matrix_set_flags_noeeprom(led_flags_t flags);
+#ifdef LED_MATRIX_MODE_NAME_ENABLE
+const char *led_matrix_get_mode_name(uint8_t mode);
+#endif // LED_MATRIX_MODE_NAME_ENABLE
+
static inline bool led_matrix_check_finished_leds(uint8_t led_idx) {
#if defined(LED_MATRIX_SPLIT)
if (is_keyboard_left()) {
diff --git a/quantum/led_matrix/led_matrix_types.h b/quantum/led_matrix/led_matrix_types.h
index 5a516ceb10..26a199701e 100644
--- a/quantum/led_matrix/led_matrix_types.h
+++ b/quantum/led_matrix/led_matrix_types.h
@@ -18,6 +18,8 @@
#include <stdint.h>
#include <stdbool.h>
+
+#include "compiler_support.h"
#include "util.h"
#if defined(LED_MATRIX_KEYPRESSES) || defined(LED_MATRIX_KEYRELEASES)
@@ -71,7 +73,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;
@@ -82,4 +84,4 @@ typedef union {
};
} led_eeconfig_t;
-_Static_assert(sizeof(led_eeconfig_t) == sizeof(uint32_t), "LED Matrix EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(led_eeconfig_t) == sizeof(uint32_t), "LED Matrix EECONFIG out of spec.");
diff --git a/quantum/logging/debug.h b/quantum/logging/debug.h
index b0d9b9a10e..6675680ec7 100644
--- a/quantum/logging/debug.h
+++ b/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;
diff --git a/quantum/nvm/eeprom/nvm_dynamic_keymap.c b/quantum/nvm/eeprom/nvm_dynamic_keymap.c
new file mode 100644
index 0000000000..5ff49795b2
--- /dev/null
+++ b/quantum/nvm/eeprom/nvm_dynamic_keymap.c
@@ -0,0 +1,194 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "compiler_support.h"
+#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((int64_t)(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (int64_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) >= 100, "Dynamic keymaps are configured to use more EEPROM than is available.");
+
+#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;
+ }
+}
diff --git a/quantum/nvm/eeprom/nvm_eeconfig.c b/quantum/nvm/eeprom/nvm_eeconfig.c
new file mode 100644
index 0000000000..d9495d2753
--- /dev/null
+++ b/quantum/nvm/eeprom/nvm_eeconfig.c
@@ -0,0 +1,305 @@
+// 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
+
+#ifdef CONNECTION_ENABLE
+# include "connection.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
+
+#ifdef CONNECTION_ENABLE
+void nvm_eeconfig_read_connection(connection_config_t *config) {
+ config->raw = eeprom_read_byte(EECONFIG_CONNECTION);
+}
+void nvm_eeconfig_update_connection(const connection_config_t *config) {
+ eeprom_update_byte(EECONFIG_CONNECTION, config->raw);
+}
+#endif // CONNECTION_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
diff --git a/quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h b/quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h
new file mode 100644
index 0000000000..78b8190eaf
--- /dev/null
+++ b/quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h
@@ -0,0 +1,63 @@
+// Copyright 2024 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h> // offsetof
+
+#include "compiler_support.h"
+#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;
+ uint8_t connection;
+} 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))
+#define EECONFIG_CONNECTION (uint8_t *)(offsetof(eeprom_core_t, connection))
+
+// 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");
diff --git a/quantum/nvm/eeprom/nvm_eeprom_via_internal.h b/quantum/nvm/eeprom/nvm_eeprom_via_internal.h
new file mode 100644
index 0000000000..bf0d38e2ac
--- /dev/null
+++ b/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)
diff --git a/quantum/nvm/eeprom/nvm_via.c b/quantum/nvm/eeprom/nvm_via.c
new file mode 100644
index 0000000000..5372791fb7
--- /dev/null
+++ b/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
+}
diff --git a/quantum/nvm/nvm_dynamic_keymap.h b/quantum/nvm/nvm_dynamic_keymap.h
new file mode 100644
index 0000000000..d6e4aaee51
--- /dev/null
+++ b/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);
diff --git a/quantum/nvm/nvm_eeconfig.h b/quantum/nvm/nvm_eeconfig.h
new file mode 100644
index 0000000000..40827361ca
--- /dev/null
+++ b/quantum/nvm/nvm_eeconfig.h
@@ -0,0 +1,111 @@
+// 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
+
+#ifdef CONNECTION_ENABLE
+typedef union connection_config_t connection_config_t;
+void nvm_eeconfig_read_connection(connection_config_t *config);
+void nvm_eeconfig_update_connection(const connection_config_t *config);
+#endif // CONNECTION_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
diff --git a/quantum/nvm/nvm_via.h b/quantum/nvm/nvm_via.h
new file mode 100644
index 0000000000..90c5e67421
--- /dev/null
+++ b/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);
diff --git a/quantum/nvm/readme.md b/quantum/nvm/readme.md
new file mode 100644
index 0000000000..0731695e92
--- /dev/null
+++ b/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. \ No newline at end of file
diff --git a/quantum/nvm/rules.mk b/quantum/nvm/rules.mk
new file mode 100644
index 0000000000..c9c1fabfd4
--- /dev/null
+++ b/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
diff --git a/quantum/os_detection.c b/quantum/os_detection.c
index 9a9f9052f2..552375f61c 100644
--- a/quantum/os_detection.c
+++ b/quantum/os_detection.c
@@ -68,6 +68,15 @@ static volatile bool first_report = true;
static volatile struct usb_device_state current_usb_device_state = {.configure_state = USB_DEVICE_STATE_NO_INIT};
static volatile struct usb_device_state maxprev_usb_device_state = {.configure_state = USB_DEVICE_STATE_NO_INIT};
+// to reset the keyboard on USB state change
+#ifdef OS_DETECTION_KEYBOARD_RESET
+# ifndef OS_DETECTION_RESET_DEBOUNCE
+# define OS_DETECTION_RESET_DEBOUNCE OS_DETECTION_DEBOUNCE
+# endif
+static volatile fast_timer_t configured_since = 0;
+static volatile bool reset_pending = false;
+#endif
+
// the OS detection might be unstable for a while, "debounce" it
static volatile bool debouncing = false;
static volatile fast_timer_t last_time = 0;
@@ -77,7 +86,10 @@ bool process_detected_host_os_modules(os_variant_t os);
void os_detection_task(void) {
#ifdef OS_DETECTION_KEYBOARD_RESET
// resetting the keyboard on the USB device state change callback results in instability, so delegate that to this task
- // only take action if it's been stable at least once, to avoid issues with some KVMs
+ if (reset_pending) {
+ soft_reset_keyboard();
+ }
+ // reset the keyboard if it is stuck in the init state for longer than debounce duration, which can happen with some KVMs
if (current_usb_device_state.configure_state <= USB_DEVICE_STATE_INIT && maxprev_usb_device_state.configure_state >= USB_DEVICE_STATE_CONFIGURED) {
if (debouncing && timer_elapsed_fast(last_time) >= OS_DETECTION_DEBOUNCE) {
soft_reset_keyboard();
@@ -187,6 +199,18 @@ void os_detection_notify_usb_device_state_change(struct usb_device_state usb_dev
current_usb_device_state = usb_device_state;
last_time = timer_read_fast();
debouncing = true;
+
+#ifdef OS_DETECTION_KEYBOARD_RESET
+ if (configured_since == 0 && current_usb_device_state.configure_state == USB_DEVICE_STATE_CONFIGURED) {
+ configured_since = timer_read_fast();
+ } else if (current_usb_device_state.configure_state == USB_DEVICE_STATE_INIT) {
+ // reset the keyboard only if it's been stable for at least debounce duration, to avoid issues with some KVMs
+ if (configured_since > 0 && timer_elapsed_fast(configured_since) >= OS_DETECTION_RESET_DEBOUNCE) {
+ reset_pending = true;
+ }
+ configured_since = 0;
+ }
+#endif
}
#if defined(SPLIT_KEYBOARD) && defined(SPLIT_DETECTED_OS_ENABLE)
diff --git a/quantum/os_detection.h b/quantum/os_detection.h
index 98a8e805e4..e9e7b25f1d 100644
--- a/quantum/os_detection.h
+++ b/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
diff --git a/quantum/painter/qff.h b/quantum/painter/qff.h
index c3b831da17..ed88508d73 100644
--- a/quantum/painter/qff.h
+++ b/quantum/painter/qff.h
@@ -9,6 +9,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include "compiler_support.h"
#include "qp_stream.h"
#include "qp_internal.h"
#include "qgf.h"
@@ -36,7 +37,7 @@ typedef struct QP_PACKED qff_font_descriptor_v1_t {
uint8_t transparency_index; // palette index used for transparent pixels (not yet implemented)
} qff_font_descriptor_v1_t;
-_Static_assert(sizeof(qff_font_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 20), "qff_font_descriptor_v1_t must be 25 bytes in v1 of QFF");
+STATIC_ASSERT(sizeof(qff_font_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 20), "qff_font_descriptor_v1_t must be 25 bytes in v1 of QFF");
#define QFF_MAGIC 0x464651
@@ -54,14 +55,14 @@ typedef struct QP_PACKED qff_ascii_glyph_v1_t {
uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
} qff_ascii_glyph_v1_t;
-_Static_assert(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF");
+STATIC_ASSERT(sizeof(qff_ascii_glyph_v1_t) == 3, "qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF");
typedef struct QP_PACKED qff_ascii_glyph_table_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = 285 }
qff_ascii_glyph_v1_t glyph[95]; // 95 glyphs, 0x20..0x7E
} qff_ascii_glyph_table_v1_t;
-_Static_assert(sizeof(qff_ascii_glyph_table_v1_t) == (sizeof(qgf_block_header_v1_t) + (95 * sizeof(qff_ascii_glyph_v1_t))), "qff_ascii_glyph_table_v1_t must be 290 bytes in v1 of QFF");
+STATIC_ASSERT(sizeof(qff_ascii_glyph_table_v1_t) == (sizeof(qgf_block_header_v1_t) + (95 * sizeof(qff_ascii_glyph_v1_t))), "qff_ascii_glyph_table_v1_t must be 290 bytes in v1 of QFF");
/////////////////////////////////////////
// Unicode glyph table descriptor
@@ -73,7 +74,7 @@ typedef struct QP_PACKED qff_unicode_glyph_v1_t {
uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined
} qff_unicode_glyph_v1_t;
-_Static_assert(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF");
+STATIC_ASSERT(sizeof(qff_unicode_glyph_v1_t) == 6, "qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF");
typedef struct QP_PACKED qff_unicode_glyph_table_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = (N * 6) }
diff --git a/quantum/painter/qgf.h b/quantum/painter/qgf.h
index 33a37709e6..a1e245f15d 100644
--- a/quantum/painter/qgf.h
+++ b/quantum/painter/qgf.h
@@ -9,6 +9,7 @@
#include <stdint.h>
#include <stdbool.h>
+#include "compiler_support.h"
#include "qp_stream.h"
#include "qp_internal.h"
@@ -24,7 +25,7 @@ typedef struct QP_PACKED qgf_block_header_v1_t {
uint32_t length : 24; // 24-bit blob length, allowing for block sizes of a maximum of 16MB.
} qgf_block_header_v1_t;
-_Static_assert(sizeof(qgf_block_header_v1_t) == 5, "qgf_block_header_v1_t must be 5 bytes in v1 of QGF");
+STATIC_ASSERT(sizeof(qgf_block_header_v1_t) == 5, "qgf_block_header_v1_t must be 5 bytes in v1 of QGF");
/////////////////////////////////////////
// Graphics descriptor
@@ -42,7 +43,7 @@ typedef struct QP_PACKED qgf_graphics_descriptor_v1_t {
uint16_t frame_count; // minimum of 1
} qgf_graphics_descriptor_v1_t;
-_Static_assert(sizeof(qgf_graphics_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 18), "qgf_graphics_descriptor_v1_t must be 23 bytes in v1 of QGF");
+STATIC_ASSERT(sizeof(qgf_graphics_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 18), "qgf_graphics_descriptor_v1_t must be 23 bytes in v1 of QGF");
#define QGF_MAGIC 0x464751
@@ -56,7 +57,7 @@ typedef struct QP_PACKED qgf_frame_offsets_v1_t {
uint32_t offset[0]; // '0' signifies that this struct is immediately followed by the frame offsets
} qgf_frame_offsets_v1_t;
-_Static_assert(sizeof(qgf_frame_offsets_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_frame_offsets_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
+STATIC_ASSERT(sizeof(qgf_frame_offsets_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_frame_offsets_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
/////////////////////////////////////////
// Frame descriptor
@@ -72,7 +73,7 @@ typedef struct QP_PACKED qgf_frame_v1_t {
uint16_t delay; // frame delay time for animations (in units of milliseconds)
} qgf_frame_v1_t;
-_Static_assert(sizeof(qgf_frame_v1_t) == (sizeof(qgf_block_header_v1_t) + 6), "qgf_frame_v1_t must be 11 bytes in v1 of QGF");
+STATIC_ASSERT(sizeof(qgf_frame_v1_t) == (sizeof(qgf_block_header_v1_t) + 6), "qgf_frame_v1_t must be 11 bytes in v1 of QGF");
#define QGF_FRAME_FLAG_DELTA 0x02
#define QGF_FRAME_FLAG_TRANSPARENT 0x01
@@ -88,14 +89,14 @@ typedef struct QP_PACKED qgf_palette_entry_v1_t {
uint8_t v; // value component: `[0,1]` is mapped to `[0,255]` uint8_t.
} qgf_palette_entry_v1_t;
-_Static_assert(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size");
+STATIC_ASSERT(sizeof(qgf_palette_entry_v1_t) == 3, "Palette entry is not 3 bytes in size");
typedef struct QP_PACKED qgf_palette_v1_t {
qgf_block_header_v1_t header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) }
qgf_palette_entry_v1_t hsv[0]; // N * hsv, where N is the number of palette entries depending on the frame format in the descriptor
} qgf_palette_v1_t;
-_Static_assert(sizeof(qgf_palette_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_palette_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
+STATIC_ASSERT(sizeof(qgf_palette_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_palette_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
/////////////////////////////////////////
// Frame delta descriptor
@@ -110,7 +111,7 @@ typedef struct QP_PACKED qgf_delta_v1_t {
uint16_t bottom; // The bottom pixel location to to draw the delta image
} qgf_delta_v1_t;
-_Static_assert(sizeof(qgf_delta_v1_t) == (sizeof(qgf_block_header_v1_t) + 8), "qgf_delta_v1_t must be 13 bytes in v1 of QGF");
+STATIC_ASSERT(sizeof(qgf_delta_v1_t) == (sizeof(qgf_block_header_v1_t) + 8), "qgf_delta_v1_t must be 13 bytes in v1 of QGF");
/////////////////////////////////////////
// Frame data descriptor
@@ -122,7 +123,7 @@ typedef struct QP_PACKED qgf_data_v1_t {
uint8_t data[0]; // 0 signifies that this struct is immediately followed by the length of data specified in the header
} qgf_data_v1_t;
-_Static_assert(sizeof(qgf_data_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_data_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
+STATIC_ASSERT(sizeof(qgf_data_v1_t) == sizeof(qgf_block_header_v1_t), "qgf_data_v1_t must only contain qgf_block_header_v1_t in v1 of QGF");
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QGF API
diff --git a/quantum/painter/qp_draw_core.c b/quantum/painter/qp_draw_core.c
index aa5fa4aa76..852abb19e8 100644
--- a/quantum/painter/qp_draw_core.c
+++ b/quantum/painter/qp_draw_core.c
@@ -2,12 +2,13 @@
// Copyright 2021 Paul Cotter (@gr1mr3aver)
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "compiler_support.h"
#include "qp_internal.h"
#include "qp_comms.h"
#include "qp_draw.h"
#include "qgf.h"
-_Static_assert((QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE > 0) && (QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE % 16) == 0, "QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE needs to be a non-zero multiple of 16");
+STATIC_ASSERT((QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE > 0) && (QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE % 16) == 0, "QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE needs to be a non-zero multiple of 16");
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Global variables
diff --git a/quantum/painter/qp_internal.c b/quantum/painter/qp_internal.c
index 24b881bd09..fe0c598d78 100644
--- a/quantum/painter/qp_internal.c
+++ b/quantum/painter/qp_internal.c
@@ -3,6 +3,8 @@
#include "qp_internal.h"
+#include "compiler_support.h"
+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: device registration
@@ -67,7 +69,7 @@ static void qp_internal_display_timeout_task(void) {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter Core API: qp_internal_task
-_Static_assert((QUANTUM_PAINTER_TASK_THROTTLE) > 0 && (QUANTUM_PAINTER_TASK_THROTTLE) < 1000, "QUANTUM_PAINTER_TASK_THROTTLE must be between 1 and 999");
+STATIC_ASSERT((QUANTUM_PAINTER_TASK_THROTTLE) > 0 && (QUANTUM_PAINTER_TASK_THROTTLE) < 1000, "QUANTUM_PAINTER_TASK_THROTTLE must be between 1 and 999");
void qp_internal_task(void) {
// Perform throttling of the internal processing of Quantum Painter
diff --git a/quantum/painter/qp_internal_formats.h b/quantum/painter/qp_internal_formats.h
index 1beb604b9e..bd7105cab2 100644
--- a/quantum/painter/qp_internal_formats.h
+++ b/quantum/painter/qp_internal_formats.h
@@ -3,6 +3,7 @@
#pragma once
+#include "compiler_support.h"
#include "qp_internal.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -29,7 +30,7 @@ typedef union QP_PACKED qp_pixel_t {
uint32_t dummy;
} qp_pixel_t;
-_Static_assert(sizeof(qp_pixel_t) == 4, "Invalid size for qp_pixel_t");
+STATIC_ASSERT(sizeof(qp_pixel_t) == 4, "Invalid size for qp_pixel_t");
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Quantum Painter image format
diff --git a/quantum/pointing_device/pointing_device.c b/quantum/pointing_device/pointing_device.c
index cac2875fc8..564c2d294c 100644
--- a/quantum/pointing_device/pointing_device.c
+++ b/quantum/pointing_device/pointing_device.c
@@ -25,6 +25,10 @@
# include "mousekey.h"
#endif
+#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE
+# include "usb_descriptor_common.h"
+#endif
+
#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1
# error More than one rotation selected. This is not supported.
#endif
@@ -78,6 +82,9 @@ uint16_t pointing_device_get_shared_cpi(void) {
static report_mouse_t local_mouse_report = {};
static bool pointing_device_force_send = false;
+#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE
+static uint16_t hires_scroll_resolution;
+#endif
#define POINTING_DEVICE_DRIVER_CONCAT(name) name##_pointing_device_driver
#define POINTING_DEVICE_DRIVER(name) POINTING_DEVICE_DRIVER_CONCAT(name)
@@ -102,6 +109,11 @@ const pointing_device_driver_t custom_pointing_device_driver = {
const pointing_device_driver_t *pointing_device_driver = &POINTING_DEVICE_DRIVER(POINTING_DEVICE_DRIVER_NAME);
+__attribute__((weak)) void pointing_device_init_modules(void) {}
+__attribute__((weak)) report_mouse_t pointing_device_task_modules(report_mouse_t mouse_report) {
+ return mouse_report;
+}
+
/**
* @brief Keyboard level code pointing device initialisation
*
@@ -176,7 +188,14 @@ __attribute__((weak)) void pointing_device_init(void) {
# endif
#endif
}
+#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE
+ hires_scroll_resolution = POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER;
+ for (int i = 0; i < POINTING_DEVICE_HIRES_SCROLL_EXPONENT; i++) {
+ hires_scroll_resolution *= 10;
+ }
+#endif
+ pointing_device_init_modules();
pointing_device_init_kb();
pointing_device_init_user();
}
@@ -306,8 +325,9 @@ __attribute__((weak)) bool pointing_device_task(void) {
local_mouse_report = is_keyboard_left() ? pointing_device_task_combined_kb(local_mouse_report, shared_mouse_report) : pointing_device_task_combined_kb(shared_mouse_report, local_mouse_report);
#else
local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report);
- local_mouse_report = pointing_device_task_kb(local_mouse_report);
#endif
+ local_mouse_report = pointing_device_task_modules(local_mouse_report);
+ local_mouse_report = pointing_device_task_kb(local_mouse_report);
// automatic mouse layer function
#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE
pointing_device_task_auto_mouse(local_mouse_report);
@@ -404,10 +424,10 @@ void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) {
* @return mouse_hv_report_t clamped value
*/
static inline mouse_hv_report_t pointing_device_hv_clamp(hv_clamp_range_t value) {
- if (value < HV_REPORT_MIN) {
- return HV_REPORT_MIN;
- } else if (value > HV_REPORT_MAX) {
- return HV_REPORT_MAX;
+ if (value < MOUSE_REPORT_HV_MIN) {
+ return MOUSE_REPORT_HV_MIN;
+ } else if (value > MOUSE_REPORT_HV_MAX) {
+ return MOUSE_REPORT_HV_MAX;
} else {
return value;
}
@@ -420,10 +440,10 @@ static inline mouse_hv_report_t pointing_device_hv_clamp(hv_clamp_range_t value)
* @return mouse_xy_report_t clamped value
*/
static inline mouse_xy_report_t pointing_device_xy_clamp(xy_clamp_range_t value) {
- if (value < XY_REPORT_MIN) {
- return XY_REPORT_MIN;
- } else if (value > XY_REPORT_MAX) {
- return XY_REPORT_MAX;
+ if (value < MOUSE_REPORT_XY_MIN) {
+ return MOUSE_REPORT_XY_MIN;
+ } else if (value > MOUSE_REPORT_XY_MAX) {
+ return MOUSE_REPORT_XY_MAX;
} else {
return value;
}
@@ -523,3 +543,9 @@ __attribute__((weak)) void pointing_device_keycode_handler(uint16_t keycode, boo
pointing_device_send();
}
}
+
+#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE
+uint16_t pointing_device_get_hires_scroll_resolution(void) {
+ return hires_scroll_resolution;
+}
+#endif \ No newline at end of file
diff --git a/quantum/pointing_device/pointing_device.h b/quantum/pointing_device/pointing_device.h
index 72188c977d..e7a0819ed9 100644
--- a/quantum/pointing_device/pointing_device.h
+++ b/quantum/pointing_device/pointing_device.h
@@ -17,6 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
+#include <stdbool.h>
#include <stdint.h>
#include "host.h"
#include "report.h"
@@ -92,27 +93,19 @@ typedef enum {
} pointing_device_buttons_t;
#ifdef MOUSE_EXTENDED_REPORT
-# define XY_REPORT_MIN INT16_MIN
-# define XY_REPORT_MAX INT16_MAX
typedef int32_t xy_clamp_range_t;
#else
-# define XY_REPORT_MIN INT8_MIN
-# define XY_REPORT_MAX INT8_MAX
typedef int16_t xy_clamp_range_t;
#endif
#ifdef WHEEL_EXTENDED_REPORT
-# define HV_REPORT_MIN INT16_MIN
-# define HV_REPORT_MAX INT16_MAX
typedef int32_t hv_clamp_range_t;
#else
-# define HV_REPORT_MIN INT8_MIN
-# define HV_REPORT_MAX INT8_MAX
typedef int16_t hv_clamp_range_t;
#endif
#define CONSTRAIN_HID(amt) ((amt) < INT8_MIN ? INT8_MIN : ((amt) > INT8_MAX ? INT8_MAX : (amt)))
-#define CONSTRAIN_HID_XY(amt) ((amt) < XY_REPORT_MIN ? XY_REPORT_MIN : ((amt) > XY_REPORT_MAX ? XY_REPORT_MAX : (amt)))
+#define CONSTRAIN_HID_XY(amt) ((amt) < MOUSE_REPORT_XY_MIN ? MOUSE_REPORT_XY_MIN : ((amt) > MOUSE_REPORT_XY_MAX ? MOUSE_REPORT_XY_MAX : (amt)))
void pointing_device_init(void);
bool pointing_device_task(void);
@@ -130,6 +123,10 @@ uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, poi
report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report);
void pointing_device_keycode_handler(uint16_t keycode, bool pressed);
+#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE
+uint16_t pointing_device_get_hires_scroll_resolution(void);
+#endif
+
#if defined(SPLIT_POINTING_ENABLE)
void pointing_device_set_shared_report(report_mouse_t report);
uint16_t pointing_device_get_shared_cpi(void);
diff --git a/quantum/process_keycode/process_autocorrect.c b/quantum/process_keycode/process_autocorrect.c
index b7f9132acf..0cde520778 100644
--- a/quantum/process_keycode/process_autocorrect.c
+++ b/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);
}
/**
diff --git a/quantum/process_keycode/process_caps_word.c b/quantum/process_keycode/process_caps_word.c
index b8fb868c6d..8ab66cc521 100644
--- a/quantum/process_keycode/process_caps_word.c
+++ b/quantum/process_keycode/process_caps_word.c
@@ -160,8 +160,13 @@ bool process_caps_word(uint16_t keycode, keyrecord_t* record) {
case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
+#ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.
case QK_TRI_LAYER_LOWER ... QK_TRI_LAYER_UPPER:
- // Ignore AltGr.
+#endif // TRI_LAYER_ENABLE
+#ifdef LAYER_LOCK_ENABLE // Ignore Layer Lock key.
+ case QK_LAYER_LOCK:
+#endif // LAYER_LOCK_ENABLE
+ // Ignore AltGr.
case KC_RALT:
case OSM(MOD_RALT):
return true;
diff --git a/quantum/process_keycode/process_clicky.c b/quantum/process_keycode/process_clicky.c
index 82000db9b3..f50761268a 100644
--- a/quantum/process_keycode/process_clicky.c
+++ b/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) {
diff --git a/quantum/process_keycode/process_connection.c b/quantum/process_keycode/process_connection.c
index b0e230d680..501529ede7 100644
--- a/quantum/process_keycode/process_connection.c
+++ b/quantum/process_keycode/process_connection.c
@@ -1,24 +1,34 @@
// Copyright 2024 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later
-#include "outputselect.h"
+#include "connection.h"
#include "process_connection.h"
bool process_connection(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
switch (keycode) {
case QK_OUTPUT_NEXT:
- set_output(OUTPUT_AUTO); // This should cycle through the outputs going forward. Ensure `docs/keycodes.md`, `docs/features/bluetooth.md` are updated when it does.
+ connection_next_host();
+ return false;
+ case QK_OUTPUT_PREV:
+ connection_prev_host();
+ return false;
+
+ case QK_OUTPUT_AUTO:
+ connection_set_host(CONNECTION_HOST_AUTO);
+ return false;
+ case QK_OUTPUT_NONE:
+ connection_set_host(CONNECTION_HOST_NONE);
return false;
case QK_OUTPUT_USB:
- set_output(OUTPUT_USB);
+ connection_set_host(CONNECTION_HOST_USB);
return false;
case QK_OUTPUT_BLUETOOTH:
- set_output(OUTPUT_BLUETOOTH);
+ connection_set_host(CONNECTION_HOST_BLUETOOTH);
return false;
-
- case QK_OUTPUT_PREV:
- case QK_OUTPUT_NONE:
case QK_OUTPUT_2P4GHZ:
+ connection_set_host(CONNECTION_HOST_2P4GHZ);
+ return false;
+
case QK_BLUETOOTH_PROFILE_NEXT:
case QK_BLUETOOTH_PROFILE_PREV:
case QK_BLUETOOTH_UNPAIR:
diff --git a/quantum/process_keycode/process_leader.c b/quantum/process_keycode/process_leader.c
index ca017a577d..a5466c513c 100644
--- a/quantum/process_keycode/process_leader.c
+++ b/quantum/process_keycode/process_leader.c
@@ -22,11 +22,7 @@ bool process_leader(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
if (leader_sequence_active() && !leader_sequence_timed_out()) {
#ifndef LEADER_KEY_STRICT_KEY_PROCESSING
- if (IS_QK_MOD_TAP(keycode)) {
- keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode);
- } else if (IS_QK_LAYER_TAP(keycode)) {
- keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
- }
+ keycode = get_tap_keycode(keycode);
#endif
if (!leader_sequence_add(keycode)) {
diff --git a/quantum/process_keycode/process_magic.c b/quantum/process_keycode/process_magic.c
index 3b35884d68..d5280105de 100644
--- a/quantum/process_keycode/process_magic.c
+++ b/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;
diff --git a/quantum/process_keycode/process_oneshot.c b/quantum/process_keycode/process_oneshot.c
new file mode 100644
index 0000000000..6c43da1011
--- /dev/null
+++ b/quantum/process_keycode/process_oneshot.c
@@ -0,0 +1,25 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "process_oneshot.h"
+#include "action_util.h"
+
+bool process_oneshot(uint16_t keycode, keyrecord_t *record) {
+#ifndef NO_ACTION_ONESHOT
+ if (record->event.pressed) {
+ switch (keycode) {
+ case QK_ONE_SHOT_TOGGLE:
+ oneshot_toggle();
+ return false;
+ case QK_ONE_SHOT_ON:
+ oneshot_enable();
+ return false;
+ case QK_ONE_SHOT_OFF:
+ oneshot_disable();
+ return false;
+ }
+ }
+#endif
+
+ return true;
+}
diff --git a/quantum/process_keycode/process_oneshot.h b/quantum/process_keycode/process_oneshot.h
new file mode 100644
index 0000000000..11445b9b38
--- /dev/null
+++ b/quantum/process_keycode/process_oneshot.h
@@ -0,0 +1,10 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "action.h"
+
+bool process_oneshot(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/process_keycode/process_quantum.c b/quantum/process_keycode/process_quantum.c
new file mode 100644
index 0000000000..703e00755b
--- /dev/null
+++ b/quantum/process_keycode/process_quantum.c
@@ -0,0 +1,76 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "process_quantum.h"
+#include "quantum.h"
+
+#ifdef ENABLE_COMPILE_KEYCODE
+/** \brief Compiles the firmware, and adds the flash command based on keyboard bootloader
+ */
+static void send_make_command(void) {
+# ifdef NO_ACTION_ONESHOT
+ const uint8_t temp_mod = mod_config(get_mods());
+# else
+ const uint8_t temp_mod = mod_config(get_mods() | get_oneshot_mods());
+ clear_oneshot_mods();
+# endif
+ clear_mods();
+
+ SEND_STRING_DELAY("qmk", TAP_CODE_DELAY);
+ if (temp_mod & MOD_MASK_SHIFT) { // if shift is held, flash rather than compile
+ SEND_STRING_DELAY(" flash ", TAP_CODE_DELAY);
+ } else {
+ SEND_STRING_DELAY(" compile ", TAP_CODE_DELAY);
+ }
+# if defined(CONVERTER_ENABLED)
+ SEND_STRING_DELAY("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP " -e CONVERT_TO=" CONVERTER_TARGET SS_TAP(X_ENTER), TAP_CODE_DELAY);
+# else
+ SEND_STRING_DELAY("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP SS_TAP(X_ENTER), TAP_CODE_DELAY);
+# endif
+ if (temp_mod & MOD_MASK_SHIFT && temp_mod & MOD_MASK_CTRL) {
+ reset_keyboard();
+ }
+}
+#endif
+
+bool process_quantum(uint16_t keycode, keyrecord_t *record) {
+ if (record->event.pressed) {
+ switch (keycode) {
+#ifndef NO_RESET
+ case QK_BOOTLOADER:
+ reset_keyboard();
+ return false;
+ case QK_REBOOT:
+ soft_reset_keyboard();
+ return false;
+#endif
+#ifndef NO_DEBUG
+ case QK_DEBUG_TOGGLE:
+ debug_enable ^= 1;
+ if (debug_enable) {
+ print("DEBUG: enabled.\n");
+ } else {
+ print("DEBUG: disabled.\n");
+ }
+ return false;
+#endif
+ case QK_CLEAR_EEPROM:
+#ifdef NO_RESET
+ eeconfig_init();
+#else
+ eeconfig_disable();
+ soft_reset_keyboard();
+#endif
+ return false;
+#ifdef ENABLE_COMPILE_KEYCODE
+ case QK_MAKE:
+ send_make_command();
+ return false;
+#endif
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
diff --git a/quantum/process_keycode/process_quantum.h b/quantum/process_keycode/process_quantum.h
new file mode 100644
index 0000000000..5c2462be01
--- /dev/null
+++ b/quantum/process_keycode/process_quantum.h
@@ -0,0 +1,10 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "action.h"
+
+bool process_quantum(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/process_keycode/process_repeat_key.c b/quantum/process_keycode/process_repeat_key.c
index 73f4ddedcf..fdeed4f466 100644
--- a/quantum/process_keycode/process_repeat_key.c
+++ b/quantum/process_keycode/process_repeat_key.c
@@ -41,7 +41,10 @@ static bool remember_last_key(uint16_t keycode, keyrecord_t* record, uint8_t* re
#ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.
case QK_TRI_LAYER_LOWER:
case QK_TRI_LAYER_UPPER:
-#endif // TRI_LAYER_ENABLE
+#endif // TRI_LAYER_ENABLE
+#ifdef LAYER_LOCK_ENABLE // Ignore Layer Lock key.
+ case QK_LAYER_LOCK:
+#endif // LAYER_LOCK_ENABLE
return false;
// Ignore hold events on tap-hold keys.
diff --git a/quantum/process_keycode/process_steno.c b/quantum/process_keycode/process_steno.c
index c491d6f1d8..4789461352 100644
--- a/quantum/process_keycode/process_steno.c
+++ b/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
diff --git a/quantum/process_keycode/process_underglow.c b/quantum/process_keycode/process_underglow.c
index b8d8989ef3..78006ee099 100644
--- a/quantum/process_keycode/process_underglow.c
+++ b/quantum/process_keycode/process_underglow.c
@@ -200,6 +200,11 @@ bool process_underglow(uint16_t keycode, keyrecord_t *record) {
}
#endif
return false;
+#ifdef VELOCIKEY_ENABLE
+ case QK_VELOCIKEY_TOGGLE:
+ velocikey_toggle();
+ return false;
+#endif
}
}
diff --git a/quantum/quantum.c b/quantum/quantum.c
index adb14d64b6..cc20c7db2b 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -15,12 +15,13 @@
*/
#include "quantum.h"
+#include "process_quantum.h"
#ifdef BACKLIGHT_ENABLE
# include "process_backlight.h"
#endif
-#ifdef BLUETOOTH_ENABLE
+#ifdef CONNECTION_ENABLE
# include "process_connection.h"
#endif
@@ -84,6 +85,10 @@
# include "process_layer_lock.h"
#endif
+#ifndef NO_ACTION_ONESHOT
+# include "process_oneshot.h"
+#endif
+
#ifdef AUDIO_ENABLE
# ifndef GOODBYE_SONG
# define GOODBYE_SONG SONG(GOODBYE_SOUND)
@@ -174,10 +179,6 @@ __attribute__((weak)) bool pre_process_record_user(uint16_t keycode, keyrecord_t
return true;
}
-__attribute__((weak)) bool process_action_kb(keyrecord_t *record) {
- return true;
-}
-
__attribute__((weak)) bool process_record_modules(uint16_t keycode, keyrecord_t *record) {
return true;
}
@@ -292,9 +293,10 @@ void post_process_record_quantum(keyrecord_t *record) {
post_process_record_kb(keycode, record);
}
-/* Core keycode function, hands off handling to other functions,
- then processes internal quantum keycodes, and then processes
- ACTIONs. */
+/** \brief Core keycode function
+ *
+ * Hands off handling to other quantum/process_keycode/ functions
+ */
bool process_record_quantum(keyrecord_t *record) {
uint16_t keycode = get_record_keycode(record, true);
@@ -436,88 +438,17 @@ bool process_record_quantum(keyrecord_t *record) {
#ifdef LAYER_LOCK_ENABLE
process_layer_lock(keycode, record) &&
#endif
-#ifdef BLUETOOTH_ENABLE
+#ifdef CONNECTION_ENABLE
process_connection(keycode, record) &&
#endif
- true)) {
- return false;
- }
-
- if (record->event.pressed) {
- switch (keycode) {
-#ifndef NO_RESET
- case QK_BOOTLOADER:
- reset_keyboard();
- return false;
- case QK_REBOOT:
- soft_reset_keyboard();
- return false;
-#endif
-#ifndef NO_DEBUG
- case QK_DEBUG_TOGGLE:
- debug_enable ^= 1;
- if (debug_enable) {
- print("DEBUG: enabled.\n");
- } else {
- print("DEBUG: disabled.\n");
- }
-#endif
- return false;
- case QK_CLEAR_EEPROM:
-#ifdef NO_RESET
- eeconfig_init();
-#else
- eeconfig_disable();
- soft_reset_keyboard();
-#endif
- return false;
-#ifdef VELOCIKEY_ENABLE
- case QK_VELOCIKEY_TOGGLE:
- velocikey_toggle();
- return false;
-#endif
#ifndef NO_ACTION_ONESHOT
- case QK_ONE_SHOT_TOGGLE:
- oneshot_toggle();
- break;
- case QK_ONE_SHOT_ON:
- oneshot_enable();
- break;
- case QK_ONE_SHOT_OFF:
- oneshot_disable();
- break;
-#endif
-#ifdef ENABLE_COMPILE_KEYCODE
- case QK_MAKE: // Compiles the firmware, and adds the flash command based on keyboard bootloader
- {
-# ifdef NO_ACTION_ONESHOT
- const uint8_t temp_mod = mod_config(get_mods());
-# else
- const uint8_t temp_mod = mod_config(get_mods() | get_oneshot_mods());
- clear_oneshot_mods();
-# endif
- clear_mods();
-
- SEND_STRING_DELAY("qmk", TAP_CODE_DELAY);
- if (temp_mod & MOD_MASK_SHIFT) { // if shift is held, flash rather than compile
- SEND_STRING_DELAY(" flash ", TAP_CODE_DELAY);
- } else {
- SEND_STRING_DELAY(" compile ", TAP_CODE_DELAY);
- }
-# if defined(CONVERTER_ENABLED)
- SEND_STRING_DELAY("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP " -e CONVERT_TO=" CONVERTER_TARGET SS_TAP(X_ENTER), TAP_CODE_DELAY);
-# else
- SEND_STRING_DELAY("-kb " QMK_KEYBOARD " -km " QMK_KEYMAP SS_TAP(X_ENTER), TAP_CODE_DELAY);
-# endif
- if (temp_mod & MOD_MASK_SHIFT && temp_mod & MOD_MASK_CTRL) {
- reset_keyboard();
- }
- }
+ process_oneshot(keycode, record) &&
#endif
- }
+ process_quantum(keycode, record))) {
+ return false;
}
- return process_action_kb(record);
+ return true;
}
void set_single_default_layer(uint8_t default_layer) {
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 59a415ead4..0036cd784b 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -39,6 +39,7 @@
#include "keymap_common.h"
#include "quantum_keycodes.h"
#include "keycode_config.h"
+#include "keycode_string.h"
#include "action_layer.h"
#include "eeconfig.h"
#include "bootloader.h"
@@ -262,7 +263,6 @@ uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);
bool pre_process_record_quantum(keyrecord_t *record);
bool pre_process_record_kb(uint16_t keycode, keyrecord_t *record);
bool pre_process_record_user(uint16_t keycode, keyrecord_t *record);
-bool process_action_kb(keyrecord_t *record);
bool process_record_kb(uint16_t keycode, keyrecord_t *record);
bool process_record_user(uint16_t keycode, keyrecord_t *record);
void post_process_record_kb(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/raw_hid.c b/quantum/raw_hid.c
new file mode 100644
index 0000000000..2c7a75f325
--- /dev/null
+++ b/quantum/raw_hid.c
@@ -0,0 +1,15 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "raw_hid.h"
+#include "host.h"
+
+void raw_hid_send(uint8_t *data, uint8_t length) {
+ host_raw_hid_send(data, length);
+}
+
+__attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {
+ // Users should #include "raw_hid.h" in their own code
+ // and implement this function there. Leave this as weak linkage
+ // so users can opt to not handle data coming in.
+}
diff --git a/quantum/rgb_matrix/animations/pixel_flow_anim.h b/quantum/rgb_matrix/animations/pixel_flow_anim.h
index 35170f9588..44b4b5c481 100644
--- a/quantum/rgb_matrix/animations/pixel_flow_anim.h
+++ b/quantum/rgb_matrix/animations/pixel_flow_anim.h
@@ -22,7 +22,7 @@ static bool PIXEL_FLOW(effect_params_t* params) {
// Clear LEDs and fill the state array
rgb_matrix_set_color_all(0, 0, 0);
for (uint8_t j = 0; j < RGB_MATRIX_LED_COUNT; ++j) {
- led[j] = (random8() & 2) ? (rgb_t){0, 0, 0} : hsv_to_rgb((hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v});
+ led[j] = (random8() & 2) ? (rgb_t){0, 0, 0} : rgb_matrix_hsv_to_rgb((hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v});
}
}
@@ -39,7 +39,7 @@ static bool PIXEL_FLOW(effect_params_t* params) {
led[j] = led[j + 1];
}
// Fill last LED
- led[led_max - 1] = (random8() & 2) ? (rgb_t){0, 0, 0} : hsv_to_rgb((hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v});
+ led[led_max - 1] = (random8() & 2) ? (rgb_t){0, 0, 0} : rgb_matrix_hsv_to_rgb((hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v});
// Set pulse timer
wait_timer = g_rgb_timer + interval();
}
diff --git a/quantum/rgb_matrix/animations/rgb_matrix_effects.inc b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
index a02238a2d1..abc2adf2cc 100644
--- a/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
+++ b/quantum/rgb_matrix/animations/rgb_matrix_effects.inc
@@ -39,7 +39,8 @@
#include "solid_reactive_nexus.h"
#include "splash_anim.h"
#include "solid_splash_anim.h"
+#include "starlight_smooth_anim.h"
#include "starlight_anim.h"
#include "starlight_dual_sat_anim.h"
#include "starlight_dual_hue_anim.h"
-#include "riverflow_anim.h" \ No newline at end of file
+#include "riverflow_anim.h"
diff --git a/quantum/rgb_matrix/animations/starlight_smooth_anim.h b/quantum/rgb_matrix/animations/starlight_smooth_anim.h
new file mode 100644
index 0000000000..a87c06f9df
--- /dev/null
+++ b/quantum/rgb_matrix/animations/starlight_smooth_anim.h
@@ -0,0 +1,26 @@
+// Copyright 2022 @art-was-here
+// SPDX-License-Identifier: GPL-2.0+
+
+#ifdef ENABLE_RGB_MATRIX_STARLIGHT_SMOOTH
+RGB_MATRIX_EFFECT(STARLIGHT_SMOOTH)
+# ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
+
+static uint8_t phase_offsets[RGB_MATRIX_LED_COUNT];
+
+hsv_t STARLIGHT_SMOOTH_math(hsv_t hsv, uint8_t i, uint8_t time) {
+ if (phase_offsets[i] == 0) {
+ phase_offsets[i] = rand() % 255;
+ }
+ hsv.v = scale8(abs8(sin8((time + phase_offsets[i]) / 2) - 128) * 2, hsv.v);
+ return hsv;
+}
+
+bool STARLIGHT_SMOOTH(effect_params_t* params) {
+ if (params->init) {
+ memset(phase_offsets, 0, sizeof(phase_offsets));
+ }
+ return effect_runner_i(params, &STARLIGHT_SMOOTH_math);
+}
+
+# endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS
+#endif // ENABLE_RGB_MATRIX_STARLIGHT_SMOOTH
diff --git a/quantum/rgb_matrix/rgb_matrix.c b/quantum/rgb_matrix/rgb_matrix.c
index 484d177546..ab0aa17512 100644
--- a/quantum/rgb_matrix/rgb_matrix.c
+++ b/quantum/rgb_matrix/rgb_matrix.c
@@ -18,7 +18,6 @@
#include "rgb_matrix.h"
#include "progmem.h"
-#include "eeprom.h"
#include "eeconfig.h"
#include "keyboard.h"
#include "sync_timer.h"
@@ -48,6 +47,9 @@ __attribute__((weak)) rgb_t rgb_matrix_hsv_to_rgb(hsv_t hsv) {
#define RGB_MATRIX_CUSTOM_EFFECT_IMPLS
#include "rgb_matrix_effects.inc"
+#ifdef COMMUNITY_MODULES_ENABLE
+# include "rgb_matrix_community_modules.inc"
+#endif
#ifdef RGB_MATRIX_CUSTOM_KB
# include "rgb_matrix_kb.inc"
#endif
@@ -88,9 +90,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);
}
@@ -107,7 +109,11 @@ void eeconfig_update_rgb_matrix_default(void) {
void eeconfig_debug_rgb_matrix(void) {
dprintf("rgb_matrix_config EEPROM\n");
dprintf("rgb_matrix_config.enable = %d\n", rgb_matrix_config.enable);
+#ifdef RGB_MATRIX_MODE_NAME_ENABLE
+ dprintf("rgb_matrix_config.mode = %d (%s)\n", rgb_matrix_config.mode, rgb_matrix_get_mode_name(rgb_matrix_config.mode));
+#else
dprintf("rgb_matrix_config.mode = %d\n", rgb_matrix_config.mode);
+#endif // RGB_MATRIX_MODE_NAME_ENABLE
dprintf("rgb_matrix_config.hsv.h = %d\n", rgb_matrix_config.hsv.h);
dprintf("rgb_matrix_config.hsv.s = %d\n", rgb_matrix_config.hsv.s);
dprintf("rgb_matrix_config.hsv.v = %d\n", rgb_matrix_config.hsv.v);
@@ -311,6 +317,15 @@ static void rgb_task_render(uint8_t effect) {
#include "rgb_matrix_effects.inc"
#undef RGB_MATRIX_EFFECT
+#ifdef COMMUNITY_MODULES_ENABLE
+# define RGB_MATRIX_EFFECT(name, ...) \
+ case RGB_MATRIX_COMMUNITY_MODULE_##name: \
+ rendering = name(&rgb_effect_params); \
+ break;
+# include "rgb_matrix_community_modules.inc"
+# undef RGB_MATRIX_EFFECT
+#endif
+
#if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER)
# define RGB_MATRIX_EFFECT(name, ...) \
case RGB_MATRIX_CUSTOM_##name: \
@@ -394,7 +409,12 @@ void rgb_matrix_task(void) {
}
}
+__attribute__((weak)) bool rgb_matrix_indicators_modules(void) {
+ return true;
+}
+
void rgb_matrix_indicators(void) {
+ rgb_matrix_indicators_modules();
rgb_matrix_indicators_kb();
}
@@ -434,6 +454,10 @@ struct rgb_matrix_limits_t rgb_matrix_get_limits(uint8_t iter) {
return limits;
}
+__attribute__((weak)) bool rgb_matrix_indicators_advanced_modules(uint8_t led_min, uint8_t led_max) {
+ return true;
+}
+
void rgb_matrix_indicators_advanced(effect_params_t *params) {
/* special handling is needed for "params->iter", since it's already been incremented.
* Could move the invocations to rgb_task_render, but then it's missing a few checks
@@ -441,6 +465,7 @@ void rgb_matrix_indicators_advanced(effect_params_t *params) {
* rgb_task_render, right before the iter++ line.
*/
RGB_MATRIX_USE_LIMITS_ITER(min, max, params->iter - 1);
+ rgb_matrix_indicators_advanced_modules(min, max);
rgb_matrix_indicators_advanced_kb(min, max);
}
@@ -539,7 +564,11 @@ void rgb_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
}
rgb_task_state = STARTING;
eeconfig_flag_rgb_matrix(write_to_eeprom);
- dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", rgb_matrix_config.mode);
+#ifdef RGB_MATRIX_MODE_NAME_ENABLE
+ dprintf("rgb matrix mode [%s]: %u (%s)\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", (unsigned)rgb_matrix_config.mode, rgb_matrix_get_mode_name(rgb_matrix_config.mode));
+#else
+ dprintf("rgb matrix mode [%s]: %u\n", (write_to_eeprom) ? "EEPROM" : "NOEEPROM", (unsigned)rgb_matrix_config.mode);
+#endif // RGB_MATRIX_MODE_NAME_ENABLE
}
void rgb_matrix_mode_noeeprom(uint8_t mode) {
rgb_matrix_mode_eeprom_helper(mode, false);
@@ -717,3 +746,49 @@ void rgb_matrix_set_flags(led_flags_t flags) {
void rgb_matrix_set_flags_noeeprom(led_flags_t flags) {
rgb_matrix_set_flags_eeprom_helper(flags, false);
}
+
+//----------------------------------------------------------
+// RGB Matrix naming
+#undef RGB_MATRIX_EFFECT
+#ifdef RGB_MATRIX_MODE_NAME_ENABLE
+const char *rgb_matrix_get_mode_name(uint8_t mode) {
+ switch (mode) {
+ case RGB_MATRIX_NONE:
+ return "NONE";
+
+# define RGB_MATRIX_EFFECT(name, ...) \
+ case RGB_MATRIX_##name: \
+ return #name;
+# include "rgb_matrix_effects.inc"
+# undef RGB_MATRIX_EFFECT
+
+# ifdef COMMUNITY_MODULES_ENABLE
+# define RGB_MATRIX_EFFECT(name, ...) \
+ case RGB_MATRIX_COMMUNITY_MODULE_##name: \
+ return #name;
+# include "rgb_matrix_community_modules.inc"
+# undef RGB_MATRIX_EFFECT
+# endif // COMMUNITY_MODULES_ENABLE
+
+# if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER)
+# define RGB_MATRIX_EFFECT(name, ...) \
+ case RGB_MATRIX_CUSTOM_##name: \
+ return #name;
+
+# ifdef RGB_MATRIX_CUSTOM_KB
+# include "rgb_matrix_kb.inc"
+# endif // RGB_MATRIX_CUSTOM_KB
+
+# ifdef RGB_MATRIX_CUSTOM_USER
+# include "rgb_matrix_user.inc"
+# endif // RGB_MATRIX_CUSTOM_USER
+
+# undef RGB_MATRIX_EFFECT
+# endif // RGB_MATRIX_CUSTOM_KB || RGB_MATRIX_CUSTOM_USER
+
+ default:
+ return "UNKNOWN";
+ }
+}
+# undef RGB_MATRIX_EFFECT
+#endif // RGB_MATRIX_MODE_NAME_ENABLE
diff --git a/quantum/rgb_matrix/rgb_matrix.h b/quantum/rgb_matrix/rgb_matrix.h
index 33f7e06a63..a91dded4a8 100644
--- a/quantum/rgb_matrix/rgb_matrix.h
+++ b/quantum/rgb_matrix/rgb_matrix.h
@@ -123,6 +123,12 @@ enum rgb_matrix_effects {
#include "rgb_matrix_effects.inc"
#undef RGB_MATRIX_EFFECT
+#ifdef COMMUNITY_MODULES_ENABLE
+# define RGB_MATRIX_EFFECT(name, ...) RGB_MATRIX_COMMUNITY_MODULE_##name,
+# include "rgb_matrix_community_modules.inc"
+# undef RGB_MATRIX_EFFECT
+#endif
+
#if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER)
# define RGB_MATRIX_EFFECT(name, ...) RGB_MATRIX_CUSTOM_##name,
# ifdef RGB_MATRIX_CUSTOM_KB
@@ -140,7 +146,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);
@@ -214,8 +220,12 @@ void rgb_matrix_set_flags(led_flags_t flags);
void rgb_matrix_set_flags_noeeprom(led_flags_t flags);
void rgb_matrix_update_pwm_buffers(void);
+#ifdef RGB_MATRIX_MODE_NAME_ENABLE
+const char *rgb_matrix_get_mode_name(uint8_t mode);
+#endif // RGB_MATRIX_MODE_NAME_ENABLE
+
#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
diff --git a/quantum/rgb_matrix/rgb_matrix_types.h b/quantum/rgb_matrix/rgb_matrix_types.h
index 0834a7067c..0115edee6a 100644
--- a/quantum/rgb_matrix/rgb_matrix_types.h
+++ b/quantum/rgb_matrix/rgb_matrix_types.h
@@ -18,6 +18,8 @@
#include <stdint.h>
#include <stdbool.h>
+
+#include "compiler_support.h"
#include "color.h"
#include "util.h"
@@ -73,7 +75,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;
@@ -84,4 +86,4 @@ typedef union {
};
} rgb_config_t;
-_Static_assert(sizeof(rgb_config_t) == sizeof(uint64_t), "RGB Matrix EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(rgb_config_t) == sizeof(uint64_t), "RGB Matrix EECONFIG out of spec.");
diff --git a/quantum/rgblight/rgblight.c b/quantum/rgblight/rgblight.c
index ddccc9bae3..bfccb472b5 100644
--- a/quantum/rgblight/rgblight.c
+++ b/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) {
@@ -513,7 +496,7 @@ void rgblight_decrease_speed_noeeprom(void) {
void rgblight_sethsv_noeeprom_old(uint8_t hue, uint8_t sat, uint8_t val) {
if (rgblight_config.enable) {
- rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
+ rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
rgblight_setrgb(rgb.r, rgb.g, rgb.b);
}
}
@@ -532,7 +515,7 @@ void rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool w
// needed for rgblight_layers_write() to get the new val, since it reads rgblight_config.val
rgblight_config.val = val;
#endif
- rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
+ rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
rgblight_setrgb(rgb.r, rgb.g, rgb.b);
} else {
// all LEDs in same color
@@ -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);
@@ -664,7 +647,7 @@ void rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index) {
return;
}
- rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
+ rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
rgblight_setrgb_at(rgb.r, rgb.g, rgb.b, index);
}
@@ -696,7 +679,7 @@ void rgblight_sethsv_range(uint8_t hue, uint8_t sat, uint8_t val, uint8_t start,
return;
}
- rgb_t rgb = hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
+ rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});
rgblight_setrgb_range(rgb.r, rgb.g, rgb.b, start, end);
}
diff --git a/quantum/rgblight/rgblight.h b/quantum/rgblight/rgblight.h
index f4b6e621e4..c061e71895 100644
--- a/quantum/rgblight/rgblight.h
+++ b/quantum/rgblight/rgblight.h
@@ -16,6 +16,8 @@
#pragma once
+#include "compiler_support.h"
+
// DEPRECATED DEFINES - DO NOT USE
#if defined(RGBLED_NUM)
# define RGBLIGHT_LED_COUNT RGBLED_NUM
@@ -168,7 +170,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 +249,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;
@@ -261,7 +262,7 @@ typedef union {
};
} rgblight_config_t;
-_Static_assert(sizeof(rgblight_config_t) == sizeof(uint64_t), "RGB Light EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(rgblight_config_t) == sizeof(uint64_t), "RGB Light EECONFIG out of spec.");
typedef struct _rgblight_status_t {
uint8_t base_mode;
@@ -370,8 +371,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);
diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c
index 9af3c29d75..59b6009ec4 100644
--- a/quantum/split_common/split_util.c
+++ b/quantum/split_common/split_util.c
@@ -13,6 +13,8 @@
* 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 "compiler_support.h"
#include "split_util.h"
#include "matrix.h"
#include "keyboard.h"
@@ -62,7 +64,7 @@ static struct {
} split_config;
#if defined(SPLIT_USB_DETECT)
-_Static_assert((SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL) <= UINT16_MAX, "Please lower SPLIT_USB_TIMEOUT and/or increase SPLIT_USB_TIMEOUT_POLL.");
+STATIC_ASSERT((SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL) <= UINT16_MAX, "Please lower SPLIT_USB_TIMEOUT and/or increase SPLIT_USB_TIMEOUT_POLL.");
static bool usb_bus_detected(void) {
for (uint16_t i = 0; i < (SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL); i++) {
// This will return true if a USB connection has been established
@@ -88,9 +90,9 @@ static inline bool usb_bus_detected(void) {
# endif
# endif
# if defined(SPLIT_USB_DETECT)
-_Static_assert(SPLIT_USB_TIMEOUT < SPLIT_WATCHDOG_TIMEOUT, "SPLIT_WATCHDOG_TIMEOUT should not be below SPLIT_USB_TIMEOUT.");
+STATIC_ASSERT(SPLIT_USB_TIMEOUT < SPLIT_WATCHDOG_TIMEOUT, "SPLIT_WATCHDOG_TIMEOUT should not be below SPLIT_USB_TIMEOUT.");
# endif
-_Static_assert(SPLIT_MAX_CONNECTION_ERRORS > 0, "SPLIT_WATCHDOG_ENABLE requires SPLIT_MAX_CONNECTION_ERRORS be above 0 for a functioning disconnection check.");
+STATIC_ASSERT(SPLIT_MAX_CONNECTION_ERRORS > 0, "SPLIT_WATCHDOG_ENABLE requires SPLIT_MAX_CONNECTION_ERRORS be above 0 for a functioning disconnection check.");
static uint32_t split_watchdog_started = 0;
static bool split_watchdog_done = false;
diff --git a/quantum/split_common/split_util.h b/quantum/split_common/split_util.h
index f83b05b6a6..6f4c4dc660 100644
--- a/quantum/split_common/split_util.h
+++ b/quantum/split_common/split_util.h
@@ -15,4 +15,4 @@ bool is_transport_connected(void);
void split_watchdog_update(bool done);
void split_watchdog_task(void);
-bool split_watchdog_check(void); \ No newline at end of file
+bool split_watchdog_check(void);
diff --git a/quantum/split_common/transaction_id_define.h b/quantum/split_common/transaction_id_define.h
index 5bfbe2aec7..694737868a 100644
--- a/quantum/split_common/transaction_id_define.h
+++ b/quantum/split_common/transaction_id_define.h
@@ -16,6 +16,8 @@
#pragma once
+#include "compiler_support.h"
+
enum serial_transaction_id {
#ifdef USE_I2C
I2C_EXECUTE_CALLBACK,
@@ -122,4 +124,4 @@ enum serial_transaction_id {
};
// Ensure we only use 5 bits for transaction
-_Static_assert(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded");
+STATIC_ASSERT(NUM_TOTAL_TRANSACTIONS <= (1 << 5), "Max number of usable transactions exceeded");
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c
index 83edc34859..ea687af1c2 100644
--- a/quantum/split_common/transport.c
+++ b/quantum/split_common/transport.c
@@ -17,6 +17,7 @@
#include <string.h>
#include <debug.h>
+#include "compiler_support.h"
#include "transactions.h"
#include "transport.h"
#include "transaction_id_define.h"
@@ -36,7 +37,7 @@
# include "i2c_slave.h"
// Ensure the I2C buffer has enough space
-_Static_assert(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT");
+STATIC_ASSERT(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, "split_shared_memory_t too large for I2C_SLAVE_REG_COUNT");
split_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg;
diff --git a/quantum/unicode/unicode.c b/quantum/unicode/unicode.c
index 78a4cad585..dff1d43fb4 100644
--- a/quantum/unicode/unicode.c
+++ b/quantum/unicode/unicode.c
@@ -16,7 +16,6 @@
#include "unicode.h"
-#include "eeprom.h"
#include "eeconfig.h"
#include "action.h"
#include "action_util.h"
@@ -136,7 +135,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 +164,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) {
diff --git a/quantum/unicode/unicode.h b/quantum/unicode/unicode.h
index 90a54c8b18..7cddc78b7a 100644
--- a/quantum/unicode/unicode.h
+++ b/quantum/unicode/unicode.h
@@ -17,6 +17,8 @@
#pragma once
#include <stdint.h>
+
+#include "compiler_support.h"
#include "unicode_keycodes.h"
/**
@@ -26,14 +28,14 @@
* \{
*/
-typedef union {
+typedef union unicode_config_t {
uint8_t raw;
struct {
uint8_t input_mode : 8;
};
} unicode_config_t;
-_Static_assert(sizeof(unicode_config_t) == sizeof(uint8_t), "Unicode EECONFIG out of spec.");
+STATIC_ASSERT(sizeof(unicode_config_t) == sizeof(uint8_t), "Unicode EECONFIG out of spec.");
extern unicode_config_t unicode_config;
diff --git a/quantum/via.c b/quantum/via.c
index 643d7aa3c3..3682b4ab2b 100644
--- a/quantum/via.c
+++ b/quantum/via.c
@@ -26,12 +26,12 @@
#include "raw_hid.h"
#include "dynamic_keymap.h"
-#include "eeprom.h"
#include "eeconfig.h"
#include "matrix.h"
#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 +65,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 +110,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 +127,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 +718,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 +797,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 +864,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
diff --git a/quantum/via.h b/quantum/via.h
index 01d4c48b37..9f0eef10f7 100644
--- a/quantum/via.h
+++ b/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.
@@ -202,4 +187,4 @@ void via_qmk_audio_command(uint8_t *data, uint8_t length);
void via_qmk_audio_set_value(uint8_t *data);
void via_qmk_audio_get_value(uint8_t *data);
void via_qmk_audio_save(void);
-#endif \ No newline at end of file
+#endif
diff --git a/quantum/wear_leveling/tests/rules.mk b/quantum/wear_leveling/tests/rules.mk
index 4d7a964049..f5f36e5b6e 100644
--- a/quantum/wear_leveling/tests/rules.mk
+++ b/quantum/wear_leveling/tests/rules.mk
@@ -63,4 +63,4 @@ wear_leveling_8byte_SRC := \
$(wear_leveling_common_SRC) \
$(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_8byte.cpp
wear_leveling_8byte_INC := \
- $(wear_leveling_common_INC) \ No newline at end of file
+ $(wear_leveling_common_INC)
diff --git a/quantum/wear_leveling/wear_leveling.c b/quantum/wear_leveling/wear_leveling.c
index 429df45df5..258f074a5c 100644
--- a/quantum/wear_leveling/wear_leveling.c
+++ b/quantum/wear_leveling/wear_leveling.c
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include "fnv.h"
#include "wear_leveling.h"
+#include "wear_leveling_drivers.h"
#include "wear_leveling_internal.h"
/*
diff --git a/quantum/wear_leveling/wear_leveling_drivers.h b/quantum/wear_leveling/wear_leveling_drivers.h
new file mode 100644
index 0000000000..e81f833fd1
--- /dev/null
+++ b/quantum/wear_leveling/wear_leveling_drivers.h
@@ -0,0 +1,13 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#if defined(WEAR_LEVELING_EMBEDDED_FLASH)
+# include "wear_leveling/wear_leveling_efl_config.h"
+#elif defined(WEAR_LEVELING_SPI_FLASH)
+# include "wear_leveling/wear_leveling_flash_spi_config.h"
+#elif defined(WEAR_LEVELING_RP2040_FLASH)
+# include "wear_leveling/wear_leveling_rp2040_flash_config.h"
+#elif defined(WEAR_LEVELING_LEGACY)
+# include "wear_leveling/wear_leveling_legacy_config.h"
+#endif
diff --git a/quantum/wear_leveling/wear_leveling_internal.h b/quantum/wear_leveling/wear_leveling_internal.h
index e83f9b22ea..c590f42235 100644
--- a/quantum/wear_leveling/wear_leveling_internal.h
+++ b/quantum/wear_leveling/wear_leveling_internal.h
@@ -2,9 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
-#ifdef __cplusplus
-# define _Static_assert static_assert
-#endif
+#include "compiler_support.h"
#include <stdint.h>
#include <string.h>
@@ -62,9 +60,9 @@ typedef uint64_t backing_store_int_t;
#endif // WEAR_LEVELING_ASSERTS
// Compile-time validation of configurable options
-_Static_assert(WEAR_LEVELING_BACKING_SIZE >= (WEAR_LEVELING_LOGICAL_SIZE * 2), "Total backing size must be at least twice the size of the logical size");
-_Static_assert(WEAR_LEVELING_LOGICAL_SIZE % BACKING_STORE_WRITE_SIZE == 0, "Logical size must be a multiple of write size");
-_Static_assert(WEAR_LEVELING_BACKING_SIZE % WEAR_LEVELING_LOGICAL_SIZE == 0, "Backing size must be a multiple of logical size");
+STATIC_ASSERT(WEAR_LEVELING_BACKING_SIZE >= (WEAR_LEVELING_LOGICAL_SIZE * 2), "Total backing size must be at least twice the size of the logical size");
+STATIC_ASSERT(WEAR_LEVELING_LOGICAL_SIZE % BACKING_STORE_WRITE_SIZE == 0, "Logical size must be a multiple of write size");
+STATIC_ASSERT(WEAR_LEVELING_BACKING_SIZE % WEAR_LEVELING_LOGICAL_SIZE == 0, "Backing size must be a multiple of logical size");
// Backing Store API, to be implemented elsewhere by flash driver etc.
bool backing_store_init(void);
@@ -86,7 +84,7 @@ typedef union write_log_entry_t {
uint8_t raw8[8];
} write_log_entry_t;
-_Static_assert(sizeof(write_log_entry_t) == 8, "Wear leveling write log entry size was not 8");
+STATIC_ASSERT(sizeof(write_log_entry_t) == 8, "Wear leveling write log entry size was not 8");
/**
* Log entry type discriminator.
@@ -104,7 +102,7 @@ enum {
LOG_ENTRY_TYPES
};
-_Static_assert(LOG_ENTRY_TYPES <= (1 << 2), "Too many log entry types to fit into 2 bits of storage");
+STATIC_ASSERT(LOG_ENTRY_TYPES <= (1 << 2), "Too many log entry types to fit into 2 bits of storage");
#define BITMASK_FOR_BITCOUNT(n) ((1 << (n)) - 1)