From df671b656bdecb76dfb055984ff950ebd98a97b2 Mon Sep 17 00:00:00 2001 From: rootiest Date: Fri, 10 Apr 2026 15:10:14 -0400 Subject: [PATCH] fix(q5_max): fix RAW_EPSIZE and via_command_kb hook conflict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two build errors: 1. RAW_EPSIZE undeclared — usb_descriptor.h is not in scope when keymap.c is compiled through Keychron's build path. Replace all uses with HID_PACKET_SIZE (= 32), now defined in hid_protocol.h. 2. via_command_kb duplicate symbol — keychron_raw_hid.c already defines via_command_kb (non-weak) so we cannot redefine it in keymap.c. Fix by adding a weak kc_raw_hid_rx_kb() extension hook to keychron_raw_hid.c (following the same pattern as kc_rgb_matrix_rx). kc_raw_hid_rx() now calls this hook from its default case instead of returning false directly. The keymap overrides kc_raw_hid_rx_kb() to handle our custom HID command range (0x40-0x7E). --- keyboards/keychron/common/keychron_raw_hid.c | 7 ++++++- keyboards/keychron/common/keychron_raw_hid.h | 14 ++++++++++++++ .../ansi_encoder/keymaps/via/hid_protocol.h | 8 ++++++++ .../q5_max/ansi_encoder/keymaps/via/keymap.c | 15 +++++++++------ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/keyboards/keychron/common/keychron_raw_hid.c b/keyboards/keychron/common/keychron_raw_hid.c index e21776f5c8..1bca970ed0 100644 --- a/keyboards/keychron/common/keychron_raw_hid.c +++ b/keyboards/keychron/common/keychron_raw_hid.c @@ -85,6 +85,11 @@ void get_firmware_version(uint8_t *data) { __attribute__((weak)) void kc_rgb_matrix_rx(uint8_t *data, uint8_t length) {} +/* Default no-op implementation; override in keymap.c to handle custom IDs. */ +__attribute__((weak)) bool kc_raw_hid_rx_kb(uint8_t *data, uint8_t length) { + return false; +} + bool kc_raw_hid_rx(uint8_t *data, uint8_t length) { switch (data[0]) { case KC_GET_PROTOCOL_VERSION: @@ -185,7 +190,7 @@ bool kc_raw_hid_rx(uint8_t *data, uint8_t length) { #endif default: - return false; + return kc_raw_hid_rx_kb(data, length); } raw_hid_send(data, length); diff --git a/keyboards/keychron/common/keychron_raw_hid.h b/keyboards/keychron/common/keychron_raw_hid.h index 4419f0f73f..fedbbd39db 100644 --- a/keyboards/keychron/common/keychron_raw_hid.h +++ b/keyboards/keychron/common/keychron_raw_hid.h @@ -63,3 +63,17 @@ enum { REPORT_RATE_GET, REPORT_RATE_SET, }; + +/** + * Keyboard-level Raw HID extension hook. + * + * Called by kc_raw_hid_rx() for any command ID not handled by Keychron's own + * protocol (0xA0-0xAB range). The default weak implementation returns false + * so that unrecognised commands fall through to VIA. + * + * Override this in your keymap to handle custom command IDs without + * conflicting with Keychron's via_command_kb() definition. + * Return true if the packet was fully handled (including calling + * raw_hid_send() if a reply is needed), false to let VIA process it. + */ +bool kc_raw_hid_rx_kb(uint8_t *data, uint8_t length); diff --git a/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/hid_protocol.h b/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/hid_protocol.h index 10b70af2e1..c4649ef2b6 100644 --- a/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/hid_protocol.h +++ b/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/hid_protocol.h @@ -80,6 +80,14 @@ // --------------------------------------------------------------------------- #define HID_APP_NAME_MAX 28u +// --------------------------------------------------------------------------- +// Packet size +// --------------------------------------------------------------------------- +// Matches RAW_EPSIZE (QMK raw HID endpoint size = 32 bytes). Defined here +// so keymap code does not depend on usb_descriptor.h being in scope, which +// it is not when compiled through Keychron's build path. +#define HID_PACKET_SIZE 32u + // --------------------------------------------------------------------------- // Convenience: first byte of payload as an absolute packet index // --------------------------------------------------------------------------- diff --git a/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/keymap.c b/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/keymap.c index aaf5e10d90..d3f1839c2c 100644 --- a/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/keymap.c +++ b/keyboards/keychron/q5_max/ansi_encoder/keymaps/via/keymap.c @@ -18,6 +18,7 @@ #include "keychron_common.h" #include "chord_unicode.h" #include "raw_hid.h" +#include "keychron_raw_hid.h" #include "hid_protocol.h" // Tap Dance declarations @@ -77,18 +78,20 @@ static uint8_t g_hid_brightness = 0; // Send the current layer state to the host / bridge application. static void hid_send_layer_sync(uint8_t layer, uint8_t locked_mask) { - uint8_t data[RAW_EPSIZE] = {0}; + uint8_t data[HID_PACKET_SIZE] = {0}; data[HID_OFF_CMD] = HID_CMD_LAYER_SYNC; data[HID_OFF_SRC] = HID_DEV_Q5MAX; data[HID_OFF_FLAGS] = 0; data[HID_PAYLOAD(HID_LAYER_OFF_ACTIVE)] = layer; data[HID_PAYLOAD(HID_LAYER_OFF_LOCKED)] = locked_mask; - raw_hid_send(data, RAW_EPSIZE); + raw_hid_send(data, HID_PACKET_SIZE); } // Handle a Raw HID packet for our custom command range (0x40-0x7E). -// Called from via_command_kb(); must call raw_hid_send() for any reply. -bool via_command_kb(uint8_t *data, uint8_t length) { +// Overrides the weak kc_raw_hid_rx_kb() hook in keychron_raw_hid.c, which is +// called by kc_raw_hid_rx() for any command ID not handled by Keychron's own +// protocol. Must call raw_hid_send() directly for any reply. +bool kc_raw_hid_rx_kb(uint8_t *data, uint8_t length) { uint8_t cmd = data[HID_OFF_CMD]; // Only intercept our custom command range; let VIA handle everything else. @@ -102,13 +105,13 @@ bool via_command_kb(uint8_t *data, uint8_t length) { case HID_CMD_LAYER_SYNC: { if (flags & HID_FLAG_QUERY) { // Host requests current state — reply without changing anything. - uint8_t resp[RAW_EPSIZE] = {0}; + uint8_t resp[HID_PACKET_SIZE] = {0}; resp[HID_OFF_CMD] = HID_CMD_LAYER_SYNC; resp[HID_OFF_SRC] = HID_DEV_Q5MAX; resp[HID_OFF_FLAGS] = HID_FLAG_RESPONSE; resp[HID_PAYLOAD(HID_LAYER_OFF_ACTIVE)] = get_highest_layer(layer_state); resp[HID_PAYLOAD(HID_LAYER_OFF_LOCKED)] = (uint8_t)locked_layers; - raw_hid_send(resp, RAW_EPSIZE); + raw_hid_send(resp, HID_PACKET_SIZE); } else { // Host or peer keyboard is pushing a new active layer. uint8_t new_layer = data[HID_PAYLOAD(HID_LAYER_OFF_ACTIVE)];