From b46f5e2e46eb51b92bc17f238fb8d65e95c3224b Mon Sep 17 00:00:00 2001
From: Drashna Jael're <drashna@live.com>
Date: Mon, 24 Jun 2024 22:14:59 -0700
Subject: [PATCH] Re-add Oryx support as keyboard code

---
 keyboards/zsa/common/config.h          |  16 ++
 keyboards/zsa/common/features.mk       |  10 +
 keyboards/zsa/common/oryx.c            | 272 +++++++++++++++++++++++++
 keyboards/zsa/common/oryx.h            |  95 +++++++++
 keyboards/zsa/common/rgb_matrix_kb.inc |  20 ++
 keyboards/zsa/ergodox_ez/ergodox_ez.c  |  10 +-
 keyboards/zsa/ergodox_ez/ergodox_ez.h  |   3 +
 keyboards/zsa/ergodox_ez/rules.mk      |   7 +-
 keyboards/zsa/moonlander/moonlander.c  |   7 +-
 keyboards/zsa/moonlander/moonlander.h  |   3 +
 keyboards/zsa/moonlander/rules.mk      |   1 +
 keyboards/zsa/planck_ez/planck_ez.c    |   5 +-
 keyboards/zsa/planck_ez/planck_ez.h    |   3 +
 keyboards/zsa/planck_ez/rules.mk       |   1 +
 keyboards/zsa/voyager/rules.mk         |   2 +
 keyboards/zsa/voyager/voyager.c        |   7 +-
 keyboards/zsa/voyager/voyager.h        |   3 +
 17 files changed, 453 insertions(+), 12 deletions(-)
 create mode 100644 keyboards/zsa/common/config.h
 create mode 100644 keyboards/zsa/common/features.mk
 create mode 100644 keyboards/zsa/common/oryx.c
 create mode 100644 keyboards/zsa/common/oryx.h
 create mode 100644 keyboards/zsa/common/rgb_matrix_kb.inc

