Browse Source

Merge remote-tracking branch 'upstream/develop' into xap

Nick Brassel 1 year ago
parent
commit
0d6dbe4a5f
46 changed files with 641 additions and 272 deletions
  1. 1 1
      .clangd
  2. 75 74
      builddefs/common_features.mk
  3. 1 0
      builddefs/generic_features.mk
  4. 1 1
      data/schemas/keyboard.jsonschema
  5. 1 0
      docs/config_options.md
  6. 0 21
      drivers/bluetooth/bluefruit_le.cpp
  7. 0 4
      drivers/bluetooth/bluefruit_le.h
  8. 0 70
      drivers/bluetooth/outputselect.c
  9. 10 14
      drivers/bluetooth/outputselect.h
  10. 0 1
      keyboards/anavi/macropad8/keyboard.json
  11. 5 0
      keyboards/handwired/footy/config.h
  12. 35 0
      keyboards/handwired/footy/keyboard.json
  13. 10 0
      keyboards/handwired/footy/keymaps/default/keymap.json
  14. 15 0
      keyboards/handwired/footy/keymaps/handy/keymap.json
  15. 15 0
      keyboards/handwired/footy/keymaps/superwhisper/keymap.json
  16. 42 0
      keyboards/handwired/footy/readme.md
  17. 2 3
      keyboards/handwired/promethium/config.h
  18. 8 9
      keyboards/handwired/promethium/promethium.c
  19. 1 1
      keyboards/handwired/promethium/rules.mk
  20. 0 1
      keyboards/matrix/falcon/config.h
  21. 0 18
      keyboards/tokyokeyboard/alix40/config.h
  22. 0 1
      keyboards/ymdk/id75/f103/keyboard.json
  23. 2 0
      keyboards/ymdk/id75/info.json
  24. 4 0
      keyboards/ymdk/id75/readme.md
  25. 20 0
      keyboards/ymdk/id75/rp2040/config.h
  26. 12 0
      keyboards/ymdk/id75/rp2040/keyboard.json
  27. 1 1
      lib/pico-sdk
  28. 16 10
      lib/python/qmk/cli/doctor/check.py
  29. 11 0
      lib/python/qmk/cli/lint.py
  30. 147 0
      quantum/connection/connection.c
  31. 110 0
      quantum/connection/connection.h
  32. 0 1
      quantum/dynamic_keymap.c
  33. 33 11
      quantum/eeconfig.c
  34. 6 0
      quantum/eeconfig.h
  35. 6 0
      quantum/keyboard.c
  36. 0 1
      quantum/led_matrix/led_matrix.c
  37. 13 0
      quantum/nvm/eeprom/nvm_eeconfig.c
  38. 2 0
      quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h
  39. 6 0
      quantum/nvm/nvm_eeconfig.h
  40. 17 7
      quantum/process_keycode/process_connection.c
  41. 2 2
      quantum/quantum.c
  42. 0 1
      quantum/rgb_matrix/rgb_matrix.c
  43. 0 1
      quantum/unicode/unicode.c
  44. 0 1
      quantum/via.c
  45. 4 13
      tmk_core/protocol/chibios/chibios.c
  46. 7 4
      tmk_core/protocol/host.c

+ 1 - 1
.clangd

@@ -1,4 +1,4 @@
 CompileFlags:
   Add: [-Wno-unknown-attributes, -Wno-maybe-uninitialized, -Wno-unknown-warning-option]
-  Remove: [-W*, -mmcu=*, -mcpu=*, -mfpu=*, -mfloat-abi=*, -mno-unaligned-access, -mno-thumb-interwork, -mcall-prologues]
+  Remove: [-W*, -mmcu=*, -mcpu=*, -mfpu=*, -mfloat-abi=*, -mno-unaligned-access, -mno-thumb-interwork, -mcall-prologues, -D__has_include*]
   Compiler: clang

+ 75 - 74
builddefs/common_features.mk

@@ -175,80 +175,82 @@ endif
 
 VALID_EEPROM_DRIVER_TYPES := vendor custom transient i2c spi wear_leveling legacy_stm32_flash
 EEPROM_DRIVER ?= vendor
-ifeq ($(filter $(EEPROM_DRIVER),$(VALID_EEPROM_DRIVER_TYPES)),)
+ifneq ($(strip $(EEPROM_DRIVER)),none)
+  ifeq ($(filter $(EEPROM_DRIVER),$(VALID_EEPROM_DRIVER_TYPES)),)
   $(call CATASTROPHIC_ERROR,Invalid EEPROM_DRIVER,EEPROM_DRIVER="$(EEPROM_DRIVER)" is not a valid EEPROM driver)
