From b6d4ded29ab4000bf47ba05ed5241b91eb4e3862 Mon Sep 17 00:00:00 2001 From: Arthur de Groot Date: Fri, 4 Jul 2025 07:34:02 +0000 Subject: [PATCH] Fix Logitech G915 blue channel writes for little packet --- .../LogitechG915Controller.cpp | 105 ++++---- .../LogitechG915Controller.h | 9 +- .../RGBController_LogitechG915.cpp | 248 +++++++++++------- 3 files changed, 208 insertions(+), 154 deletions(-) diff --git a/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp index 1691de63..be2b9bb1 100644 --- a/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp +++ b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.cpp @@ -13,6 +13,12 @@ #include "LogitechG915Controller.h" #include "StringUtils.h" +const size_t MIN_DATA_FRAME_SIZE = 4; +const size_t MAX_DATA_FRAME_SIZE = 16; +const size_t HEADER_SIZE = 4; +const size_t MESSAGE_LEN = 20; +const size_t RESPONSE_LEN = 20; + LogitechG915Controller::LogitechG915Controller(hid_device* dev_handle, bool wired) { this->dev_handle = dev_handle; @@ -61,10 +67,11 @@ void LogitechG915Controller::Commit() void LogitechG915Controller::SetDirect ( unsigned char frame_type, - unsigned char * frame_data + unsigned char * frame_data, + size_t length ) { - SendDirectFrame(frame_type, frame_data); + SendDirectFrame(frame_type, frame_data, length); } void LogitechG915Controller::SetMode @@ -115,7 +122,7 @@ void LogitechG915Controller::SetMode void LogitechG915Controller::SendCommit() { - unsigned char usb_buf[20]; + unsigned char usb_buf[MESSAGE_LEN]; /*-----------------------------------------------------*\ | Zero out buffer | @@ -133,13 +140,13 @@ void LogitechG915Controller::SendCommit() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read_timeout(dev_handle, usb_buf, 20, LOGITECH_READ_TIMEOUT); + hid_write(dev_handle, usb_buf, 4); + hid_read_timeout(dev_handle, usb_buf, RESPONSE_LEN, LOGITECH_READ_TIMEOUT); } void LogitechG915Controller::BeginModeSet() { - unsigned char usb_buf[20]; + unsigned char usb_buf[MESSAGE_LEN]; /*-----------------------------------------------------*\ | Zero out buffer | @@ -157,8 +164,8 @@ void LogitechG915Controller::BeginModeSet() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, 4); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); /*-----------------------------------------------------*\ | Zero out buffer | @@ -176,8 +183,8 @@ void LogitechG915Controller::BeginModeSet() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, 4); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); } void LogitechG915Controller::InitializeModeSet() @@ -203,13 +210,13 @@ void LogitechG915Controller::InitializeModeSet() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, 7); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); } void LogitechG915Controller::InitializeDirect() { - unsigned char usb_buf[20]; + unsigned char usb_buf[MESSAGE_LEN]; /*-----------------------------------------------------*\ | Zero out buffer | @@ -227,8 +234,8 @@ void LogitechG915Controller::InitializeDirect() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, 4); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); /*-----------------------------------------------------*\ | Zero out buffer | @@ -246,8 +253,8 @@ void LogitechG915Controller::InitializeDirect() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, 4); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); /*-----------------------------------------------------*\ | Zero out buffer | @@ -266,8 +273,8 @@ void LogitechG915Controller::InitializeDirect() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, 17); + hid_read(dev_handle, usb_buf, MESSAGE_LEN); /*-----------------------------------------------------*\ | Zero out buffer | @@ -287,8 +294,8 @@ void LogitechG915Controller::InitializeDirect() /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, 17); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); } void LogitechG915Controller::SendSingleLed @@ -299,41 +306,27 @@ void LogitechG915Controller::SendSingleLed unsigned char b ) { - unsigned char usb_buf[20]; - - /*-----------------------------------------------------*\ - | Zero out buffer | - \*-----------------------------------------------------*/ - memset(usb_buf, 0x00, sizeof(usb_buf)); - - /*-----------------------------------------------------*\ - | Set up a 6F packet with a single color | - \*-----------------------------------------------------*/ - usb_buf[0x00] = 0x11; - usb_buf[0x01] = device_index; - usb_buf[0x02] = feature_8081_idx; - usb_buf[0x03] = LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE; - - usb_buf[0x04] = keyCode; - - usb_buf[0x05] = r; - usb_buf[0x06] = g; - usb_buf[0x07] = b; - - /*-----------------------------------------------------*\ - | Send packet | - \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + unsigned char little_frame[4] = { keyCode, r, g, b }; + SendDirectFrame(LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE, little_frame, 4); } void LogitechG915Controller::SendDirectFrame ( unsigned char frame_type, - unsigned char * frame_data + unsigned char * frame_data, + size_t length ) { - unsigned char usb_buf[20]; + if(length < MIN_DATA_FRAME_SIZE) + { + return; + } + else if(length > MAX_DATA_FRAME_SIZE) + { + length = MAX_DATA_FRAME_SIZE; + } + + unsigned char usb_buf[MESSAGE_LEN]; /*-----------------------------------------------------*\ | Zero out buffer | @@ -351,13 +344,13 @@ void LogitechG915Controller::SendDirectFrame /*-----------------------------------------------------*\ | Copy in frame data | \*-----------------------------------------------------*/ - memcpy(&usb_buf[0x04], frame_data, 16); + memcpy(&usb_buf[0x04], frame_data, length); /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read_timeout(dev_handle, usb_buf, 20, LOGITECH_READ_TIMEOUT); + hid_write(dev_handle, usb_buf, HEADER_SIZE + length); + hid_read_timeout(dev_handle, usb_buf, RESPONSE_LEN, LOGITECH_READ_TIMEOUT); } void LogitechG915Controller::SendMode @@ -371,7 +364,7 @@ void LogitechG915Controller::SendMode unsigned char blue ) { - unsigned char usb_buf[20]; + unsigned char usb_buf[HEADER_SIZE + 13]; /*-----------------------------------------------------*\ | Zero out buffer | @@ -385,7 +378,7 @@ void LogitechG915Controller::SendMode usb_buf[0x00] = 0x11; usb_buf[0x01] = device_index; usb_buf[0x02] = feature_8071_idx; - usb_buf[0x03] = 0x1E; + usb_buf[0x03] = LOGITECH_G915_ZONE_FRAME_TYPE_MODE; usb_buf[0x04] = zone; usb_buf[0x05] = mode; @@ -434,6 +427,6 @@ void LogitechG915Controller::SendMode /*-----------------------------------------------------*\ | Send packet | \*-----------------------------------------------------*/ - hid_write(dev_handle, usb_buf, 20); - hid_read(dev_handle, usb_buf, 20); + hid_write(dev_handle, usb_buf, HEADER_SIZE + 13); + hid_read(dev_handle, usb_buf, RESPONSE_LEN); } diff --git a/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h index 749b59da..dcede90e 100644 --- a/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h +++ b/Controllers/LogitechController/LogitechG915Controller/LogitechG915Controller.h @@ -30,7 +30,8 @@ enum enum { LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE = 0x1F, - LOGITECH_G915_ZONE_FRAME_TYPE_BIG = 0x6F + LOGITECH_G915_ZONE_FRAME_TYPE_BIG = 0x6F, + LOGITECH_G915_ZONE_FRAME_TYPE_MODE = 0x1E }; enum @@ -93,7 +94,8 @@ public: void SetDirect ( unsigned char frame_type, - unsigned char * frame_data + unsigned char * frame_data, + size_t length ); void SendSingleLed ( @@ -123,7 +125,8 @@ private: void SendDirectFrame ( unsigned char frame_type, - unsigned char * frame_data + unsigned char * frame_data, + size_t length ); void SendMode diff --git a/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp b/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp index 03f91e45..b29eb116 100644 --- a/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp +++ b/Controllers/LogitechController/LogitechG915Controller/RGBController_LogitechG915.cpp @@ -16,8 +16,9 @@ #include "RGBController_LogitechG915.h" #define NA 0xFFFFFFFF -const size_t max_key_per_color = 13; -const size_t data_size = 16; +const size_t DATA_FRAME_SIZE = 16; +const size_t BIG_FRAME_MAX_KEYS = 13; +const size_t LITTLE_FRAME_MAX_KEYS = 4; static unsigned int matrix_map[7][27] = { { 93, NA, NA, NA, NA, NA, NA, NA, NA, NA, 94, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA }, @@ -179,6 +180,90 @@ static const led_type led_names[] = { "Key: G5", LOGITECH_G915_ZONE_MODE_GKEYS, 0x05 }, }; +/*--------------------------------------*\ +| Small dataframe | +| Contains a max of 4 pairs | +\*--------------------------------------*/ +struct LittleFrame +{ + std::pair color_key[LITTLE_FRAME_MAX_KEYS]; + size_t len = 0; +}; + +/*--------------------------------------*\ +| Small dataframe | +| Contains 1 color for max 13 keys | +\*--------------------------------------*/ +struct BigFrame +{ + RGBColor color; + char keys[BIG_FRAME_MAX_KEYS]; + size_t len = 0; +}; + +/*-------------------------------------------*\ +| Add termination byte and zero out rest | +\*-------------------------------------------*/ +void terminate_buffer(unsigned char buf[DATA_FRAME_SIZE], size_t idx) +{ + memset(&buf[idx], 0x00, DATA_FRAME_SIZE - idx); + buf[idx] = 0xFF; +} + +/*-------------------------------------------*\ +| small frame: [KEY, R, G, B] | +| If less than 4 keys, terminate using 0xFF | +\*-------------------------------------------*/ +size_t populate_little_frame_data(unsigned char buf[DATA_FRAME_SIZE], const LittleFrame& frame) +{ + if(frame.len == 0) + { + return 0; + } + + for(size_t i = 0; i < frame.len && i < LITTLE_FRAME_MAX_KEYS; i++) + { + buf[4 * i + 0] = frame.color_key[i].second; + buf[4 * i + 1] = RGBGetRValue(frame.color_key[i].first); + buf[4 * i + 2] = RGBGetGValue(frame.color_key[i].first); + buf[4 * i + 3] = RGBGetBValue(frame.color_key[i].first); + } + + if(frame.len < LITTLE_FRAME_MAX_KEYS) + { + terminate_buffer(buf, 4 * frame.len); + return 4 * frame.len + 1; // termination byte + } + return DATA_FRAME_SIZE; +} + +/*-------------------------------------------------*\ +| Large frame: [R, G, B, Key0, Key1, ..., Key12] | +| If less than 13 keys, terminate using 0xFF | +\*--------------------------------------------------*/ +size_t populate_big_frame_data(unsigned char buf[DATA_FRAME_SIZE], const BigFrame& frame) +{ + if(frame.len == 0) + { + return 0; + } + + buf[0] = RGBGetRValue(frame.color); + buf[1] = RGBGetGValue(frame.color); + buf[2] = RGBGetBValue(frame.color); + for(size_t i = 0; i < frame.len && i < BIG_FRAME_MAX_KEYS; i++) + { + buf[i + 3] = frame.keys[i]; + } + + if(frame.len < BIG_FRAME_MAX_KEYS) + { + terminate_buffer(buf, frame.len + 3); + return frame.len + 4; // color + termination byte + } + return DATA_FRAME_SIZE; +} + /**------------------------------------------------------------------*\ @name Logitech G915 @category Keyboard @@ -375,8 +460,6 @@ void RGBController_LogitechG915::DeviceUpdateLEDs() std::vector new_colors; unsigned char zone = 0; unsigned char idx = 0; - unsigned char frame_buffer_big_mode[data_size]; - unsigned char frame_buffer_little_mode[data_size]; RGBColor colorkey; /*---------------------------------------------------------*\ @@ -434,10 +517,23 @@ void RGBController_LogitechG915::DeviceUpdateLEDs() ledsByColors[colorkey].push_back(idx); } - uint8_t led_in_little_frame = 0; - uint8_t bi = 0; - size_t frame_pos = 3; - uint8_t li = 0; + /*-------------------------------------------------*\ + | Nothing to do, we can skip rest of work | + \*-------------------------------------------------*/ + if(ledsByColors.size() == 0) + { + return; + } + + /*-----------------------------------------------------*\ + | Copy the current color vector to avoid set keys that | + | have not changed | + \*-----------------------------------------------------*/ + std::copy(new_colors.begin(), new_colors.end(),current_colors.begin()); + + std::vector little_frames; + std::vector big_frames; + LittleFrame cur_small; /*---------------------------------------------------------*\ | Create frame_buffers of type 1F (Little, up to 4 leds | @@ -445,109 +541,71 @@ void RGBController_LogitechG915::DeviceUpdateLEDs() \*---------------------------------------------------------*/ for(std::pair>& x: ledsByColors) { - /*-----------------------------------------------------*\ - | For colors with more than 4 keys. Better to use big | - | (6F) packets to save USB transfers. | - \*-----------------------------------------------------*/ - if(x.second.size() > 4) + for(size_t bi = 0; bi < x.second.size(); bi += BIG_FRAME_MAX_KEYS) { - bi = 0; + size_t n_colors_left = x.second.size() - bi; - while(bi < x.second.size()) + /*-----------------------------------------------------*\ + | For colors with more than 4 keys. Better to use big | + | (6F) packets to save USB transfers. | + \*-----------------------------------------------------*/ + if(n_colors_left > 4) { - frame_buffer_big_mode[0] = RGBGetRValue(x.first); - frame_buffer_big_mode[1] = RGBGetGValue(x.first); - frame_buffer_big_mode[2] = RGBGetBValue(x.first); - frame_pos = 3; + BigFrame b_frame; + b_frame.color = x.first; - for(uint8_t i = 0; i < max_key_per_color; i++) + for(size_t i = 0; i < BIG_FRAME_MAX_KEYS && i < n_colors_left; i++) { - if((bi + i) < (uint8_t)x.second.size()) + b_frame.keys[i] = x.second[bi + i]; + b_frame.len++; + } + big_frames.push_back(b_frame); + } + /*-----------------------------------------------------*\ + | For colors with up to 4 keys. Use 1F packet to send | + | up to 4 colors-keys combinations per packet. | + \*-----------------------------------------------------*/ + else + { + for(size_t li = 0; li < n_colors_left; li++) + { + cur_small.color_key[cur_small.len] = std::make_pair(x.first, x.second[bi + li]); + cur_small.len++; + /*-------------------------------*\ + | Frame is full, create a new one | + \*-------------------------------*/ + if(cur_small.len >= LITTLE_FRAME_MAX_KEYS) { - frame_buffer_big_mode[frame_pos] = x.second[bi+i]; - frame_pos++; + little_frames.push_back(std::move(cur_small)); + cur_small = LittleFrame(); } } - - if(frame_pos < data_size) - { - /*-----------------------------------------*\ - | Zeroing just what is needed and if needed | - \*-----------------------------------------*/ - memset(frame_buffer_big_mode + frame_pos, 0x00, sizeof(frame_buffer_big_mode) - frame_pos); - - /*-----------------------------------------*\ - | End of Data byte | - \*-----------------------------------------*/ - frame_buffer_big_mode[frame_pos] = 0xFF; - } - - /*-----------------------------------------------------*\ - | Zeroing just what is needed | - \*-----------------------------------------------------*/ - controller->SetDirect(LOGITECH_G915_ZONE_FRAME_TYPE_BIG, frame_buffer_big_mode); - bi = bi + max_key_per_color; - } - } - /*-----------------------------------------------------*\ - | For colors with up to 4 keys. Use 1F packet to send | - | up to 4 colors-keys combinations per packet. | - \*-----------------------------------------------------*/ - else - { - li = 0; - - while(li < x.second.size()) - { - frame_buffer_little_mode[led_in_little_frame*4 + 0] = x.second[li]; - frame_buffer_little_mode[led_in_little_frame*4 + 1] = RGBGetRValue(x.first); - frame_buffer_little_mode[led_in_little_frame*4 + 2] = RGBGetGValue(x.first); - frame_buffer_little_mode[led_in_little_frame*4 + 3] = RGBGetBValue(x.first); - li++; - led_in_little_frame++; - - if(led_in_little_frame == 4) - { - /*-----------------------------------------*\ - | No End of Data byte if the packet is full | - \*-----------------------------------------*/ - controller->SetDirect(LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE, frame_buffer_little_mode); - led_in_little_frame=0; - } } } } - /*---------------------------------------------------------*\ - | If there is a left 1F packet with less than 4 keys, send | - | it and add an End of Data byte. | - \*---------------------------------------------------------*/ - if(led_in_little_frame > 0) + /*-------------------------------*\ + | Move leftover small frame | + \*-------------------------------*/ + if(cur_small.len > 0) { - /*-----------------------------------------------------*\ - | Zeroing just what is needed | - \*-----------------------------------------------------*/ - memset(frame_buffer_little_mode + (led_in_little_frame * 4 - 1), 0x00, sizeof(frame_buffer_little_mode) - led_in_little_frame * 4); - - /*-----------------------------------------------------*\ - | Data byte | - \*-----------------------------------------------------*/ - frame_buffer_little_mode[led_in_little_frame*4 + 0] = 0xFF; - - /*-----------------------------------------------------*\ - | Send little frame and clear little frame buffer | - \*-----------------------------------------------------*/ - controller->SetDirect(LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE, frame_buffer_little_mode); + little_frames.push_back(std::move(cur_small)); } - if(ledsByColors.size() > 0) + + unsigned char frame_buffer[DATA_FRAME_SIZE]; + for(const BigFrame& frame : big_frames) { - /*-----------------------------------------------------*\ - | Copy the current color vector to avoid set keys that | - | has not being | - \*-----------------------------------------------------*/ - controller->Commit(); - std::copy(new_colors.begin(), new_colors.end(),current_colors.begin()); + size_t length = populate_big_frame_data(frame_buffer, frame); + controller->SetDirect(LOGITECH_G915_ZONE_FRAME_TYPE_BIG, frame_buffer, length); } + + for(const LittleFrame& frame : little_frames) + { + size_t length = populate_little_frame_data(frame_buffer, frame); + controller->SetDirect(LOGITECH_G915_ZONE_FRAME_TYPE_LITTLE, frame_buffer, length); + } + + controller->Commit(); } void RGBController_LogitechG915::UpdateZoneLEDs(int /*zone*/)