diff --git a/keyboards/zsa/common/config.h b/keyboards/zsa/common/config.h
new file mode 100644
index 000000000000..8cc9bb6e4b1c
--- /dev/null
+++ b/keyboards/zsa/common/config.h
@@ -0,0 +1,16 @@
+// Copyright 2024 ZSA Technology Labs, Inc <@zsa>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#ifdef ORYX_ENABLE
+#    ifndef FIRWMARE_VERSION
+#        define FIRMWARE_VERSION u8"default/latest"
+#    endif // FIRMWARE_VERSION
+#    ifndef RAW_USAGE_PAGE
+#        define RAW_USAGE_PAGE 0xFF60
+#    endif // RAW_USAGE_PAGE
+#    ifndef RAW_USAGE_ID
+#        define RAW_USAGE_ID 0x61
+#    endif // RAW_USAGE_ID
+#endif     // ORYX_ENABLE
diff --git a/keyboards/zsa/common/features.mk b/keyboards/zsa/common/features.mk
new file mode 100644
index 000000000000..0b2bee6de9a8
--- /dev/null
+++ b/keyboards/zsa/common/features.mk
@@ -0,0 +1,10 @@
+VPATH += keyboards/zsa/common
+
+ifeq ($(strip $(ORYX_ENABLE)), yes)
+    SRC += keyboards/zsa/common/oryx.c
+    OPT_DEFS += -DORYX_ENABLE
+    POST_CONFIG_H += keyboards/zsa/common/config.h
+    RAW_ENABLE := yes
+    VIA_ENABLE := no
+    RGB_MATRIX_CUSTOM_KB = yes
+endif
diff --git a/keyboards/zsa/common/oryx.c b/keyboards/zsa/common/oryx.c
new file mode 100644
index 000000000000..5cabb6cb82a3
--- /dev/null
+++ b/keyboards/zsa/common/oryx.c
@@ -0,0 +1,272 @@
+// Copyright 2024 ZSA Technology Labs, Inc <@zsa>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <string.h>
+#include "oryx.h"
+
+#ifndef FIRMWARE_VERSION
+#    define FIRMWARE_VERSION u8"default/latest"
+#endif // FIRMWARE_VERSION
+
+rawhid_state_t rawhid_state = {
+    .paired      = false,
+    .rgb_control = false,
+};
+
+uint8_t pairing_input_index = 0;
+
+void raw_hid_send_oryx(uint8_t *data, uint8_t length) {
+    if (!raw_hid_send(data, length)) {
+        rawhid_state.paired = false;
+    }
+}
+
+void oryx_error(uint8_t code) {
+    uint8_t event[RAW_EPSIZE];
+    event[0]            = ORYX_EVT_ERROR;
+    event[1]            = code;
+    raw_hid_send_oryx(event, RAW_EPSIZE);
+}
+
+void oryx_layer_event(void) {
+    uint8_t layer;
+    uint8_t event[RAW_EPSIZE];
+    layer               = get_highest_layer(layer_state);
+    event[0]            = ORYX_EVT_LAYER;
+    event[1]            = layer;
+    event[2]            = ORYX_STOP_BIT;
+    raw_hid_send_oryx(event, sizeof(event));
+}
+
+void pairing_failed_event(void) {
+    rawhid_state.paired = false;
+    uint8_t event[RAW_EPSIZE];
+    event[0] = ORYX_EVT_PAIRING_FAILED;
+    event[1] = ORYX_STOP_BIT;
+    raw_hid_send_oryx(event, sizeof(event));
+}
+
+void pairing_success_event(void) {
+    rawhid_state.paired = true;
+    uint8_t event[RAW_EPSIZE];
+    event[0] = ORYX_EVT_PAIRING_SUCCESS;
+    event[1] = ORYX_STOP_BIT;
+    raw_hid_send_oryx(event, sizeof(event));
+}
+
+void toggle_smart_layer(void) {
+    uint8_t event[RAW_EPSIZE];
+    event[0]            = ORYX_EVT_TOGGLE_SMART_LAYER;
+    event[1]            = ORYX_STOP_BIT;
+    raw_hid_send_oryx(event, sizeof(event));
+}
+
+void trigger_smart_layer(void) {
+    uint8_t event[RAW_EPSIZE];
+    event[0]            = ORYX_EVT_TRIGGER_SMART_LAYER;
+    event[1]            = ORYX_STOP_BIT;
+    raw_hid_send_oryx(event, sizeof(event));
+}
+
+void set_webhid_effect(void) {
+#if defined(RGB_MATRIX_ENABLE) && !defined(PROTOCOL_LUFA)
+    rgb_matrix_mode_noeeprom(RGB_MATRIX_CUSTOM_oryx_webhid_effect);
+    rawhid_state.rgb_control = true;
+#endif
+}
+
+void clear_webhid_effect(void) {
+#if defined(RGB_MATRIX_ENABLE) && !defined(PROTOCOL_LUFA)
+    // Clear the pattern
+    for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
+        webhid_leds[i] = (RGB){.r = 0, .g = 0, .b = 0};
+    }
+    rgb_matrix_reload_from_eeprom();
+    rawhid_state.rgb_control = false;
+#endif
+}
+
+void raw_hid_receive(uint8_t *data, uint8_t length) {
+    uint8_t  command = data[0];
+    uint8_t *param   = &data[1];
+
+    switch (command) {
+        case ORYX_CMD_GET_FW_VERSION: {
+            uint8_t event[RAW_EPSIZE];
+            uint8_t fw_version_size = sizeof(FIRMWARE_VERSION);
+            uint8_t stop[1];
+
+            event[0] = ORYX_EVT_GET_FW_VERSION;
+            stop[0]  = ORYX_STOP_BIT;
+
+            memcpy(event + 1, FIRMWARE_VERSION, fw_version_size);
+            memcpy(event + fw_version_size, stop, 1);
+
+            raw_hid_send_oryx(event, RAW_EPSIZE);
+            break;
+        }
+
+        case ORYX_GET_PROTOCOL_VERSION: {
+            uint8_t event[RAW_EPSIZE];
+            event[0] = ORYX_EVT_GET_PROTOCOL_VERSION;
+            event[1] = ORYX_PROTOCOL_VERSION;
+            event[2] = ORYX_STOP_BIT;
+
+            raw_hid_send_oryx(event, RAW_EPSIZE);
+            break;
+        }
+
+        case ORYX_CMD_PAIRING_INIT:
+            pairing_success_event();
+
+        case ORYX_CMD_PAIRING_VALIDATE:
+            break; // Keeping this for backwards compatibility with older versions of Wally / Keymapp
+
+        case ORYX_SET_LAYER:
+            // The first param's byte is on / off
+            // The second param's byte is the layer number
+            if (rawhid_state.paired == true) {
+                if (param[0] == 0) {
+                    layer_off(param[1]);
+                } else {
+                    layer_move(param[1]);
+                }
+            }
+            break;
+
+        case ORYX_RGB_CONTROL:
+#if defined(RGB_MATRIX_ENABLE) && !defined(PROTOCOL_LUFA)
+            if (param[0] == 0) {
+                clear_webhid_effect();
+            } else {
+                set_webhid_effect();
+            }
+            uint8_t event[RAW_EPSIZE];
+            event[0]            = ORYX_EVT_RGB_CONTROL;
+            event[1]            = rawhid_state.rgb_control;
+            raw_hid_send_oryx(event, RAW_EPSIZE);
+#else
+            oryx_error(ORYX_ERR_RGB_MATRIX_NOT_ENABLED);
+#endif
+            break;
+
+        case ORYX_SET_RGB_LED:
+#if defined(RGB_MATRIX_ENABLE) && !defined(PROTOCOL_LUFA)
+            webhid_leds[param[0]] = (RGB){.r = param[1], .g = param[2], .b = param[3]};
+            if (rawhid_state.rgb_control == false) {
+                set_webhid_effect();
+            }
+#else
+            oryx_error(ORYX_ERR_RGB_MATRIX_NOT_ENABLED);
+#endif
+            break;
+        case ORYX_SET_RGB_LED_ALL:
+#if defined(RGB_MATRIX_ENABLE) && !defined(PROTOCOL_LUFA)
+            for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++) {
+                webhid_leds[i] = (RGB){.r = param[0], .g = param[1], .b = param[2]};
+            }
+            if (rawhid_state.rgb_control == false) {
+                set_webhid_effect();
+            }
+#else
+            oryx_error(ORYX_ERR_RGB_MATRIX_NOT_ENABLED);
+#endif
+            break;
+        case ORYX_SET_STATUS_LED:
+            rawhid_state.status_led_control = true; // Eagerly take control of the status LEDs
+            switch (param[0]) {
+                case 0:
+#ifdef STATUS_LED_1
+                    STATUS_LED_1(param[1]);
+#else
+                    oryx_error(ORYX_ERR_STATUS_LED_OUT_OF_RANGE);
+#endif
+                    break;
+                case 1:
+#ifdef STATUS_LED_2
+                    STATUS_LED_2(param[1]);
+#else
+                    oryx_error(ORYX_ERR_STATUS_LED_OUT_OF_RANGE);
+#endif
+                    break;
+                case 2:
+#ifdef STATUS_LED_3
+                    STATUS_LED_3(param[1]);
+#else
+                    oryx_error(ORYX_ERR_STATUS_LED_OUT_OF_RANGE);
+#endif
+                    break;
+                case 3:
+#ifdef STATUS_LED_4
+                    STATUS_LED_4(param[1]);
+#else
+                    oryx_error(ORYX_ERR_STATUS_LED_OUT_OF_RANGE);
+#endif
+                    break;
+                case 4:
+#ifdef STATUS_LED_5
+                    STATUS_LED_5(param[1]);
+#else
+                    oryx_error(ORYX_ERR_STATUS_LED_OUT_OF_RANGE);
+#endif
+                    break;
+                case 5:
+#ifdef STATUS_LED_6
+                    STATUS_LED_6(param[1]);
+#else
+                    oryx_error(ORYX_ERR_STATUS_LED_OUT_OF_RANGE);
+#endif
+                    break;
+
+                default:
+                    oryx_error(ORYX_ERR_STATUS_LED_OUT_OF_RANGE);
+                    break;
+            }
+            break;
+        case ORYX_UPDATE_BRIGHTNESS: {
+#if defined(RGB_MATRIX_ENABLE) && !defined(PROTOCOL_LUFA)
+            if (param[0]) {
+                rgb_matrix_increase_val_noeeprom();
+            } else {
+                rgb_matrix_decrease_val_noeeprom();
+            }
+#else
+            oryx_error(ORYX_ERR_RGB_MATRIX_NOT_ENABLED);
+#endif
+            break;
+        }
+        case ORYX_STATUS_LED_CONTROL: {
+            rawhid_state.status_led_control = param[0];
+            uint8_t event[RAW_EPSIZE];
+            event[0]            = ORYX_EVT_STATUS_LED_CONTROL;
+            event[1]            = rawhid_state.status_led_control;
+            raw_hid_send_oryx(event, RAW_EPSIZE);
+            break;
+        }
+        default:
+            oryx_error(ORYX_ERR_UNKNOWN_COMMAND);
+    }
+}
+
+bool pre_process_record_kb(uint16_t keycode, keyrecord_t *record) {
+    // While paired, the keyboard sends keystrokes positions to the host
+    if (rawhid_state.paired == true) {
+        uint8_t event[RAW_EPSIZE];
+        event[0]            = record->event.pressed ? ORYX_EVT_KEYDOWN : ORYX_EVT_KEYUP;
+        event[1]            = record->event.key.col;
+        event[2]            = record->event.key.row;
+        event[3]            = ORYX_STOP_BIT;
+        raw_hid_send_oryx(event, sizeof(event));
+    }
+    return true;
+}
+
+void layer_state_set_oryx(layer_state_t state) {
+    if (rawhid_state.paired) {
+        uint8_t event[RAW_EPSIZE];
+        event[0]            = ORYX_EVT_LAYER;
+        event[1]            = get_highest_layer(state);
+        event[2]            = ORYX_STOP_BIT;
+        raw_hid_send_oryx(event, sizeof(event));
+    }
+}
diff --git a/keyboards/zsa/common/oryx.h b/keyboards/zsa/common/oryx.h
new file mode 100644
index 000000000000..86a4b5829e52
--- /dev/null
+++ b/keyboards/zsa/common/oryx.h
@@ -0,0 +1,95 @@
+// Copyright 2024 ZSA Technology Labs, Inc <@zsa>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+/*
+The Oryx Webhid protocol
+
+Each HID packet is a series of bytes.  The first byte is the packet type is the command. The rest of the bytes are the params.
+
+Before sending a packet, the host needs to be paired or should request a pairing code.
+
+The pairing code is a sequence of key positions derived from Oryx's firmware version code stored in the FIRMWARE_VERSION define.
+
+Once the host has paired, it can freely use the commands define in the Oryx_Command_Code enum for which the board will always respond with a Oryx_Event_Code or a Oryx_Error_Code.
+*/
+
+#include "quantum.h"
+#include "raw_hid.h"
+
+#ifndef RAW_ENABLE
+#    error "Raw hid needs to be enabled, please enable it!"
+#endif
+#ifndef RAW_EPSIZE
+#    define RAW_EPSIZE 32
+#endif
+
+#define ORYX_PROTOCOL_VERSION 0x04
+#define ORYX_STOP_BIT -2
+
+enum Oryx_Command_Code {
+    ORYX_CMD_GET_FW_VERSION,
+    ORYX_CMD_PAIRING_INIT,
+    ORYX_CMD_PAIRING_VALIDATE,
+    ORYX_CMD_DISCONNECT,
+    ORYX_SET_LAYER,
+    ORYX_RGB_CONTROL,
+    ORYX_SET_RGB_LED,
+    ORYX_SET_STATUS_LED,
+    ORYX_UPDATE_BRIGHTNESS,
+    ORYX_SET_RGB_LED_ALL,
+    ORYX_STATUS_LED_CONTROL,
+    ORYX_GET_PROTOCOL_VERSION = 0xFE,
+};
+
+enum Oryx_Event_Code {
+    ORYX_EVT_GET_FW_VERSION,
+    ORYX_EVT_PAIRING_INPUT,
+    ORYX_EVT_PAIRING_KEY_INPUT,
+    ORYX_EVT_PAIRING_FAILED,
+    ORYX_EVT_PAIRING_SUCCESS,
+    ORYX_EVT_LAYER,
+    ORYX_EVT_KEYDOWN,
+    ORYX_EVT_KEYUP,
+    ORYX_EVT_RGB_CONTROL,
+    ORYX_EVT_TOGGLE_SMART_LAYER,
+    ORYX_EVT_TRIGGER_SMART_LAYER,
+    ORYX_EVT_STATUS_LED_CONTROL,
+    ORYX_EVT_GET_PROTOCOL_VERSION = 0XFE,
+    ORYX_EVT_ERROR                = 0xFF,
+};
+
+enum Oryx_Error_Code {
+    ORYX_ERR_PAIRING_INIT_FAILED,
+    ORYX_ERR_PAIRING_INPUT_FAILED,
+    ORYX_ERR_PAIRING_KEY_INPUT_FAILED,
+    ORYX_ERR_PAIRING_FAILED,
+    ORYX_ERR_RGB_MATRIX_NOT_ENABLED,
+    ORYX_ERR_STATUS_LED_OUT_OF_RANGE,
+    ORYX_ERR_UNKNOWN_COMMAND = 0xFF,
+};
+
+extern bool oryx_state_live_training_enabled;
+
+typedef struct {
+    bool paired;
+    bool rgb_control;
+    bool status_led_control;
+} rawhid_state_t;
+
+extern rawhid_state_t rawhid_state;
+
+void oryx_error(uint8_t code);
+void pairing_failed_event(void);
+void pairing_succesful_event(void);
+void toggle_smart_layer(void);
+void trigger_smart_layer(void);
+void set_webhid_effect(void);
+
+void oryx_layer_event(void);
+void layer_state_set_oryx(layer_state_t state);
+
+
+#if defined(RGB_MATRIX_ENABLE) && !defined(KEYBOARD_ergodox_ez_glow)
+RGB webhid_leds[RGB_MATRIX_LED_COUNT];
+#endif
diff --git a/keyboards/zsa/common/rgb_matrix_kb.inc b/keyboards/zsa/common/rgb_matrix_kb.inc
new file mode 100644
index 000000000000..b6295deedc46
--- /dev/null
+++ b/keyboards/zsa/common/rgb_matrix_kb.inc
@@ -0,0 +1,20 @@
+#ifdef RGB_MATRIX_ENABLE
+RGB_MATRIX_EFFECT(oryx_webhid_effect)
+#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
+
+extern RGB webhid_leds[RGB_MATRIX_LED_COUNT];
+
+static bool oryx_webhid_effect_run(effect_params_t* params) {
+    RGB_MATRIX_USE_LIMITS(led_min, led_max);
+    for (uint8_t i = led_min; i < led_max; ++i) {
+        rgb_matrix_set_color(i, webhid_leds[i].r, webhid_leds[i].g, webhid_leds[i].b);
+    }
+    return rgb_matrix_check_finished_leds(led_max);
+}
+
+static bool oryx_webhid_effect(effect_params_t* params) {
+    return oryx_webhid_effect_run(params);
+}
+
+#    endif
+#endif
diff --git a/keyboards/zsa/ergodox_ez/ergodox_ez.c b/keyboards/zsa/ergodox_ez/ergodox_ez.c
index 665dd297fd7b..6e1dd2f090d9 100644
--- a/keyboards/zsa/ergodox_ez/ergodox_ez.c
+++ b/keyboards/zsa/ergodox_ez/ergodox_ez.c
@@ -242,9 +242,16 @@ __attribute__((weak)) const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] =
 
 #endif
 
