fix(q5_max): fix RAW_EPSIZE and via_command_kb hook conflict

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).
This commit is contained in:
2026-04-10 15:10:14 -04:00
parent 6fd5179c55
commit df671b656b
4 changed files with 37 additions and 7 deletions
+6 -1
View File
@@ -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);
@@ -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);
@@ -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
// ---------------------------------------------------------------------------
@@ -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)];