-else
-  OPT_DEFS += -DEEPROM_ENABLE
-  COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/eeprom
-  COMMON_VPATH += $(DRIVER_PATH)/eeprom
-  COMMON_VPATH += $(PLATFORM_COMMON_DIR)
-  ifeq ($(strip $(EEPROM_DRIVER)), custom)
-    # Custom EEPROM implementation -- only needs to implement init/erase/read_block/write_block
-    OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_CUSTOM
-    SRC += eeprom_driver.c
-  else ifeq ($(strip $(EEPROM_DRIVER)), wear_leveling)
-    # Wear-leveling EEPROM implementation
-    OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING
-    SRC += eeprom_driver.c eeprom_wear_leveling.c
-  else ifeq ($(strip $(EEPROM_DRIVER)), i2c)
-    # External I2C EEPROM implementation
-    OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_I2C
-    I2C_DRIVER_REQUIRED = yes
-    SRC += eeprom_driver.c eeprom_i2c.c
-  else ifeq ($(strip $(EEPROM_DRIVER)), spi)
-    # External SPI EEPROM implementation
-    OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_SPI
-    SPI_DRIVER_REQUIRED = yes
-    SRC += eeprom_driver.c eeprom_spi.c
-  else ifeq ($(strip $(EEPROM_DRIVER)), legacy_stm32_flash)
-    # STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)
-    OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_LEGACY_EMULATED_FLASH
-    COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash
-    COMMON_VPATH += $(DRIVER_PATH)/flash
-    SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c
-  else ifeq ($(strip $(EEPROM_DRIVER)), transient)
-    # Transient EEPROM implementation -- no data storage but provides runtime area for it
-    OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT
-    SRC += eeprom_driver.c eeprom_transient.c
-  else ifeq ($(strip $(EEPROM_DRIVER)), vendor)
-    # Vendor-implemented EEPROM
-    OPT_DEFS += -DEEPROM_VENDOR
-    ifeq ($(PLATFORM),AVR)
-      # Automatically provided by avr-libc, nothing required
-    else ifeq ($(PLATFORM),CHIBIOS)
-      ifneq ($(filter %_STM32F072xB %_STM32F042x6, $(MCU_SERIES)_$(MCU_LDSCRIPT)),)
-        # STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)
-        OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_LEGACY_EMULATED_FLASH
-        COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash
-        COMMON_VPATH += $(DRIVER_PATH)/flash
-        SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c
-      else ifneq ($(filter $(MCU_SERIES),STM32F1xx STM32F3xx STM32F4xx STM32L4xx STM32G4xx WB32F3G71xx WB32FQ95xx AT32F415 GD32VF103),)
-        # Wear-leveling EEPROM implementation, backed by MCU flash
-        OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING
-        SRC += eeprom_driver.c eeprom_wear_leveling.c
-        WEAR_LEVELING_DRIVER ?= embedded_flash
-      else ifneq ($(filter $(MCU_SERIES),STM32L0xx STM32L1xx),)
-        # True EEPROM on STM32L0xx, L1xx
-        OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_STM32_L0_L1
-        SRC += eeprom_driver.c eeprom_stm32_L0_L1.c
-      else ifneq ($(filter $(MCU_SERIES),RP2040),)
-        # Wear-leveling EEPROM implementation, backed by RP2040 flash
-        OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING
-        SRC += eeprom_driver.c eeprom_wear_leveling.c
-        WEAR_LEVELING_DRIVER ?= rp2040_flash
-      else ifneq ($(filter $(MCU_SERIES),KL2x K20x),)
-        # Teensy EEPROM implementations
-        OPT_DEFS += -DEEPROM_KINETIS_FLEXRAM
-        SRC += eeprom_kinetis_flexram.c
-      else
-        # Fall back to transient, i.e. non-persistent
-        OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT
-        SRC += eeprom_driver.c eeprom_transient.c
+  else
+    OPT_DEFS += -DEEPROM_ENABLE
+    COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/eeprom
+    COMMON_VPATH += $(DRIVER_PATH)/eeprom
+    COMMON_VPATH += $(PLATFORM_COMMON_DIR)
+    ifeq ($(strip $(EEPROM_DRIVER)), custom)
+      # Custom EEPROM implementation -- only needs to implement init/erase/read_block/write_block
+      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_CUSTOM
+      SRC += eeprom_driver.c
+    else ifeq ($(strip $(EEPROM_DRIVER)), wear_leveling)
+      # Wear-leveling EEPROM implementation
+      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING
+      SRC += eeprom_driver.c eeprom_wear_leveling.c
+    else ifeq ($(strip $(EEPROM_DRIVER)), i2c)
+      # External I2C EEPROM implementation
+      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_I2C
+      I2C_DRIVER_REQUIRED = yes
+      SRC += eeprom_driver.c eeprom_i2c.c
+    else ifeq ($(strip $(EEPROM_DRIVER)), spi)
+      # External SPI EEPROM implementation
+      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_SPI
+      SPI_DRIVER_REQUIRED = yes
+      SRC += eeprom_driver.c eeprom_spi.c
+    else ifeq ($(strip $(EEPROM_DRIVER)), legacy_stm32_flash)
+      # STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)
+      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_LEGACY_EMULATED_FLASH
+      COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash
+      COMMON_VPATH += $(DRIVER_PATH)/flash
+      SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c
+    else ifeq ($(strip $(EEPROM_DRIVER)), transient)
+      # Transient EEPROM implementation -- no data storage but provides runtime area for it
+      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT
+      SRC += eeprom_driver.c eeprom_transient.c
+    else ifeq ($(strip $(EEPROM_DRIVER)), vendor)
+      # Vendor-implemented EEPROM
+      OPT_DEFS += -DEEPROM_VENDOR
+      ifeq ($(PLATFORM),AVR)
+        # Automatically provided by avr-libc, nothing required
+      else ifeq ($(PLATFORM),CHIBIOS)
+        ifneq ($(filter %_STM32F072xB %_STM32F042x6, $(MCU_SERIES)_$(MCU_LDSCRIPT)),)
+          # STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)
+          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_LEGACY_EMULATED_FLASH
+          COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash
+          COMMON_VPATH += $(DRIVER_PATH)/flash
+          SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c
+        else ifneq ($(filter $(MCU_SERIES),STM32F1xx STM32F3xx STM32F4xx STM32L4xx STM32G4xx WB32F3G71xx WB32FQ95xx AT32F415 GD32VF103),)
+          # Wear-leveling EEPROM implementation, backed by MCU flash
+          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING
+          SRC += eeprom_driver.c eeprom_wear_leveling.c
+          WEAR_LEVELING_DRIVER ?= embedded_flash
+        else ifneq ($(filter $(MCU_SERIES),STM32L0xx STM32L1xx),)
+          # True EEPROM on STM32L0xx, L1xx
+          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_STM32_L0_L1
+          SRC += eeprom_driver.c eeprom_stm32_L0_L1.c
+        else ifneq ($(filter $(MCU_SERIES),RP2040),)
+          # Wear-leveling EEPROM implementation, backed by RP2040 flash
+          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING
+          SRC += eeprom_driver.c eeprom_wear_leveling.c
+          WEAR_LEVELING_DRIVER ?= rp2040_flash
+        else ifneq ($(filter $(MCU_SERIES),KL2x K20x),)
+          # Teensy EEPROM implementations
+          OPT_DEFS += -DEEPROM_KINETIS_FLEXRAM
+          SRC += eeprom_kinetis_flexram.c
+        else
+          # Fall back to transient, i.e. non-persistent
+          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT
+          SRC += eeprom_driver.c eeprom_transient.c
+        endif
+      else ifeq ($(PLATFORM),TEST)
+        # Test harness "EEPROM"
+        OPT_DEFS += -DEEPROM_TEST_HARNESS
+        SRC += eeprom.c
       endif