+#ifdef ORYX_ENABLE
+layer_state_t layer_state_set_kb(layer_state_t state) {
+    state = layer_state_set_user(state);
+    layer_state_set_oryx(state);
+    return state;
+}
+#endif
+
 bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
     switch (keycode) {
-#ifdef ORYX_CONFIGURATOR
         case LED_LEVEL:
             if (record->event.pressed) {
                 keyboard_config.led_level++;
@@ -278,7 +285,6 @@ bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
             }
             return false;
 #    endif
-#endif
     }
     return process_record_user(keycode, record);
 }
diff --git a/keyboards/zsa/ergodox_ez/ergodox_ez.h b/keyboards/zsa/ergodox_ez/ergodox_ez.h
index cb6d0466c6c9..633f6a148721 100644
--- a/keyboards/zsa/ergodox_ez/ergodox_ez.h
+++ b/keyboards/zsa/ergodox_ez/ergodox_ez.h
@@ -24,6 +24,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <stdint.h>
 #include <stdbool.h>
 #include "i2c_master.h"
+#ifdef ORYX_ENABLE
+#    include "oryx.h"
+#endif // ORYX_ENABLE
 
 extern i2c_status_t mcp23018_status;
 
diff --git a/keyboards/zsa/ergodox_ez/rules.mk b/keyboards/zsa/ergodox_ez/rules.mk
index 19088af9a318..4c6f80cecd88 100644
--- a/keyboards/zsa/ergodox_ez/rules.mk
+++ b/keyboards/zsa/ergodox_ez/rules.mk
@@ -2,11 +2,10 @@
 CUSTOM_MATRIX = lite
 
 #project specific files
