From cce2d415001974147b5b1b7afd1897cff0c137c9 Mon Sep 17 00:00:00 2001 From: Adam Honse Date: Sun, 21 Jun 2026 21:12:08 -0500 Subject: [PATCH] Cleanups to QMKKeychronController and create common QMK VIA protocol header --- .../QMKKeychronController.cpp | 671 +++++++++++------- .../QMKKeychronController.h | 170 +++-- .../QMKKeychronControllerDetect.cpp | 9 +- .../RGBController_QMKKeychron.cpp | 11 +- Controllers/QMKController/QMKViaCommands.h | 71 ++ 5 files changed, 578 insertions(+), 354 deletions(-) create mode 100644 Controllers/QMKController/QMKViaCommands.h diff --git a/Controllers/QMKController/QMKKeychronController/QMKKeychronController.cpp b/Controllers/QMKController/QMKKeychronController/QMKKeychronController.cpp index 03767bbe2..05d34c871 100644 --- a/Controllers/QMKController/QMKKeychronController/QMKKeychronController.cpp +++ b/Controllers/QMKController/QMKKeychronController/QMKKeychronController.cpp @@ -9,17 +9,80 @@ \*---------------------------------------------------------*/ #include +#include "hsv.h" #include "QMKKeychronController.h" +#include "QMKViaCommands.h" #include "StringUtils.h" #include "LogManager.h" using namespace std::chrono_literals; -QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name) +QMKKeychronController::QMKKeychronController(hid_device* dev_handle, const char *path) { + /*-----------------------------------------------------*\ + | Initialize controller fields | + \*-----------------------------------------------------*/ dev = dev_handle; - location = info.path; - name = dev_name; + location = path; + supported = false; + + /*-----------------------------------------------------*\ + | Read product string | + \*-----------------------------------------------------*/ + wchar_t product_string[256]; + + int ret = hid_get_product_string(dev, product_string, 256); + + if(ret != 0) + { + name = ""; + } + else + { + name = StringUtils::wstring_to_string(product_string); + } + + /*-----------------------------------------------------*\ + | Read vendor string | + \*-----------------------------------------------------*/ + wchar_t vendor_string[256]; + + ret = hid_get_manufacturer_string(dev, vendor_string, 256); + + if(ret != 0) + { + vendor = ""; + } + else + { + vendor = StringUtils::wstring_to_string(vendor_string); + } + + /*-----------------------------------------------------*\ + | Read serial string | + \*-----------------------------------------------------*/ + wchar_t serial_string[256]; + + ret = hid_get_serial_number_string(dev, serial_string, 256); + + if(ret != 0) + { + serial = ""; + } + else + { + serial = StringUtils::wstring_to_string(serial_string); + } + + /*-----------------------------------------------------*\ + | Get VIA protocol version | + \*-----------------------------------------------------*/ + CmdGetViaProtocolVersion(&via_protocol_version); + + /*-----------------------------------------------------*\ + | Get Keychron protocol version | + \*-----------------------------------------------------*/ + CmdGetKeychronProtocolVersion(&kc_protocol_version); } QMKKeychronController::~QMKKeychronController() @@ -27,27 +90,286 @@ QMKKeychronController::~QMKKeychronController() hid_close(dev); } -std::string QMKKeychronController::GetDeviceLocation() +std::string QMKKeychronController::GetLocation() { return("HID: " + location); } -std::string QMKKeychronController::GetNameString() +std::string QMKKeychronController::GetName() { return(name); } -std::string QMKKeychronController::GetSerialString() +std::string QMKKeychronController::GetSerial() { - wchar_t serial_string[128]; - int ret = hid_get_serial_number_string(dev, serial_string, 128); + return(serial); +} - if(ret != 0) +std::string QMKKeychronController::GetVendor() +{ + return(vendor); +} + +std::string QMKKeychronController::GetVersion() +{ + /*-----------------------------------------------------*\ + | Format multi-line version text | + \*-----------------------------------------------------*/ + return("VIA: " + std::to_string(via_protocol_version) + "\r\n" + + "Keychron: " + std::to_string(kc_protocol_version)); +} + +bool QMKKeychronController::GetSupported() +{ + return(supported); +} + +void QMKKeychronController::CmdGetKeychronProtocolVersion + ( + unsigned char* kc_protocol_version + ) +{ + ViaSendCommand(KC_GET_PROTOCOL_VERSION, NULL, 0, (unsigned char*)kc_protocol_version, sizeof(unsigned char)); +} + +void QMKKeychronController::CmdGetViaProtocolVersion + ( + unsigned short* via_protocol_version + ) +{ + ViaSendCommand(QMK_VIA_CMD_GET_PROTOCOL_VERSION, NULL, 0, (unsigned char*)via_protocol_version, sizeof(unsigned short)); + + /*-----------------------------------------------------*\ + | The protocol version byte order is reversed | + \*-----------------------------------------------------*/ + *via_protocol_version = ((*via_protocol_version & 0x00FF) << 8) | ((*via_protocol_version & 0xFF00) >> 8); +} + +unsigned int QMKKeychronController::GetLedCount() +{ + unsigned short led_count; + + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_LED_COUNT, NULL, 0, (unsigned char*)&led_count, sizeof(unsigned short)); + + /*-----------------------------------------------------*\ + | The LED count byte order is reversed | + \*-----------------------------------------------------*/ + led_count = ((led_count & 0x00FF) << 8) | ((led_count & 0xFF00) >> 8); + + return(led_count); +} + +std::vector QMKKeychronController::GetLedNumbersByRow(unsigned char row) +{ + unsigned char args[4]; + unsigned char response[KEYCHRON_QHE_PACKET_SIZE - 2]; + + args[0] = row; + args[1] = 0xFF; + args[2] = 0xFF; + args[3] = 0xFF; + + int bytes_read = ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_LED_IDX, args, sizeof(args), response, sizeof(response)); + + std::vector result; + + if(bytes_read > 0) { - return(""); + for(int i = 0; i < bytes_read; i++) + { + result.push_back(response[i] == 0xFF ? -1 : response[i]); + } } - return(StringUtils::wstring_to_string(serial_string)); + return result; +} + +std::vector> QMKKeychronController::GetAllLedNumbers(unsigned char num_rows) +{ + std::vector> all_rows; + + for(unsigned char row = 0; row < num_rows; row++) + { + std::vector row_data = GetLedNumbersByRow(row); + all_rows.push_back(row_data); + } + + return all_rows; +} + +void QMKKeychronController::SetPerKeyRgbColor(unsigned char start, unsigned char count, const std::vector& hsv_data) +{ + unsigned char args[KEYCHRON_QHE_PACKET_SIZE - 2]; + + args[0] = start; + args[1] = count; + + size_t data_offset = 2; + size_t copy_len = hsv_data.size(); + + if(data_offset + copy_len > (KEYCHRON_QHE_PACKET_SIZE - 2)) + { + copy_len = (KEYCHRON_QHE_PACKET_SIZE - 2) - data_offset; + } + + memcpy(&args[data_offset], hsv_data.data(), copy_len); + + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PER_KEY_SET_COLOR, args, sizeof(args), NULL, 0); +} + +std::vector QMKKeychronController::GetPerKeyRgbColor(unsigned char start, unsigned char count) +{ + unsigned char args[2]; + unsigned char response[KEYCHRON_QHE_PACKET_SIZE - 2]; + + args[0] = start; + args[1] = count; + + int bytes_read = ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PER_KEY_GET_COLOR, args, sizeof(args), response, sizeof(response)); + + std::vector result; + size_t color_bytes = (size_t)count * 3; + + if(color_bytes <= (size_t)bytes_read) + { + for(size_t i = 0; i < color_bytes; i++) + { + result.push_back(response[i]); + } + } + + return result; +} + +void QMKKeychronController::SetPerKeyRgbType(unsigned char type) +{ + unsigned char args[1]; + + args[0] = type; + + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_PER_KEY_SET_TYPE, args, sizeof(args), NULL, 0); +} + +void QMKKeychronController::SetRgbMatrixMode(unsigned char mode) +{ + unsigned char args[3]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_EFFECT; + args[2] = mode; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +unsigned char QMKKeychronController::GetRgbMatrixMode() +{ + unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; + memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); + + cmd[0] = QMK_VIA_CMD_CUSTOM_GET_VALUE; + cmd[1] = QMK_VIA_RGB_MATRIX_CHANNEL; + cmd[2] = QMK_VIA_RGB_MATRIX_EFFECT; + + SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); + + unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; + memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); + + int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); + + if(bytes_read > 3) + { + return response[3]; + } + + return 0; +} + +void QMKKeychronController::SetBrightness(unsigned char brightness) +{ + unsigned char args[3]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_BRIGHTNESS; + args[2] = brightness; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +unsigned char QMKKeychronController::GetBrightness() +{ + unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; + memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); + + cmd[0] = QMK_VIA_CMD_CUSTOM_GET_VALUE; + cmd[1] = QMK_VIA_RGB_MATRIX_CHANNEL; + cmd[2] = QMK_VIA_RGB_MATRIX_BRIGHTNESS; + + SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); + + unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; + memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); + + int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); + + if(bytes_read > 3) + { + return response[3]; + } + + return 0; +} + +void QMKKeychronController::SetSpeed(unsigned char speed) +{ + unsigned char args[3]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_EFFECT_SPEED; + args[2] = speed; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +unsigned char QMKKeychronController::GetSpeed() +{ + unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; + memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); + + cmd[0] = QMK_VIA_CMD_CUSTOM_GET_VALUE; + cmd[1] = QMK_VIA_RGB_MATRIX_CHANNEL; + cmd[2] = QMK_VIA_RGB_MATRIX_EFFECT_SPEED; + + SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); + + unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; + memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); + + int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); + + if(bytes_read > 3) + { + return response[3]; + } + + return 0; +} + +void QMKKeychronController::SetColorHSV(unsigned char h, unsigned char s) +{ + unsigned char args[4]; + + args[0] = QMK_VIA_RGB_MATRIX_CHANNEL; + args[1] = QMK_VIA_RGB_MATRIX_COLOR; + args[2] = h; + args[3] = s; + + ViaSendCommand(QMK_VIA_CMD_CUSTOM_SET_VALUE, args, sizeof(args), NULL, 0); +} + +void QMKKeychronController::SaveLedConf() +{ + ViaSendCommandSub(KC_KEYCHRON_RGB, KEYCHRON_RGB_SAVE, NULL, 0, NULL, 0); } void QMKKeychronController::SendPacket(unsigned char* data, size_t len) @@ -90,286 +412,89 @@ int QMKKeychronController::ReadPacket(unsigned char* buf, size_t buf_len) return bytes_read; } -unsigned int QMKKeychronController::GetLedCount() +int QMKKeychronController::ViaSendCommand + ( + unsigned char cmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ) { - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); + /*-----------------------------------------------------*\ + | Standard VIA command with no sub-command | + | | + | Byte 0: Command | + | Byte 1+: Data | + \*-----------------------------------------------------*/ + unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1]; - cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP; - cmd[1] = KEYCHRON_KC_RGB_LED_COUNT; + memset(usb_buf, 0, sizeof(usb_buf)); - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); + /*-----------------------------------------------------*\ + | Write command, offsetting by 1 for HID report ID | + \*-----------------------------------------------------*/ + usb_buf[1] = cmd; + memcpy(&usb_buf[2], data_in, data_in_size); - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); + hid_write(dev, usb_buf, sizeof(usb_buf)); - int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); + /*-----------------------------------------------------*\ + | Read response | + \*-----------------------------------------------------*/ + int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf) - 1, 1000); - if(bytes_read > 3 && response[0] == KEYCHRON_KC_RGB_CMD_GROUP && response[1] == KEYCHRON_KC_RGB_LED_COUNT) + if(usb_buf[0] != cmd) { - return response[3]; + return(-1); } - return 0; + memcpy(data_out, &usb_buf[1], data_out_size); + + return(bytes_received - 1); } -std::vector QMKKeychronController::GetLedNumbersByRow(unsigned char row) +int QMKKeychronController::ViaSendCommandSub + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ) { - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); + /*-----------------------------------------------------*\ + | Standard VIA command with sub-command | + | | + | Byte 0: Command | + | Byte 1: Sub-Command | + | Byte 2+: Data | + \*-----------------------------------------------------*/ + unsigned char usb_buf[KEYCHRON_QHE_PACKET_SIZE + 1]; - cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP; - cmd[1] = KEYCHRON_KC_RGB_LED_IDX; - cmd[2] = row; - cmd[3] = 0xFF; - cmd[4] = 0xFF; - cmd[5] = 0xFF; + memset(usb_buf, 0, sizeof(usb_buf)); - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); + /*-----------------------------------------------------*\ + | Write command, offsetting by 1 for HID report ID | + \*-----------------------------------------------------*/ + usb_buf[1] = cmd; + usb_buf[2] = subcmd; + memcpy(&usb_buf[3], data_in, data_in_size); - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); + hid_write(dev, usb_buf, sizeof(usb_buf)); - int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); + /*-----------------------------------------------------*\ + | Read response | + \*-----------------------------------------------------*/ + int bytes_received = hid_read_timeout(dev, usb_buf, sizeof(usb_buf) - 1, 1000); - std::vector result; - - if(bytes_read > 3 && response[0] == KEYCHRON_KC_RGB_CMD_GROUP && response[1] == KEYCHRON_KC_RGB_LED_IDX) + if(usb_buf[0] != cmd || usb_buf[1] != subcmd) { - for(int i = 3; i < bytes_read; i++) - { - result.push_back(response[i] == 0xFF ? -1 : response[i]); - } + return(-1); } - return result; -} - -std::vector> QMKKeychronController::GetAllLedNumbers(unsigned char num_rows) -{ - std::vector> all_rows; - - for(unsigned char row = 0; row < num_rows; row++) - { - std::vector row_data = GetLedNumbersByRow(row); - all_rows.push_back(row_data); - } - - return all_rows; -} - -void QMKKeychronController::SetPerKeyRgbColor(unsigned char start, unsigned char count, const std::vector& hsv_data) -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP; - cmd[1] = KEYCHRON_KC_RGB_PER_KEY_SET_COLOR; - cmd[2] = start; - cmd[3] = count; - - size_t data_offset = 4; - size_t copy_len = hsv_data.size(); - if(data_offset + copy_len > KEYCHRON_QHE_PACKET_SIZE) - { - copy_len = KEYCHRON_QHE_PACKET_SIZE - data_offset; - } - - memcpy(&cmd[data_offset], hsv_data.data(), copy_len); - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); -} - -std::vector QMKKeychronController::GetPerKeyRgbColor(unsigned char start, unsigned char count) -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP; - cmd[1] = KEYCHRON_KC_RGB_PER_KEY_GET_COLOR; - cmd[2] = start; - cmd[3] = count; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); - - std::vector result; - - if(bytes_read > 3 && response[0] == KEYCHRON_KC_RGB_CMD_GROUP && response[1] == KEYCHRON_KC_RGB_PER_KEY_GET_COLOR) - { - size_t color_bytes = (size_t)count * 3; - if(3 + color_bytes <= (size_t)bytes_read) - { - for(size_t i = 3; i < 3 + color_bytes; i++) - { - result.push_back(response[i]); - } - } - } - - return result; -} - -void QMKKeychronController::SetPerKeyRgbType(unsigned char type) -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP; - cmd[1] = KEYCHRON_KC_RGB_PER_KEY_SET_TYPE; - cmd[2] = type; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); -} - -void QMKKeychronController::SetRgbMatrixMode(unsigned char mode) -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE; - cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX; - cmd[2] = KEYCHRON_VIA_BACKLIGHT_EFFECT; - cmd[3] = mode; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); -} - -unsigned char QMKKeychronController::GetRgbMatrixMode() -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_VIA_BACKLIGHT_GET_VALUE; - cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX; - cmd[2] = KEYCHRON_VIA_BACKLIGHT_EFFECT; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); - - if(bytes_read > 3) - { - return response[3]; - } - - return 0; -} - -void QMKKeychronController::SetBrightness(unsigned char brightness) -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE; - cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX; - cmd[2] = KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS; - cmd[3] = brightness; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); -} - -unsigned char QMKKeychronController::GetBrightness() -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_VIA_BACKLIGHT_GET_VALUE; - cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX; - cmd[2] = KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); - - if(bytes_read > 3) - { - return response[3]; - } - - return 0; -} - -void QMKKeychronController::SetSpeed(unsigned char speed) -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE; - cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX; - cmd[2] = KEYCHRON_VIA_BACKLIGHT_SPEED; - cmd[3] = speed; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); -} - -unsigned char QMKKeychronController::GetSpeed() -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_VIA_BACKLIGHT_GET_VALUE; - cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX; - cmd[2] = KEYCHRON_VIA_BACKLIGHT_SPEED; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - memset(response, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - int bytes_read = ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); - - if(bytes_read > 3) - { - return response[3]; - } - - return 0; -} - -void QMKKeychronController::SetColorHSV(unsigned char h, unsigned char s) -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_VIA_BACKLIGHT_SET_VALUE; - cmd[1] = KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX; - cmd[2] = KEYCHRON_VIA_BACKLIGHT_COLOR; - cmd[3] = h; - cmd[4] = s; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); -} - -void QMKKeychronController::SaveLedConf() -{ - unsigned char cmd[KEYCHRON_QHE_PACKET_SIZE]; - memset(cmd, 0x00, KEYCHRON_QHE_PACKET_SIZE); - - cmd[0] = KEYCHRON_KC_RGB_CMD_GROUP; - cmd[1] = KEYCHRON_KC_RGB_SAVE; - - SendPacket(cmd, KEYCHRON_QHE_PACKET_SIZE); - - unsigned char response[KEYCHRON_QHE_PACKET_SIZE]; - ReadPacket(response, KEYCHRON_QHE_PACKET_SIZE); + memcpy(data_out, &usb_buf[2], data_out_size); + + return(bytes_received - 2); } diff --git a/Controllers/QMKController/QMKKeychronController/QMKKeychronController.h b/Controllers/QMKController/QMKKeychronController/QMKKeychronController.h index c1022ef02..ef77ac465 100644 --- a/Controllers/QMKController/QMKKeychronController/QMKKeychronController.h +++ b/Controllers/QMKController/QMKKeychronController/QMKKeychronController.h @@ -23,6 +23,7 @@ | Product IDs | \*---------------------------------------------------------*/ #define KEYCHRON_Q1_HE_PID 0x0B10 +#define KEYCHRON_Q2_PID 0x0111 /*---------------------------------------------------------*\ | QMK raw HID usage page/usage | @@ -37,31 +38,38 @@ #define KEYCHRON_QHE_HID_READ_TIMEOUT 1000 /*---------------------------------------------------------*\ -| KC_RGB command group (0xA8) | -| | -| Keychron-specific per-key RGB protocol. | -| Matches the firmware enum in | -| keyboards/keychron/common/rgb/keychron_rgb.c | +| Keychron-specific VIA protocol extension | \*---------------------------------------------------------*/ -#define KEYCHRON_KC_RGB_CMD_GROUP 0xA8 +enum +{ + KC_GET_PROTOCOL_VERSION = 0xA0, + KC_GET_FIRMWARE_VERSION = 0xA1, + KC_GET_SUPPORT_FEATURE = 0xA2, + KC_GET_DEFAULT_LAYER = 0xA3, + KC_MISC_CMD_GROUP = 0xA7, + KC_KEYCHRON_RGB = 0xA8, + KC_ANALOG_MATRIX = 0xA9, + KC_WIRELESS_DFU = 0xAA, + KC_FACTORY_TEST = 0xAB +}; enum KeychronKCRGBCommand { - KEYCHRON_KC_RGB_PROTOCOL_VER = 0x01, - KEYCHRON_KC_RGB_SAVE = 0x02, - KEYCHRON_KC_RGB_GET_INDICATORS = 0x03, - KEYCHRON_KC_RGB_SET_INDICATORS = 0x04, - KEYCHRON_KC_RGB_LED_COUNT = 0x05, - KEYCHRON_KC_RGB_LED_IDX = 0x06, - KEYCHRON_KC_RGB_PER_KEY_GET_TYPE = 0x07, - KEYCHRON_KC_RGB_PER_KEY_SET_TYPE = 0x08, - KEYCHRON_KC_RGB_PER_KEY_GET_COLOR = 0x09, - KEYCHRON_KC_RGB_PER_KEY_SET_COLOR = 0x0A, - KEYCHRON_KC_RGB_MIXED_GET_INFO = 0x0B, - KEYCHRON_KC_RGB_MIXED_GET_REGIONS = 0x0C, - KEYCHRON_KC_RGB_MIXED_SET_REGIONS = 0x0D, - KEYCHRON_KC_RGB_MIXED_GET_EFFECTS = 0x0E, - KEYCHRON_KC_RGB_MIXED_SET_EFFECTS = 0x0F, + KEYCHRON_RGB_PROTOCOL_VER = 0x01, + KEYCHRON_RGB_SAVE = 0x02, + KEYCHRON_RGB_GET_INDICATORS = 0x03, + KEYCHRON_RGB_SET_INDICATORS = 0x04, + KEYCHRON_RGB_LED_COUNT = 0x05, + KEYCHRON_RGB_LED_IDX = 0x06, + KEYCHRON_RGB_PER_KEY_GET_TYPE = 0x07, + KEYCHRON_RGB_PER_KEY_SET_TYPE = 0x08, + KEYCHRON_RGB_PER_KEY_GET_COLOR = 0x09, + KEYCHRON_RGB_PER_KEY_SET_COLOR = 0x0A, + KEYCHRON_RGB_MIXED_GET_INFO = 0x0B, + KEYCHRON_RGB_MIXED_GET_REGIONS = 0x0C, + KEYCHRON_RGB_MIXED_SET_REGIONS = 0x0D, + KEYCHRON_RGB_MIXED_GET_EFFECTS = 0x0E, + KEYCHRON_RGB_MIXED_SET_EFFECTS = 0x0F, }; /*---------------------------------------------------------*\ @@ -69,45 +77,31 @@ enum KeychronKCRGBCommand \*---------------------------------------------------------*/ enum KeychronPerKeyRgbType { - KEYCHRON_PER_KEY_RGB_SOLID = 0, - KEYCHRON_PER_KEY_RGB_BREATHING = 1, - KEYCHRON_PER_KEY_RGB_REACTIVE_SIMPLE = 2, - KEYCHRON_PER_KEY_RGB_REACTIVE_WIDE = 3, - KEYCHRON_PER_KEY_RGB_REACTIVE_SPLASH = 4, + KEYCHRON_PER_KEY_RGB_SOLID = 0, + KEYCHRON_PER_KEY_RGB_BREATHING = 1, + KEYCHRON_PER_KEY_RGB_REACTIVE_SIMPLE = 2, + KEYCHRON_PER_KEY_RGB_REACTIVE_WIDE = 3, + KEYCHRON_PER_KEY_RGB_REACTIVE_SPLASH = 4, }; /*---------------------------------------------------------*\ -| VIA backlight config commands | -| | -| Standard QMK/VIA rgb_matrix control. | -| Format: [cmd, value_type, value_id, value] | -| value_type = 3 for rgb_matrix | -\*---------------------------------------------------------*/ -enum KeychronVIABacklightCommand -{ - KEYCHRON_VIA_BACKLIGHT_SET_VALUE = 0x07, - KEYCHRON_VIA_BACKLIGHT_GET_VALUE = 0x08, - KEYCHRON_VIA_BACKLIGHT_SAVE = 0x09, -}; - -/*---------------------------------------------------------*\ -| VIA backlight value IDs | +| VIA backlight value IDs | \*---------------------------------------------------------*/ #define KEYCHRON_VIA_BACKLIGHT_TYPE_RGB_MATRIX 0x03 enum KeychronVIABacklightValueID { - KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS = 0x01, - KEYCHRON_VIA_BACKLIGHT_EFFECT = 0x02, - KEYCHRON_VIA_BACKLIGHT_SPEED = 0x03, - KEYCHRON_VIA_BACKLIGHT_COLOR = 0x04, + KEYCHRON_VIA_BACKLIGHT_BRIGHTNESS = 0x01, + KEYCHRON_VIA_BACKLIGHT_EFFECT = 0x02, + KEYCHRON_VIA_BACKLIGHT_SPEED = 0x03, + KEYCHRON_VIA_BACKLIGHT_COLOR = 0x04, }; /*---------------------------------------------------------*\ | Q1 HE effect IDs | | | | Determined by the number of standard effects enabled | -| in the Q1 HE firmware (info.json animations) + | +| in the Q1 HE firmware (info.json animations) + | | 2 custom effects (PER_KEY_RGB, MIXED_RGB). | | May need adjustment for other firmware versions. | \*---------------------------------------------------------*/ @@ -124,39 +118,77 @@ enum KeychronVIABacklightValueID class QMKKeychronController { public: - QMKKeychronController(hid_device* dev_handle, const hid_device_info& info, std::string dev_name); + QMKKeychronController(hid_device* dev_handle, const char *path); ~QMKKeychronController(); - std::string GetDeviceLocation(); - std::string GetNameString(); - std::string GetSerialString(); + std::string GetLocation(); + std::string GetName(); + std::string GetSerial(); + std::string GetVendor(); + std::string GetVersion(); - unsigned int GetLedCount(); - std::vector GetLedNumbersByRow(unsigned char row); - std::vector> GetAllLedNumbers(unsigned char num_rows); + bool GetSupported(); - void SetPerKeyRgbColor(unsigned char start, unsigned char count, const std::vector& hsv_data); - std::vector GetPerKeyRgbColor(unsigned char start, unsigned char count); + unsigned int GetLedCount(); + std::vector GetLedNumbersByRow(unsigned char row); + std::vector> GetAllLedNumbers(unsigned char num_rows); - void SetRgbMatrixMode(unsigned char mode); - unsigned char GetRgbMatrixMode(); + void SetPerKeyRgbColor(unsigned char start, unsigned char count, const std::vector& hsv_data); + std::vector GetPerKeyRgbColor(unsigned char start, unsigned char count); - void SetPerKeyRgbType(unsigned char type); + void SetRgbMatrixMode(unsigned char mode); + unsigned char GetRgbMatrixMode(); - void SetBrightness(unsigned char brightness); - unsigned char GetBrightness(); + void SetPerKeyRgbType(unsigned char type); - void SetSpeed(unsigned char speed); - unsigned char GetSpeed(); + void SetBrightness(unsigned char brightness); + unsigned char GetBrightness(); - void SetColorHSV(unsigned char h, unsigned char s); - void SaveLedConf(); + void SetSpeed(unsigned char speed); + unsigned char GetSpeed(); + + void SetColorHSV(unsigned char h, unsigned char s); + void SaveLedConf(); private: - hid_device* dev; - std::string location; - std::string name; + hid_device* dev; + unsigned char kc_protocol_version; + std::string location; + std::string name; + std::string serial; + bool supported; + std::string vendor; + unsigned short via_protocol_version; - void SendPacket(unsigned char* data, size_t len); - int ReadPacket(unsigned char* buf, size_t buf_len); + void CmdGetKeychronProtocolVersion + ( + unsigned char* kc_protocol_version + ); + + void CmdGetViaProtocolVersion + ( + unsigned short* via_protocol_version + ); + + void SendPacket(unsigned char* data, size_t len); + int ReadPacket(unsigned char* buf, size_t buf_len); + + int ViaSendCommand + ( + unsigned char cmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ); + + int ViaSendCommandSub + ( + unsigned char cmd, + unsigned char subcmd, + unsigned char* data_in, + unsigned char data_in_size, + unsigned char* data_out, + unsigned char data_out_size + ); }; diff --git a/Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp b/Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp index c537f5d8c..3d8de2209 100644 --- a/Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp +++ b/Controllers/QMKController/QMKKeychronController/QMKKeychronControllerDetect.cpp @@ -12,23 +12,18 @@ #include "QMKKeychronController.h" #include "RGBController_QMKKeychron.h" -/*---------------------------------------------------------*\ -| Q1 HE detector | -\*---------------------------------------------------------*/ void DetectQMKKeychronController(hid_device_info* info, const std::string& name) { hid_device* dev = hid_open_path(info->path); if(dev) { - QMKKeychronController* controller = new QMKKeychronController(dev, *info, name); + QMKKeychronController* controller = new QMKKeychronController(dev, info->path); RGBController_QMKKeychron* rgb_controller = new RGBController_QMKKeychron(controller); ResourceManager::get()->RegisterRGBController(rgb_controller); } } -/*---------------------------------------------------------*\ -| Register HID detectors | -\*---------------------------------------------------------*/ REGISTER_HID_DETECTOR_IPU("Keychron Q1 HE", DetectQMKKeychronController, KEYCHRON_VID, KEYCHRON_Q1_HE_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); +REGISTER_HID_DETECTOR_IPU("Keychron Q2", DetectQMKKeychronController, KEYCHRON_VID, KEYCHRON_Q2_PID, 1, KEYCHRON_QMK_USAGE_PAGE, KEYCHRON_QMK_USAGE); diff --git a/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp b/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp index 7cff89eaa..b583e1fe9 100644 --- a/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp +++ b/Controllers/QMKController/QMKKeychronController/RGBController_QMKKeychron.cpp @@ -67,12 +67,13 @@ RGBController_QMKKeychron::RGBController_QMKKeychron(QMKKeychronController* cont { controller = controller_ptr; - name = controller->GetNameString(); - vendor = "Keychron"; + name = controller->GetName(); + vendor = controller->GetVendor(); type = DEVICE_TYPE_KEYBOARD; - description = name; - location = controller->GetDeviceLocation(); - serial = controller->GetSerialString(); + description = "QMK Keychron Device"; + location = controller->GetLocation(); + serial = controller->GetSerial(); + version = controller->GetVersion(); for(const keychron_qhe_effect& effect : qhe_effects) { diff --git a/Controllers/QMKController/QMKViaCommands.h b/Controllers/QMKController/QMKViaCommands.h new file mode 100644 index 000000000..f0884846f --- /dev/null +++ b/Controllers/QMKController/QMKViaCommands.h @@ -0,0 +1,71 @@ +/*---------------------------------------------------------*\ +| QMKViaCommands.h | +| | +| List of QMK VIA command values | +| | +| Adam Honse