-    else ifeq ($(PLATFORM),TEST)
-      # Test harness "EEPROM"
-      OPT_DEFS += -DEEPROM_TEST_HARNESS
-      SRC += eeprom.c
     endif
   endif
 endif
@@ -910,12 +912,11 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes)
     OPT_DEFS += -DBLUETOOTH_ENABLE
     OPT_DEFS += -DBLUETOOTH_$(strip $(shell echo $(BLUETOOTH_DRIVER) | tr '[:lower:]' '[:upper:]'))
     NO_USB_STARTUP_CHECK := yes
+    CONNECTION_ENABLE := yes
     COMMON_VPATH += $(DRIVER_PATH)/bluetooth
-    SRC += outputselect.c process_connection.c
 
     ifeq ($(strip $(BLUETOOTH_DRIVER)), bluefruit_le)
         SPI_DRIVER_REQUIRED = yes
-        ANALOG_DRIVER_REQUIRED = yes
         SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c
         SRC += $(DRIVER_PATH)/bluetooth/bluefruit_le.cpp
     endif

+ 1 - 0
builddefs/generic_features.mk

@@ -25,6 +25,7 @@ GENERIC_FEATURES = \
     CAPS_WORD \
     COMBO \
     COMMAND \
+    CONNECTION \
     CRC \
     DEFERRED_EXEC \
     DIGITIZER \

+ 1 - 1
data/schemas/keyboard.jsonschema