-SRC += matrix.c \
-	   led_i2c.c
-
-QUANTUM_LIB_SRC += i2c_master.c
+SRC += matrix.c
+I2C_DRIVER_REQUIRED = yes
 
 MOUSE_SHARED_EP = no
 
 DEFAULT_FOLDER = zsa/ergodox_ez/m32u4/base
+include keyboards/zsa/common/features.mk
diff --git a/keyboards/zsa/moonlander/moonlander.c b/keyboards/zsa/moonlander/moonlander.c
index b69766e2ac97..ed59b1d1f5a7 100644
--- a/keyboards/zsa/moonlander/moonlander.c
+++ b/keyboards/zsa/moonlander/moonlander.c
@@ -117,8 +117,8 @@ void keyboard_pre_init_kb(void) {
     keyboard_pre_init_user();
 }
 
-#if !defined(MOONLANDER_USER_LEDS)
 layer_state_t layer_state_set_kb(layer_state_t state) {
+#if !defined(MOONLANDER_USER_LEDS)
     state = layer_state_set_user(state);
     if (is_launching || !keyboard_config.led_level) return state;
     bool LED_1 = false;
@@ -169,10 +169,13 @@ layer_state_t layer_state_set_kb(layer_state_t state) {
 #if !defined(CAPS_LOCK_STATUS)
     ML_LED_6(LED_6);
 #endif
+#endif
+#ifdef ORYX_ENABLE
+    layer_state_set_oryx(state);
+#endif
 
     return state;
 }
-#endif
 
 #ifdef RGB_MATRIX_ENABLE
 // clang-format off
diff --git a/keyboards/zsa/moonlander/moonlander.h b/keyboards/zsa/moonlander/moonlander.h
index 3e1f39ad964b..a5c7552d6a52 100644
--- a/keyboards/zsa/moonlander/moonlander.h
+++ b/keyboards/zsa/moonlander/moonlander.h
@@ -21,6 +21,9 @@
 #pragma once
 
 #include "quantum.h"
+#ifdef ORYX_ENABLE
+#    include "oryx.h"
+#endif // ORYX_ENABLE
 
 extern bool mcp23018_leds[];
 
diff --git a/keyboards/zsa/moonlander/rules.mk b/keyboards/zsa/moonlander/rules.mk
index d2018cfff0cb..63d2d8230fe6 100644
--- a/keyboards/zsa/moonlander/rules.mk
+++ b/keyboards/zsa/moonlander/rules.mk
@@ -5,3 +5,4 @@ SRC += matrix.c mcp23018.c
 I2C_DRIVER_REQUIRED = yes
 
 MCU_LDSCRIPT = STM32F303xB
+include keyboards/zsa/common/features.mk
diff --git a/keyboards/zsa/planck_ez/planck_ez.c b/keyboards/zsa/planck_ez/planck_ez.c
index 9c0e911654eb..dd2fad8aa05a 100644
--- a/keyboards/zsa/planck_ez/planck_ez.c
+++ b/keyboards/zsa/planck_ez/planck_ez.c
@@ -146,7 +146,6 @@ void eeconfig_init_kb(void) {  // EEPROM is getting reset!
 }
 
 
-#ifdef ORYX_CONFIGURATOR
 
 #ifndef PLANCK_EZ_USER_LEDS
 
@@ -179,6 +178,9 @@ layer_state_t layer_state_set_kb(layer_state_t state) {
         default:
             break;
     }
+#ifdef ORYX_ENABLE
+    layer_state_set_oryx(state);
+#endif
     return state;
 }
 #endif
