From 7b454ce5f4ece4eb0fce7347aa1955904a51f721 Mon Sep 17 00:00:00 2001 From: Chris M Date: Wed, 27 Dec 2023 10:02:58 +1100 Subject: [PATCH] Preliminary support for Logitech FP8071 * Updated detection for differences in FP8071 --- .../RGBController_LogitechLightspeed.cpp | 1 + .../LogitechProtocolCommon.cpp | 141 ++++++++++++++++-- .../LogitechProtocolCommon.h | 50 ++++++- 3 files changed, 180 insertions(+), 12 deletions(-) diff --git a/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp b/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp index 0171befd..7b70ada6 100644 --- a/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp +++ b/Controllers/LogitechController/LogitechLightspeedController/RGBController_LogitechLightspeed.cpp @@ -168,6 +168,7 @@ void RGBController_LogitechLightspeed::SetupZones() const std::string led_string = "LED"; uint8_t led_count = controller->lightspeed->getLED_count(); + LOG_DEBUG("[%s] Setting up %d LEDs", name.c_str(), led_count); if(led_count > 0) { for(size_t i = 0; i < led_count; i++) diff --git a/Controllers/LogitechController/LogitechProtocolCommon.cpp b/Controllers/LogitechController/LogitechProtocolCommon.cpp index f80d6bb5..4011186b 100644 --- a/Controllers/LogitechController/LogitechProtocolCommon.cpp +++ b/Controllers/LogitechController/LogitechProtocolCommon.cpp @@ -17,7 +17,14 @@ const char* logitech_led_locations[] = "Primary", "Logo", "Left", - "Right" + "Right", + "Combined", + "Group One", + "Group Two", + "Group Three", + "Group Four", + "Group Five", + "Group Six" }; const int NUM_LOGITECH_LED_LOCATIONS = sizeof(logitech_led_locations); @@ -589,11 +596,11 @@ void logitech_device::getRGBconfig() result = hid_write(dev_use2, get_count.buffer, get_count.size()); result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); - LOG_DEBUG("[%s] FP8071 - LED Count - %02X : %02X %02X %04X %04X %04X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), - response.data[2], response.data[0], response.data[1], (response.data[3] << 8 | response.data[4]), (response.data[5] << 8 | response.data[6]), + LOG_DEBUG("[%s] FP8071 - LED Count - %04X : %02X %04X %04X %04X %04X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), + (response.data[1] << 8 | response.data[2]), response.data[0], (response.data[1] << 8 | response.data[2]), (response.data[3] << 8 | response.data[4]), (response.data[5] << 8 | response.data[6]), (response.data[7] << 8 | response.data[8]), response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); - led_response = response.data[2]; + led_response = (response.data[1] << 8 | response.data[2]); for(size_t i = 0; i < led_response; i++) { get_count.data[0] = (uint8_t)i; @@ -602,7 +609,7 @@ void logitech_device::getRGBconfig() result = hid_write(dev_use2, get_count.buffer, get_count.size()); result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); - LOG_DEBUG("[%s] FP8071 - LED %02i - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), led_counter, + LOG_DEBUG("[%s] FP8071 - LED %02i - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", device_name.c_str(), i, response.data[0], response.data[1], response.data[2], response.data[3], response.data[4], response.data[5], response.data[6], response.data[7], response.data[8], response.data[9], response.data[10], response.data[11], response.data[12], response.data[13], response.data[14], response.data[15]); @@ -617,7 +624,7 @@ void logitech_device::getRGBconfig() fx_response.init(); longFAPrequest get_effect; - get_effect.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_GET_INFO); + get_effect.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_GET_COUNT); get_effect.data[0] = get_count.data[0]; get_effect.data[1] = i; @@ -638,6 +645,10 @@ void logitech_device::getRGBconfig() leds.emplace(response.data[0], new_led); } + /*-----------------------------------------------------------------*\ + | Set the config to SW control mode | + \*-----------------------------------------------------------------*/ + set8071Effects(5); } /*get_count.feature_command = LOGITECH_CMD_RGB_EFFECTS_GET_STATE; @@ -682,7 +693,7 @@ uint8_t logitech_device::setDirectMode(bool direct) | Turn the direct mode on or off via the RGB_feature_index | \*-----------------------------------------------------------------*/ longFAPrequest set_direct; - set_direct.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_UNKNOWN); + set_direct.init(device_index, RGB_feature_index, LOGITECH_FP8070_SET_SW_CTL); set_direct.data[0] = (direct) ? 1 : 0; set_direct.data[1] = set_direct.data[0]; @@ -733,7 +744,13 @@ uint8_t logitech_device::setMode(uint8_t mode, uint16_t speed, uint8_t zone, uin | Set the mode via the RGB_feature_index | \*-----------------------------------------------------------------*/ longFAPrequest set_mode; - set_mode.init(device_index, RGB_feature_index, ((feature_page == LOGITECH_HIDPP_PAGE_RGB_EFFECTS1) ? LOGITECH_CMD_RGB_EFFECTS_SET_CONTROL : LOGITECH_CMD_RGB_EFFECTS_GET_INFO)); + bool fp8070 = (feature_page == LOGITECH_HIDPP_PAGE_RGB_EFFECTS1); + + set_mode.init( + device_index, + RGB_feature_index, + (fp8070 ? LOGITECH_FP8070_SET_EFFECT : LOGITECH_FP8071_SET_LED_EFFECT) + ); set_mode.data[0] = zone; set_mode.data[1] = mode; @@ -741,12 +758,13 @@ uint8_t logitech_device::setMode(uint8_t mode, uint16_t speed, uint8_t zone, uin set_mode.data[3] = green; set_mode.data[4] = blue; + set_mode.data[12] = fp8070 ? 0x00 : 0x01; //Bit 2-3 Power Mode : Bit 1-0 Persistence + speed *= 100; switch(fx) { case LOGITECH_DEVICE_LED_ON: - set_mode.data[5] = 0x02; //zone; - //set_mode.data[12] = 0x01; //Save to flash testing + //set_mode.data[5] = 0x02; //zone; break; case LOGITECH_DEVICE_LED_SPECTRUM: @@ -795,5 +813,108 @@ uint8_t logitech_device::setMode(uint8_t mode, uint16_t speed, uint8_t zone, uin } } + return result; +} + +uint8_t logitech_device::set8071Effects(uint8_t control) +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | then set the device into direct mode via register 0x80 | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Turn the direct mode on or off via the RGB_feature_index | + \*-----------------------------------------------------------------*/ + shortFAPrequest set_effects; + set_effects.init(device_index, RGB_feature_index); + set_effects.feature_command = LOGITECH_FP8071_CONTROL; + set_effects.data[0] = 1; + set_effects.data[1] = 3; //Disables all FW control for PWR (0x02) and RGB (0x01) + set_effects.data[2] = control; + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + result = hid_write(dev_use2, set_effects.buffer, set_effects.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + result = hid_write(dev_use2, set_effects.buffer, set_effects.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + } + return result; +} + +uint8_t logitech_device::set8071TimeoutControl(uint8_t control) +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | then set the device into direct mode via register 0x80 | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Turn the direct mode on or off via the RGB_feature_index | + \*-----------------------------------------------------------------*/ + longFAPrequest set_control; + set_control.init(device_index, RGB_feature_index, LOGITECH_FP8071_PWR_CFG); + set_control.data[0] = 1; //1; + set_control.data[3] = 0; //0x3C; //Inactive Lighting timeout MSB + set_control.data[4] = 5; //0x3C; //Inactive Lighting timeout LSB + set_control.data[5] = 0; //1; //Lights off timeout MSB + set_control.data[6] = 20; //0x2C; //Lights off timeout LSB + + /*-----------------------------------------------------*\ + | Send packet | + | This code has to be protected to avoid crashes when | + | this is called at the same time to change a powerplay | + | mat and its paired wireless mouse leds. It will | + | happen when using effects engines with high framerate | + \*-----------------------------------------------------*/ + if(mutex) + { + std::lock_guard guard(*mutex); + + result = hid_write(dev_use2, set_control.buffer, set_control.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + result = hid_write(dev_use2, set_control.buffer, set_control.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + } return(result); } diff --git a/Controllers/LogitechController/LogitechProtocolCommon.h b/Controllers/LogitechController/LogitechProtocolCommon.h index d3753880..9a0383f3 100644 --- a/Controllers/LogitechController/LogitechProtocolCommon.h +++ b/Controllers/LogitechController/LogitechProtocolCommon.h @@ -52,7 +52,6 @@ #define LOGITECH_CMD_RGB_EFFECTS_SET_STATE 0x50 #define LOGITECH_CMD_RGB_EFFECTS_GET_CONFIG 0x60 #define LOGITECH_CMD_RGB_EFFECTS_SET_CONFIG 0x70 -#define LOGITECH_CMD_RGB_EFFECTS_UNKNOWN 0x80 //Used to set "direct" mode enum LOGITECH_DEVICE_TYPE { @@ -79,6 +78,51 @@ enum LOGITECH_DEVICE_MODE LOGITECH_DEVICE_LED_CUSTOM = 0x000C }; +enum LOGITECH_FP8070 +{ + LOGITECH_FP8070_INFO = 0x00, + LOGITECH_FP8070_ZONE_INFO = 0x10, + LOGITECH_FP8070_EFFECT_INFO = 0x20, + LOGITECH_FP8070_SET_EFFECT = 0x30, + LOGITECH_FP8070_SET_CFG = 0x40, + LOGITECH_FP8070_GET_CFG = 0x50, + LOGITECH_FP8070_GET_BIN_INFO = 0x60, + LOGITECH_FP8070_GET_SW_CTL = 0x70, + LOGITECH_FP8070_SET_SW_CTL = 0x80, + LOGITECH_FP8070_GET_STATUS = 0x90, + LOGITECH_FP8070_CLEAR_EFFECT = 0xA0, + LOGITECH_FP8070_SET_DIR = 0xB0, + LOGITECH_FP8070_GET_COLOUR = 0xC0, + LOGITECH_FP8070_SYNC_CFG = 0xD0, + LOGITECH_FP8070_GET_EFFECT = 0xE0, + LOGITECH_FP8070_SET_BIN_INFO = 0xF0, +}; + +enum LOGITECH_FP8071 +{ + LOGITECH_FP8071_INFO = 0x00, + LOGITECH_FP8071_SET_LED_EFFECT = 0x10, + LOGITECH_FP8071_ZONE_PATTERN = 0x20, + LOGITECH_FP8071_CONFIG = 0x30, + LOGITECH_FP8070_BIN_INFO = 0x40, + LOGITECH_FP8071_CONTROL = 0x50, + LOGITECH_FP8071_SYNC_CFG = 0x60, + LOGITECH_FP8071_PWR_CFG = 0x70, + LOGITECH_FP8071_PWR_MODE = 0x80, + LOGITECH_FP8071_SHUTDOWN = 0x90 +}; + +enum LOGITECH_FP8071_FLAGS +{ + FP8071_SUPPORTS_GET_STATUS = 0x01, + FP8071_RESERVED = 0x02, + FP8071_SUPPORTS_SET_BIN_INFO = 0x04, + FP8071_MONOCHROME_ONLY = 0x08, + FP8071_NO_SYNC_SUPPORT = 0x10, + FP8071_SUPPORTS_SHUTDOWN = 0x20, + FP8071_SUPPORTS_CLUSTER_CHANGED = 0x40, +}; + extern const char* logitech_led_locations[]; extern const int NUM_LOGITECH_LED_LOCATIONS; @@ -122,7 +166,7 @@ union shortFAPrequest this->report_id = LOGITECH_SHORT_MESSAGE; this->device_index = device_index; this->feature_index = feature_index; - this->feature_command = 0; + this->feature_command = feature_command; for(size_t i = 0; i < sizeof(data); i++) { this->data[i] = 0; @@ -258,6 +302,8 @@ public: logitech_led getLED_info(uint8_t LED_num); uint8_t setDirectMode(bool direct); uint8_t setMode(uint8_t mode, uint16_t speed, uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness); + uint8_t set8071Effects(uint8_t control); + uint8_t set8071TimeoutControl(uint8_t control); int getDeviceName(); private: std::map leds;