@@ -319,7 +319,7 @@
                     "properties": {
                         "driver": {
                             "type": "string",
-                            "enum": ["custom", "embedded_flash", "legacy", "rp2040_flash", "spi_flash"]
+                            "enum": ["none", "custom", "embedded_flash", "legacy", "rp2040_flash", "spi_flash"]
                         },
                         "backing_size": {"$ref": "qmk.definitions.v1#/unsigned_int"},
                         "logical_size": {"$ref": "qmk.definitions.v1#/unsigned_int"}

+ 1 - 0
docs/config_options.md

@@ -401,6 +401,7 @@ This is a [make](https://www.gnu.org/software/make/manual/make.html) file that i
   * `atmel-dfu`
   * `lufa-dfu`
   * `qmk-dfu`
+  * `qmk-hid`
   * `halfkay`
   * `caterina`
   * `bootloadhid`

+ 0 - 21
drivers/bluetooth/bluefruit_le.cpp

@@ -32,13 +32,8 @@
 #    define BLUEFRUIT_LE_SCK_DIVISOR 2 // 4MHz SCK/8MHz CPU, calculated for Feather 32U4 BLE
 #endif
 
-#define SAMPLE_BATTERY
 #define ConnectionUpdateInterval 1000 /* milliseconds */
 
-#ifndef BATTERY_LEVEL_PIN
-#    define BATTERY_LEVEL_PIN B5
-#endif
-
 static struct {
     bool is_connected;
     bool initialized;
@@ -48,10 +43,6 @@ static struct {
 #define UsingEvents 2
     bool event_flags;
 
-#ifdef SAMPLE_BATTERY
-    uint16_t last_battery_update;
-    uint32_t vbat;
-#endif
     uint16_t last_connection_update;
 } state;
 
@@ -549,14 +540,6 @@ void bluefruit_le_task(void) {
             set_connected(atoi(resbuf));
         }
     }
-
-#ifdef SAMPLE_BATTERY
-    if (timer_elapsed(state.last_battery_update) > BatteryUpdateInterval && resp_buf.empty()) {
-        state.last_battery_update = timer_read();
-
-        state.vbat = analogReadPin(BATTERY_LEVEL_PIN);
-    }
-#endif
 }
 
 static bool process_queue_item(struct queue_item *item, uint16_t timeout) {
@@ -655,10 +638,6 @@ void bluefruit_le_send_mouse(report_mouse_t *report) {
     }
 }
 
-uint32_t bluefruit_le_read_battery_voltage(void) {
-    return state.vbat;
-}
-
 bool bluefruit_le_set_mode_leds(bool on) {
     if (!state.configured) {
         return false;

+ 0 - 4
drivers/bluetooth/bluefruit_le.h

@@ -45,10 +45,6 @@ extern void bluefruit_le_send_consumer(uint16_t usage);
  * change. */
 extern void bluefruit_le_send_mouse(report_mouse_t *report);
 
-/* Compute battery voltage by reading an analog pin.
- * Returns the integer number of millivolts */
-extern uint32_t bluefruit_le_read_battery_voltage(void);
-
 extern bool bluefruit_le_set_mode_leds(bool on);
 extern bool bluefruit_le_set_power_level(int8_t level);
 

+ 0 - 70
drivers/bluetooth/outputselect.c

@@ -1,70 +0,0 @@
-/*
-Copyright 2017 Priyadi Iman Nurcahyo
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "outputselect.h"
-#include "usb_util.h"
-
-#ifdef BLUETOOTH_BLUEFRUIT_LE
-#    include "bluefruit_le.h"
-#endif
-
-uint8_t desired_output = OUTPUT_DEFAULT;
-
-/** \brief Set Output
- *
- * FIXME: Needs doc
- */
-void set_output(uint8_t output) {
-    set_output_user(output);
-    desired_output = output;
-}
-
-/** \brief Set Output User
- *
- * FIXME: Needs doc
- */
-__attribute__((weak)) void set_output_user(uint8_t output) {}
-
-/** \brief Auto Detect Output
- *
- * FIXME: Needs doc
- */
-uint8_t auto_detect_output(void) {
-    if (usb_connected_state()) {
-        return OUTPUT_USB;
-    }
-
-#ifdef BLUETOOTH_BLUEFRUIT_LE
-    if (bluefruit_le_is_connected()) {
-        return OUTPUT_BLUETOOTH;
-    }
-#endif
-
-#ifdef BLUETOOTH_ENABLE
-    return OUTPUT_BLUETOOTH; // should check if BT is connected here
-#endif
-
-    return OUTPUT_NONE;
-}
-
-/** \brief Where To Send
- *
- * FIXME: Needs doc
- */
-uint8_t where_to_send(void) {
-    if (desired_output == OUTPUT_AUTO) {
-        return auto_detect_output();
-    }
-    return desired_output;
-}

+ 10 - 14
drivers/bluetooth/outputselect.h

@@ -14,21 +14,17 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #pragma once
 
-#include <stdint.h>
+#include "connection.h"
 
-enum outputs {
-    OUTPUT_AUTO,
+// DEPRECATED - DO NOT USE
 
-    OUTPUT_NONE,
-    OUTPUT_USB,
-    OUTPUT_BLUETOOTH
-};
+#define OUTPUT_AUTO CONNECTION_HOST_AUTO
+#define OUTPUT_NONE CONNECTION_HOST_NONE
+#define OUTPUT_USB CONNECTION_HOST_USB
+#define OUTPUT_BLUETOOTH CONNECTION_HOST_BLUETOOTH
 
-#ifndef OUTPUT_DEFAULT
-#    define OUTPUT_DEFAULT OUTPUT_AUTO
-#endif
+#define set_output connection_set_host_noeeprom
+#define where_to_send connection_get_host
+#define auto_detect_output connection_auto_detect_host
 
-void    set_output(uint8_t output);
-void    set_output_user(uint8_t output);
-uint8_t auto_detect_output(void);
-uint8_t where_to_send(void);
+void set_output_user(uint8_t output);

+ 0 - 1
keyboards/anavi/macropad8/keyboard.json

@@ -23,7 +23,6 @@
       "rainbow_swirl": true,
       "snake": true,
       "knight": true,
-      "christmas": true,
       "static_gradient": true,
       "rgb_test": true,
       "alternating": true,

+ 5 - 0
keyboards/handwired/footy/config.h

@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 500U

+ 35 - 0
keyboards/handwired/footy/keyboard.json

@@ -0,0 +1,35 @@
+{
+    "manufacturer": "CJ Pais",
+    "keyboard_name": "Footy",
+    "maintainer": "cjpais",
+    "bootloader": "rp2040",
+    "diode_direction": "COL2ROW",
+    "features": {
+        "bootmagic": true,
+        "extrakey": true,
+        "mousekey": true
+    },
+    "bootmagic": {
+        "matrix": [0, 1]
+    },
+    "matrix_pins": {
+        "cols": ["GP11", "GP10", "GP15"],
+        "rows": ["GP14"]
+    },
+    "processor": "RP2040",
+    "url": "https://workshop.cjpais.com/projects/footy",
+    "usb": {
+        "device_version": "1.0.0",
+        "pid": "0x0001",
+        "vid": "0x636A"
+    },
+    "layouts": {
+        "LAYOUT": {
+            "layout": [
+                {"matrix": [0, 0], "x": 0, "y": 0},
+                {"matrix": [0, 1], "x": 1, "y": 0},
+                {"matrix": [0, 2], "x": 2, "y": 0}
+            ]
+        }
+    }
+}

+ 10 - 0
keyboards/handwired/footy/keymaps/default/keymap.json

@@ -0,0 +1,10 @@
+{
+    "keyboard": "handwired/footy",
+    "keymap": "default",
+    "layout": "LAYOUT",
+    "layers": [
+       [
+        "KC_A", "KC_B", "KC_C"
+       ]
+    ]
+}

+ 15 - 0
keyboards/handwired/footy/keymaps/handy/keymap.json

@@ -0,0 +1,15 @@
+{
+    "keyboard": "handwired/footy",
+    "keymap": "handy",
+    "layout": "LAYOUT",
+    "layers": [
+       [
+        "KC_NO", "MT(MOD_RCTL | MOD_RGUI, KC_NO)", "KC_NO"
+       ]
+    ],
+    "config": {
+        "tapping": {
+            "term": 50
+        }
+    }
+}

+ 15 - 0
keyboards/handwired/footy/keymaps/superwhisper/keymap.json

@@ -0,0 +1,15 @@
+{
+    "keyboard": "handwired/footy",
+    "keymap": "superwhisper",
+    "layout": "LAYOUT",
+    "layers": [
+       [
+        "KC_NO", "LOPT(KC_SPACE)", "KC_NO"
+       ]
+    ],
+    "config": {
+        "tapping": {
+            "term": 50
+        }
+    }
+}

+ 42 - 0
keyboards/handwired/footy/readme.md

@@ -0,0 +1,42 @@
+# Footy
+
+![Footy Foot Pedal](https://i.imgur.com/ZdvlgRB.jpeg)
+
+*Footy is a foot pedal intended to be used with a speech to text application like 'handy' or superwhisper. Initially developed as an assistive technology device for those with limited typing abilities.*
+
+* Keyboard Maintainer: [CJ Pais](https://github.com/cjpais)
+* Hardware Supported: [Keebio Stampy](https://keeb.io/products/stampy-rp2040-usb-c-controller-board-for-handwiring)
+* Hardware Availability: [3D printed case](https://makerworld.com/en/models/1185240-footy)
+
+Make example for this keyboard (after setting up your build environment):
+
+    make handwired/footy:default
+
+Flashing example for this keyboard (enter the bootloader first):
+
+    make handwired/footy:default:flash
+    
+or drag and drop .uf2 file on the removable storage
+
+See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
+
+## Bootloader
+
+Enter the bootloader:
+
+* **Physical reset button**: Briefly double press the button on the back of the PCB
+
+## Keymaps
+
+Right now there are two primary keymaps, one for 'handy' and one for 'superwhisper'.
+
+Handy's default keymap is for the center switch to be RCTRL + RGUI.
+
+Superwhisper's default keymap is for the center switch to be LOPT + SPACE.
+
+You can build either of these firmware images by running the following commands:
+
+```
+make handwired/footy:handy
+make handwired/footy:superwhisper
+```

+ 2 - 3
keyboards/handwired/promethium/config.h

@@ -63,9 +63,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //#define NO_ACTION_ONESHOT
 
 #define PS2_MOUSE_INIT_DELAY 2000
-#define BATTERY_POLL         30000
-#define MAX_VOLTAGE          4.2
-#define MIN_VOLTAGE          3.2
+
+#define BATTERY_PIN B5
 
 #ifndef __ASSEMBLER__  // assembler doesn't like enum in .h file
 enum led_sequence {

+ 8 - 9
keyboards/handwired/promethium/promethium.c

@@ -1,16 +1,15 @@
-#include "promethium.h"
-#include "analog.h"
+#include "keyboard.h"
 #include "timer.h"
-#include "matrix.h"
-#include "bluefruit_le.h"
+#include "battery.h"
 
-// cubic fit {3.3, 0}, {3.5, 2.9}, {3.6, 5}, {3.7, 8.6}, {3.8, 36},  {3.9, 62}, {4.0, 73}, {4.05, 83}, {4.1, 89}, {4.15, 94}, {4.2, 100}
+#ifndef BATTERY_POLL
+#    define BATTERY_POLL 30000
+#endif
 
 uint8_t battery_level(void) {
-    float voltage = bluefruit_le_read_battery_voltage() * 2 * 3.3 / 1024;
-    if (voltage < MIN_VOLTAGE) return 0;
-    if (voltage > MAX_VOLTAGE) return 255;
-    return (voltage - MIN_VOLTAGE) / (MAX_VOLTAGE - MIN_VOLTAGE) * 255;
+    // maintain legacy behaviour and scale 0-100 percent to 0-255
+    uint16_t percent = battery_get_percent();
+    return  (percent * 255) / 100;
 }
 
 __attribute__ ((weak))

+ 1 - 1
keyboards/handwired/promethium/rules.mk

@@ -5,7 +5,7 @@ PS2_DRIVER = interrupt
 CUSTOM_MATRIX = yes
 
 WS2812_DRIVER_REQUIRED = yes
-ANALOG_DRIVER_REQUIRED = yes
+BATTERY_DRIVER_REQUIRED = yes
 
 SRC += rgbsps.c
 SRC += matrix.c

+ 0 - 1
keyboards/matrix/falcon/config.h

@@ -29,4 +29,3 @@
 //pin setting
 #define LED_POWER_PIN       D5
 #define CHG_EN_PIN          E6
-#define BATTERY_LEVEL_PIN   F0

+ 0 - 18
keyboards/tokyokeyboard/alix40/config.h

@@ -1,18 +0,0 @@
-/*
-Copyright 2021 quadcube <james@quadcube.xyz>
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#pragma once
-
-/* Bluetooth */
-#define BATTERY_LEVEL_PIN   B6

+ 0 - 1
keyboards/ymdk/id75/f103/keyboard.json

@@ -1,5 +1,4 @@
 {
-    "manufacturer": "YMDK",
     "bootloader": "uf2boot",
     "matrix_pins": {
         "cols": ["A10", "A9", "A8", "B15", "B14", "B13", "B12", "A5", "A6", "A4", "A3", "A2", "A1", "A0", "A15"],

+ 2 - 0
keyboards/ymdk/id75/info.json

@@ -1,6 +1,8 @@
 {
+    "manufacturer": "YMDK",
     "keyboard_name": "Idobao x YMDK ID75",
     "maintainer": "qmk",
+    "bootloader_instructions": "Press the button on the back of the PCB twice in quick succession.",
     "diode_direction": "ROW2COL",
     "features": {
         "bootmagic": true,

+ 4 - 0
keyboards/ymdk/id75/readme.md

@@ -8,15 +8,18 @@ A 75-key, 5-row ortholinear keyboard with per-key and underglow RGB LEDs.
 * Hardware Supported: [Idobao x YMDK ID75](https://www.aliexpress.com/item/3256804537842097.html). **This is not the same PCB as `idobao/id75` or `ymdk/ymd75`.**
   This keyboard has had multiple PCB revisions, some of which may not work with the firmware in this repository. **Check your PCB before flashing.**
   * `f103`: (Geehy APM32F103CBT6, uf2boot)
+  * `rp2040`: (RP2040, rp2040)
 * Hardware Availability: [YMDK](https://ymdkey.com/products/id75-75-keys-ortholinear-layout-qmk-anodized-aluminum-case-plate-hot-swappable-hot-swap-type-c-pcb-mechanical-keyboard-kit), [AliExpress (YMDK Store)](https://www.aliexpress.com/item/2255800125183974.html), [Amazon](https://www.amazon.com/Ortholinear-Anodized-Aluminum-hot-swappable-Mechanical/dp/B07ZQ8CD88)
 
 Make example for this keyboard (after setting up your build environment):
 
     make ymdk/id75/f103:default
+    make ymdk/id75/rp2040:default
 
 Flashing example for this keyboard:
 
     make ymdk/id75/f103:default:flash
+    make ymdk/id75/rp2040:default:flash
 
 See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
 
@@ -30,3 +33,4 @@ Enter the bootloader in 3 ways:
 
 After entering the bootloader through one of the three methods above, the keyboard will appear as a USB mass storage device. If the CLI is unable to find this device, the compiled `.uf2` file can be manually copied to it. The keyboard will reboot on completion with the new firmware loaded.
 - `f103`: The volume name is `MT.KEY`.
+- `rp2040`: The volume name is `RPI-RP2`.

+ 20 - 0
keyboards/ymdk/id75/rp2040/config.h

@@ -0,0 +1,20 @@
+/* Copyright 2021 Mike Tsao
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 500U

+ 12 - 0
keyboards/ymdk/id75/rp2040/keyboard.json

@@ -0,0 +1,12 @@
+{
+    "bootloader": "rp2040",
+    "matrix_pins": {
+        "cols": ["GP26", "GP27", "GP4", "GP5", "GP1", "GP23", "GP22", "GP21", "GP28", "GP3", "GP7", "GP12", "GP13", "GP14", "GP15"],
+        "rows": ["GP8", "GP6", "GP19", "GP20", "GP18"]
+    },
+    "processor": "RP2040",
+    "ws2812": {
+        "driver": "vendor",
+        "pin": "GP2"
+    }
+}

+ 1 - 1
lib/pico-sdk

@@ -1 +1 @@
-Subproject commit a3398d8d3a772f37fef44a74743a1de69770e9c2
+Subproject commit d0c5cac430cc955b65efa0e899748853d9a80928

+ 16 - 10
lib/python/qmk/cli/doctor/check.py

@@ -54,10 +54,13 @@ def _check_arm_gcc_installation():
     """Returns OK if the arm-none-eabi-gcc is fully installed and can produce binaries.
     """
     with TemporaryDirectory() as temp_dir:
-        temp_file = Path(temp_dir) / 'test.elf'
+        temp_in = Path(temp_dir) / 'test.c'
+        temp_out = Path(temp_dir) / 'test.elf'
 
-        args = ['arm-none-eabi-gcc', '-mcpu=cortex-m0', '-mthumb', '-mno-thumb-interwork', '--specs=nosys.specs', '--specs=nano.specs', '-x', 'c', '-o', str(temp_file), '-']
-        result = cli.run(args, stdin=None, stdout=None, stderr=None, input='#include <newlib.h>\nint main() { return __NEWLIB__ * __NEWLIB_MINOR__ * __NEWLIB_PATCHLEVEL__; }')
+        temp_in.write_text('#include <newlib.h>\nint main() { return __NEWLIB__ * __NEWLIB_MINOR__ * __NEWLIB_PATCHLEVEL__; }', encoding='utf-8')
+
+        args = ['arm-none-eabi-gcc', '-mcpu=cortex-m0', '-mthumb', '-mno-thumb-interwork', '--specs=nosys.specs', '--specs=nano.specs', '-x', 'c', '-o', str(temp_out), str(temp_in)]
+        result = cli.run(args, stdout=None, stderr=None)
         if result.returncode == 0:
             cli.log.info('Successfully compiled using arm-none-eabi-gcc')
         else:
@@ -65,8 +68,8 @@ def _check_arm_gcc_installation():
             cli.log.error(f'Command: {" ".join(args)}')
             return CheckStatus.ERROR
 
-        args = ['arm-none-eabi-size', str(temp_file)]
-        result = cli.run(args, stdin=None, stdout=None, stderr=None)
+        args = ['arm-none-eabi-size', str(temp_out)]
+        result = cli.run(args, stdout=None, stderr=None)
         if result.returncode == 0:
             cli.log.info('Successfully tested arm-none-eabi-binutils using arm-none-eabi-size')
         else:
@@ -91,10 +94,13 @@ def _check_avr_gcc_installation():
     """Returns OK if the avr-gcc is fully installed and can produce binaries.
     """
     with TemporaryDirectory() as temp_dir:
-        temp_file = Path(temp_dir) / 'test.elf'
+        temp_in = Path(temp_dir) / 'test.c'
+        temp_out = Path(temp_dir) / 'test.elf'
+
+        temp_in.write_text('int main() { return 0; }', encoding='utf-8')
 
-        args = ['avr-gcc', '-mmcu=atmega32u4', '-x', 'c', '-o', str(temp_file), '-']
-        result = cli.run(args, stdin=None, stdout=None, stderr=None, input='int main() { return 0; }')
+        args = ['avr-gcc', '-mmcu=atmega32u4', '-x', 'c', '-o', str(temp_out), str(temp_in)]
+        result = cli.run(args, stdout=None, stderr=None)
         if result.returncode == 0:
             cli.log.info('Successfully compiled using avr-gcc')
         else:
@@ -102,8 +108,8 @@ def _check_avr_gcc_installation():
             cli.log.error(f'Command: {" ".join(args)}')
             return CheckStatus.ERROR
 
-        args = ['avr-size', str(temp_file)]
-        result = cli.run(args, stdin=None, stdout=None, stderr=None)
+        args = ['avr-size', str(temp_out)]
+        result = cli.run(args, stdout=None, stderr=None)
         if result.returncode == 0:
             cli.log.info('Successfully tested avr-binutils using avr-size')
         else:

+ 11 - 0
lib/python/qmk/cli/lint.py

@@ -171,6 +171,14 @@ def _handle_invalid_features(kb, info):
     return ok
 
 
+def _handle_invalid_config(kb, info):
+    """Check for invalid keyboard level config
+    """
+    if info.get('url') == "":
+        cli.log.warning(f'{kb}: Invalid keyboard level config detected - Optional field "url" should not be empty.')
+    return True
+
+
 def _chibios_conf_includenext_check(target):
     """Check the ChibiOS conf.h for the correct inclusion of the next conf.h
     """
@@ -255,6 +263,9 @@ def keyboard_check(kb):  # noqa C901
     if not _handle_invalid_features(kb, kb_info):
         ok = False
 
+    if not _handle_invalid_config(kb, kb_info):
+        ok = False
+
     invalid_files = git_get_ignored_files(f'keyboards/{kb}/')
     for file in invalid_files:
         if 'keymap' in file:

+ 147 - 0
quantum/connection/connection.c

@@ -0,0 +1,147 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "connection.h"
+#include "eeconfig.h"
+#include "usb_util.h"
+#include "util.h"
+
+// ======== 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) {}
+// ========
+
+#ifdef BLUETOOTH_ENABLE
+#    ifdef BLUETOOTH_BLUEFRUIT_LE
+#        include "bluefruit_le.h"
+#        define bluetooth_is_connected() bluefruit_le_is_connected()
+#    else
+// TODO: drivers should check if BT is connected here
+#        define bluetooth_is_connected() true
+#    endif
+#endif
+
+#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;
+}

+ 110 - 0
quantum/connection/connection.h

@@ -0,0 +1,110 @@
+// Copyright 2025 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#include <stdint.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);

+ 0 - 1
quantum/dynamic_keymap.c

@@ -18,7 +18,6 @@
 #include "dynamic_keymap.h"
 #include "keymap_introspection.h"
 #include "action.h"
-#include "eeprom.h"
 #include "send_string.h"
 #include "keycodes.h"
 #include "nvm_eeconfig.h"

+ 33 - 11
quantum/eeconfig.c

@@ -2,16 +2,11 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include "debug.h"
-#include "eeprom.h"
 #include "eeconfig.h"
 #include "action_layer.h"
 #include "nvm_eeconfig.h"
 #include "keycode_config.h"
 
-#ifdef EEPROM_DRIVER
-#    include "eeprom_driver.h"
-#endif // EEPROM_DRIVER
-
 #ifdef BACKLIGHT_ENABLE
 #    include "backlight.h"
 #endif // BACKLIGHT_ENABLE
@@ -40,6 +35,10 @@
 #    include "haptic.h"
 #endif // HAPTIC_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);
@@ -105,8 +104,8 @@ void eeconfig_init_quantum(void) {
 #endif // AUDIO_ENABLE
 
 #ifdef RGBLIGHT_ENABLE
-    rgblight_config_t rgblight_config = {0};
-    eeconfig_update_rgblight(&rgblight_config);
+    extern void eeconfig_update_rgblight_default(void);
+    eeconfig_update_rgblight_default();
 #endif // RGBLIGHT_ENABLE
 
 #ifdef UNICODE_COMMON_ENABLE
@@ -119,13 +118,13 @@ void eeconfig_init_quantum(void) {
 #endif // STENO_ENABLE
 
 #ifdef RGB_MATRIX_ENABLE
-    rgb_config_t rgb_matrix_config = {0};
-    eeconfig_update_rgb_matrix(&rgb_matrix_config);
+    extern void eeconfig_update_rgb_matrix_default(void);
+    eeconfig_update_rgb_matrix_default();
 #endif
 
 #ifdef LED_MATRIX_ENABLE
-    led_eeconfig_t led_matrix_config = {0};
-    eeconfig_update_led_matrix(&led_matrix_config);
+    extern void eeconfig_update_led_matrix_default(void);
+    eeconfig_update_led_matrix_default();
 #endif // LED_MATRIX_ENABLE
 
 #ifdef HAPTIC_ENABLE
@@ -134,6 +133,11 @@ void eeconfig_init_quantum(void) {
     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 // (EECONFIG_KB_DATA_SIZE) > 0
@@ -153,6 +157,15 @@ void eeconfig_init_quantum(void) {
 #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
 }
 
 void eeconfig_init(void) {
@@ -306,6 +319,15 @@ void eeconfig_update_haptic(const haptic_config_t *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 nvm_eeconfig_read_handedness();
 }

+ 6 - 0
quantum/eeconfig.h

@@ -131,6 +131,12 @@ void                          eeconfig_read_haptic(haptic_config_t *haptic_confi
 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);
 

+ 6 - 0
quantum/keyboard.c

@@ -146,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) {
@@ -465,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();

+ 0 - 1
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"

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

@@ -41,6 +41,10 @@
 #    include "haptic.h"
 #endif
 
+#ifdef CONNECTION_ENABLE
+#    include "connection.h"
+#endif
+
 void nvm_eeconfig_erase(void) {
 #ifdef EEPROM_DRIVER
     eeprom_driver_format(false);
@@ -196,6 +200,15 @@ void nvm_eeconfig_update_haptic(const haptic_config_t *haptic_config) {
 }
 #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);
 }

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

@@ -27,6 +27,7 @@ typedef struct PACKED {
     };
     uint32_t haptic;
     uint8_t  rgblight_ext;
+    uint8_t  connection;
     uint32_t keymap_hash;
 } eeprom_core_t;
 
@@ -47,6 +48,7 @@ typedef struct PACKED {
 #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))
 #define EECONFIG_KEYMAP_HASH (uint32_t *)(offsetof(eeprom_core_t, keymap_hash))
 
 // Size of EEPROM being used for core data storage

+ 6 - 0
quantum/nvm/nvm_eeconfig.h

@@ -87,6 +87,12 @@ void                          nvm_eeconfig_read_haptic(haptic_config_t *haptic_c
 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);
 

+ 17 - 7
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:

+ 2 - 2
quantum/quantum.c

@@ -20,7 +20,7 @@
 #    include "process_backlight.h"
 #endif
 
-#ifdef BLUETOOTH_ENABLE
+#ifdef CONNECTION_ENABLE
 #    include "process_connection.h"
 #endif
 
@@ -436,7 +436,7 @@ 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)) {

+ 0 - 1
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"

+ 0 - 1
quantum/unicode/unicode.c

@@ -16,7 +16,6 @@
 
 #include "unicode.h"
 
-#include "eeprom.h"
 #include "eeconfig.h"
 #include "action.h"
 #include "action_util.h"

+ 0 - 1
quantum/via.c

@@ -26,7 +26,6 @@
 
 #include "raw_hid.h"
 #include "dynamic_keymap.h"
-#include "eeprom.h"
 #include "eeconfig.h"
 #include "matrix.h"
 #include "timer.h"

+ 4 - 13
tmk_core/protocol/chibios/chibios.c

@@ -134,8 +134,6 @@ void protocol_setup(void) {
     // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
 }
 
-static host_driver_t *driver = NULL;
-
 void protocol_pre_init(void) {
     /* Init USB */
     usb_event_queue_init();
@@ -146,18 +144,11 @@ void protocol_pre_init(void) {
 #endif
 
     /* Wait until USB is active */
-    while (true) {
-#if defined(USB_WAIT_FOR_ENUMERATION)
-        if (USB_DRIVER.state == USB_ACTIVE) {
-            driver = &chibios_driver;
-            break;
-        }
-#else
-        driver = &chibios_driver;
-        break;
-#endif
+#ifdef USB_WAIT_FOR_ENUMERATION
+    while (USB_DRIVER.state != USB_ACTIVE) {
         wait_ms(50);
     }
+#endif
 
     /* Do need to wait here!
      * Otherwise the next print might start a transfer on console EP
@@ -170,7 +161,7 @@ void protocol_pre_init(void) {
 }
 
 void protocol_post_init(void) {
-    host_set_driver(driver);
+    host_set_driver(&chibios_driver);
 }
 
 void protocol_pre_task(void) {

+ 7 - 4
tmk_core/protocol/host.c

@@ -31,8 +31,11 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #endif
 
 #ifdef BLUETOOTH_ENABLE
+#    ifndef CONNECTION_ENABLE
+#        error CONNECTION_ENABLE required and not enabled
+#    endif
+#    include "connection.h"
 #    include "bluetooth.h"
-#    include "outputselect.h"
 #endif
 
 #ifdef NKRO_ENABLE
@@ -74,7 +77,7 @@ led_t host_keyboard_led_state(void) {
 /* send report */
 void host_keyboard_send(report_keyboard_t *report) {
 #ifdef BLUETOOTH_ENABLE
-    if (where_to_send() == OUTPUT_BLUETOOTH) {
+    if (connection_get_host() == CONNECTION_HOST_BLUETOOTH) {
         bluetooth_send_keyboard(report);
         return;
     }
@@ -111,7 +114,7 @@ void host_nkro_send(report_nkro_t *report) {
 
 void host_mouse_send(report_mouse_t *report) {
 #ifdef BLUETOOTH_ENABLE
-    if (where_to_send() == OUTPUT_BLUETOOTH) {
+    if (connection_get_host() == CONNECTION_HOST_BLUETOOTH) {
         bluetooth_send_mouse(report);
         return;
     }
@@ -147,7 +150,7 @@ void host_consumer_send(uint16_t usage) {
     last_consumer_usage = usage;
 
 #ifdef BLUETOOTH_ENABLE
-    if (where_to_send() == OUTPUT_BLUETOOTH) {
+    if (connection_get_host() == CONNECTION_HOST_BLUETOOTH) {
         bluetooth_send_consumer(usage);
         return;
     }