@@ -228,7 +230,6 @@ bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
     }
     return process_record_user(keycode, record);
 }
-#endif
 
 #ifdef AUDIO_ENABLE
 bool music_mask_kb(uint16_t keycode) {
diff --git a/keyboards/zsa/planck_ez/planck_ez.h b/keyboards/zsa/planck_ez/planck_ez.h
index 695b14d9ad00..fa4a9b92c6da 100644
--- a/keyboards/zsa/planck_ez/planck_ez.h
+++ b/keyboards/zsa/planck_ez/planck_ez.h
@@ -18,6 +18,9 @@
 #pragma once
 
 #include "quantum.h"
+#ifdef ORYX_ENABLE
+#    include "oryx.h"
+#endif // ORYX_ENABLE
 
 void planck_ez_right_led_on(void);
 void planck_ez_right_led_off(void);
diff --git a/keyboards/zsa/planck_ez/rules.mk b/keyboards/zsa/planck_ez/rules.mk
index 67921c96ed87..bc8fe396eedc 100644
--- a/keyboards/zsa/planck_ez/rules.mk
+++ b/keyboards/zsa/planck_ez/rules.mk
@@ -2,3 +2,4 @@ RGBLIGHT_SUPPORTED = no
 BAKCLIGHT_SUPPORTED = no
 
 DEFAULT_FOLDER = zsa/planck_ez/base
+include keyboards/zsa/common/features.mk
diff --git a/keyboards/zsa/voyager/rules.mk b/keyboards/zsa/voyager/rules.mk
index bb95224d2bfe..28c538b3eae3 100644
--- a/keyboards/zsa/voyager/rules.mk
+++ b/keyboards/zsa/voyager/rules.mk
@@ -8,3 +8,5 @@ DFU_SUFFIX_ARGS = -v 3297 -p 0791
 VPATH += drivers/gpio
 SRC += matrix.c mcp23018.c
 I2C_DRIVER_REQUIRED = yes
+
+include keyboards/zsa/common/features.mk
diff --git a/keyboards/zsa/voyager/voyager.c b/keyboards/zsa/voyager/voyager.c
index 3255f25a979e..9e65ea9a6934 100644
--- a/keyboards/zsa/voyager/voyager.c
+++ b/keyboards/zsa/voyager/voyager.c
@@ -93,8 +93,8 @@ void keyboard_pre_init_kb(void) {
     keyboard_pre_init_user();
 }
 
-#if !defined(VOYAGER_USER_LEDS)
 layer_state_t layer_state_set_kb(layer_state_t state) {
+#if !defined(VOYAGER_USER_LEDS)
     state = layer_state_set_user(state);
     if (is_launching || !keyboard_config.led_level) return state;
 
@@ -107,9 +107,12 @@ layer_state_t layer_state_set_kb(layer_state_t state) {
 #    if !defined(CAPS_LOCK_STATUS)
     STATUS_LED_4(layer & (1 << 3));
 #    endif
+#endif
+#ifdef ORYX_ENABLE
+    layer_state_set_oryx(state);
+#endif
     return state;
 }
-#endif
 
 #ifdef RGB_MATRIX_ENABLE
 // clang-format off
diff --git a/keyboards/zsa/voyager/voyager.h b/keyboards/zsa/voyager/voyager.h
index a00cc995c627..b60658864e19 100644
--- a/keyboards/zsa/voyager/voyager.h
+++ b/keyboards/zsa/voyager/voyager.h
@@ -5,6 +5,9 @@
 #pragma once
 
 #include "quantum.h"
+#ifdef ORYX_ENABLE
+#    include "oryx.h"
+#endif // ORYX_ENABLE
 
 extern bool mcp23018_leds[];