diff --git a/Controllers/LogitechController/LogitechControllerDetect.cpp b/Controllers/LogitechController/LogitechControllerDetect.cpp index a3b74e27..b0332407 100644 --- a/Controllers/LogitechController/LogitechControllerDetect.cpp +++ b/Controllers/LogitechController/LogitechControllerDetect.cpp @@ -1,4 +1,6 @@ #include "Detector.h" +#include "LogManager.h" +#include "LogitechProtocolCommon.h" #include "LogitechG203LController.h" #include "LogitechG213Controller.h" #include "LogitechG560Controller.h" @@ -6,6 +8,7 @@ #include "LogitechG910Controller.h" #include "LogitechG815Controller.h" #include "LogitechGLightsyncController.h" +#include "LogitechGProWirelessController.h" #include "RGBController.h" #include "RGBController_LogitechG203L.h" #include "RGBController_LogitechG213.h" @@ -15,10 +18,10 @@ #include "RGBController_LogitechG815.h" #include "RGBController_LogitechGLightsync.h" #include "RGBController_LogitechGLightsync1zone.h" +#include "RGBController_LogitechGProWireless.h" #include "RGBController_LogitechGPowerPlay.h" #include #include -#include /*-----------------------------------------------------*\ | Logitech vendor ID | @@ -44,27 +47,20 @@ | Mouse product IDs | \*-----------------------------------------------------*/ #define LOGITECH_G203_PID 0xC084 -#define LOGITECH_G203L_PID 0xC092 +#define LOGITECH_G203_LIGHTSYNC_PID 0xC092 #define LOGITECH_G303_PID 0xC080 #define LOGITECH_G403_PID 0xC083 -#define LOGITECH_G403H_PID 0xC08F -#define LOGITECH_G403_WIRELESS_PID 0xC082 -#define LOGITECH_G403_WIRELESS_VIRTUAL_PID 0x405D -#define LOGITECH_G502_PS_PID 0xC332 -#define LOGITECH_G502H_PID 0xC08B -#define LOGITECH_G502_WIRELESS_PID 0xC08D -#define LOGITECH_G502_WIRELESS_VIRTUAL_PID 0x407F -#define LOGITECH_G703_WIRELESS_PID 0xC087 -#define LOGITECH_G703_WIRELESS_VIRTUAL_PID 0x4070 -#define LOGITECH_G900_WIRELESS_PID 0xC081 -#define LOGITECH_G900_WIRELESS_VIRTUAL_PID 0x4053 -#define LOGITECH_G903_WIRELESS_PID 0xC086 -#define LOGITECH_G903_WIRELESS_VIRTUAL_PID 0x4067 -#define LOGITECH_G_LIGHTSPEED_WIRELESS_PID 0xC539 -#define LOGITECH_GPRO_WIRED_PID 0xC085 -#define LOGITECH_GPRO_HERO_WIRED_PID 0xC08C -#define LOGITECH_GPRO_WIRELESS_PID 0xC088 -#define LOGITECH_GPRO_WIRELESS_VIRTUAL_PID 0x4079 +#define LOGITECH_G403_HERO_PID 0xC08F +#define LOGITECH_G403_LIGHTSPEED_PID 0xC082 +#define LOGITECH_G502_PROTEUS_SPECTRUM_PID 0xC332 +#define LOGITECH_G502_HERO_PID 0xC08B +#define LOGITECH_G502_LIGHTSPEED_PID 0xC08D +#define LOGITECH_G703_LIGHTSPEED_PID 0xC087 +#define LOGITECH_G900_LIGHTSPEED_PID 0xC081 +#define LOGITECH_G903_LIGHTSPEED_PID 0xC086 +#define LOGITECH_G_PRO_PID 0xC085 +#define LOGITECH_G_PRO_HERO_PID 0xC08C +#define LOGITECH_G_PRO_WIRED_PID 0xC088 /*-----------------------------------------------------*\ | Mousemat product IDs | @@ -76,6 +72,24 @@ \*-----------------------------------------------------*/ #define LOGITECH_G560_PID 0x0A78 +/*-----------------------------------------------------*\ +| Unifying Device IDs (Including Lightspeed receivers) | +| NB: Not used but preserved for debugging | +\*-----------------------------------------------------*/ +#define LOGITECH_G_UNIFYING_RECEIVER_1_PID 0xC52B +#define LOGITECH_G_NANO_RECEIVER_PID 0xC52F +#define LOGITECH_G_G700_RECEIVER_PID 0xC531 +#define LOGITECH_G_UNIFYING_RECEIVER_2_PID 0xC532 +#define LOGITECH_G_G602_RECEIVER_PID 0xC537 + +#define LOGITECH_G_LIGHTSPEED_RECEIVER_PID 0xC539 +#define LOGITECH_G403_LIGHTSPEED_VIRTUAL_PID 0x405D +#define LOGITECH_G502_LIGHTSPEED_VIRTUAL_PID 0x407F +#define LOGITECH_G703_LIGHTSPEED_VIRTUAL_PID 0x4070 +#define LOGITECH_G900_LIGHTSPEED_VIRTUAL_PID 0x4053 +#define LOGITECH_G903_LIGHTSPEED_VIRTUAL_PID 0x4067 +#define LOGITECH_GPRO_LIGHTSPEED_VIRTUAL_PID 0x4079 + /*-----------------------------------------------------*\ | Logitech Keyboards | \*-----------------------------------------------------*/ @@ -417,7 +431,6 @@ void DetectLogitechMouseG203L(hid_device_info* info, const std::string& name) } } - void DetectLogitechMouseG303(hid_device_info* info, const std::string& name) { addLogitechLightsyncMouse2zone(info, name, 0xFF, 0x0E, 0x3A); @@ -428,71 +441,6 @@ void DetectLogitechMouseG403(hid_device_info* info, const std::string& name) addLogitechLightsyncMouse2zone(info, name, 0xFF, 0x0E, 0x3A); } -void DetectLogitechMouseG403WW(hid_device_info* info, const std::string& name) -{ - addLogitechLightsyncMouse2zone(info, name, 0xFF, 0x18, 0x3A); -} - -void DetectLogitechMouseG502PS(hid_device_info* info, const std::string& name) -{ - addLogitechLightsyncMouse2zone(info, name, 0xFF, 0x02, 0x3A); -} - -void DetectLogitechMouseG502WW(hid_device_info* info, const std::string& name) -{ - addLogitechLightsyncMouse2zone(info, name, 0xFF, 0x07, 0x3A); -} - -void DetectLogitechMouseG703WW(hid_device_info* info, const std::string& name) -{ - addLogitechLightsyncMouse2zone(info, name, 0x01, 0x18, 0x3C); -} - -void DetectLogitechMouseG900WW(hid_device_info* info, const std::string& name) -{ - addLogitechLightsyncMouse2zone(info, name, 0x01, 0x17, 0x3A); -} - -void DetectLogitechMouseG903WW(hid_device_info* info, const std::string& name) -{ - addLogitechLightsyncMouse2zone(info, name, 0x01, 0x17, 0x3A); -} - -void DetectLogitechMouseGPRO(hid_device_info* info, const std::string& name) -{ - addLogitechLightsyncMouse2zone(info, name, 0x01, 0x07, 0x3C); -} - -void DetectLogitechMouseGLS(hid_device_info* info, const std::string& name) -{ - hid_device* dev = hid_open_path(info->path); - - if(dev) - { - /*---------------------------------------------*\ - | Create mutex to prevent the two controllers | - | from interfering with each other | - \*---------------------------------------------*/ - std::shared_ptr logitech_mutex = std::make_shared(); - - /*---------------------------------------------*\ - | Add mouse | - \*---------------------------------------------*/ - LogitechGLightsyncController* controller = new LogitechGLightsyncController(dev, dev, info->path, 0x01, 0x07, 0x3C, logitech_mutex); - RGBController_LogitechGLightsync* rgb_controller = new RGBController_LogitechGLightsync(controller); - rgb_controller->name = name; - ResourceManager::get()->RegisterRGBController(rgb_controller); - - /*---------------------------------------------*\ - | Add Powerplay mousemat | - \*---------------------------------------------*/ - LogitechGLightsyncController* mousemat_controller = new LogitechGLightsyncController(dev, dev, info->path, 0x07, 0x0B, 0x3C, logitech_mutex); - RGBController_LogitechGPowerPlay* mousemat_rgb_controller = new RGBController_LogitechGPowerPlay(mousemat_controller); - mousemat_rgb_controller->name = name; - ResourceManager::get()->RegisterRGBController(mousemat_rgb_controller); - } -} - /*-----------------------------------------------------*\ | Other Logitech Devices | \*-----------------------------------------------------*/ @@ -529,27 +477,189 @@ REGISTER_HID_DETECTOR_IP ("Logitech G910 Orion Spectrum", Dete /*-------------------------------------------------------------------------------------------------------------------------------------------------*\ | Mice | \*-------------------------------------------------------------------------------------------------------------------------------------------------*/ -REGISTER_HID_DETECTOR_IP("Logitech G203 Prodigy", DetectLogitechMouseG203, LOGITECH_VID, LOGITECH_G203_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IPU("Logitech G203 Lightsync", DetectLogitechMouseG203L, LOGITECH_VID, LOGITECH_G203L_PID, 1, 0xFF00, 2); -REGISTER_HID_DETECTOR_IP("Logitech G303 Daedalus Apex", DetectLogitechMouseG303, LOGITECH_VID, LOGITECH_G303_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G403 Prodigy", DetectLogitechMouseG403, LOGITECH_VID, LOGITECH_G403_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G403 Hero", DetectLogitechMouseG403, LOGITECH_VID, LOGITECH_G403H_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G403 Prodigy Wireless (wired)", DetectLogitechMouseG403WW, LOGITECH_VID, LOGITECH_G403_WIRELESS_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G502 Proteus Spectrum", DetectLogitechMouseG502PS, LOGITECH_VID, LOGITECH_G502_PS_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G502 Hero", DetectLogitechMouseG502PS, LOGITECH_VID, LOGITECH_G502H_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G502 LIGHTSPEED Wireless (wired)", DetectLogitechMouseG502WW, LOGITECH_VID, LOGITECH_G502_WIRELESS_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G703 Wireless (wired)", DetectLogitechMouseG703WW, LOGITECH_VID, LOGITECH_G703_WIRELESS_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G900 Chaos Spectrum Wireless (wired)", DetectLogitechMouseG900WW, LOGITECH_VID, LOGITECH_G900_WIRELESS_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G903 LIGHTSPEED Wireless (wired)", DetectLogitechMouseG903WW, LOGITECH_VID, LOGITECH_G903_WIRELESS_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G Pro Gaming Mouse", DetectLogitechMouseG203, LOGITECH_VID, LOGITECH_GPRO_WIRED_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G Pro (HERO) Gaming Mouse", DetectLogitechMouseG203, LOGITECH_VID, LOGITECH_GPRO_HERO_WIRED_PID, 1, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G Lightspeed Wireless Gaming Mouse", DetectLogitechMouseGPRO, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_WIRELESS_PID, 2, 0xFF00); -REGISTER_HID_DETECTOR_IP("Logitech G Pro Wireless Gaming Mouse (Wired)", DetectLogitechMouseGPRO, LOGITECH_VID, LOGITECH_GPRO_WIRELESS_PID, 2, 0xFF00); -/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ -| Mousemats | -\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ -REGISTER_HID_DETECTOR_IPU("Logitech G Powerplay Mousepad with Lightspeed", DetectLogitechMouseGLS, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_POWERPLAY_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IP ("Logitech G203 Prodigy", DetectLogitechMouseG203, LOGITECH_VID, LOGITECH_G203_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IPU("Logitech G203 Lightsync", DetectLogitechMouseG203L, LOGITECH_VID, LOGITECH_G203_LIGHTSYNC_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IP ("Logitech G303 Daedalus Apex", DetectLogitechMouseG303, LOGITECH_VID, LOGITECH_G303_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP ("Logitech G403 Hero", DetectLogitechMouseG403, LOGITECH_VID, LOGITECH_G403_HERO_PID, 1, 0xFF00); /*-------------------------------------------------------------------------------------------------------------------------------------------------*\ | Speakers | \*-------------------------------------------------------------------------------------------------------------------------------------------------*/ REGISTER_HID_DETECTOR_IPU("Logitech G560 Lightsync Speaker", DetectLogitechG560, LOGITECH_VID, LOGITECH_G560_PID, 2, 0xFF43, 514); + +/*---------------------------------------------------------------------------------------------------------*\ +| Windows and MacOS Lightspeed Detection | +| | +| The Lightspeed receiver is a unifying receiver that will only accept 1 connection | +| We must probe the receiver to check what is currently connected | +| | +| Hat tip - kernel driver https://github.com/torvalds/linux/blob/master/drivers/hid/hid-logitech-dj.c | +| - ltunify https://github.com/Lekensteyn/ltunify/ | +\*---------------------------------------------------------------------------------------------------------*/ +#if defined(_WIN32) || defined(__APPLE__) + +usages BundleLogitechUsages(hid_device_info* info) +{ + usages temp_usages; + + /*-----------------------------------------------------------------*\ + | Need a unique ID to group usages for 1 device if multiple exist | + | Grab all usages that you can open. For normal Logitech FAP | + | devices this will be usage 1, 2 and 4 | + \*-----------------------------------------------------------------*/ + if(info->usage == 1) + { + while(info) + { + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + temp_usages.emplace((uint8_t)info->usage, dev); + } + + info = info->next; + } + } + + return temp_usages; +} + +void CreateLogitechLightspeedDevice(char *path, usages device_usages, uint8_t device_index, bool wireless, std::shared_ptr mutex_ptr) +{ + LogitechGProWirelessController* controller = new LogitechGProWirelessController(device_usages.find(2)->second, path); + controller->lightspeed = new logitech_device(path, device_usages, device_index, wireless, mutex_ptr); + RGBController_LogitechGProWireless* rgb_controller = new RGBController_LogitechGProWireless(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); +} + +void DetectLogitechLightspeedReceiver(hid_device_info* info, const std::string& /*name*/) +{ + /*-----------------------------------------------------------------*\ + | Need to save the PID and the device path before iterating | + | over "info" in BundleLogitechUsages() | + \*-----------------------------------------------------------------*/ + char *path = info->path; + uint16_t dev_pid = info->product_id; + usages device_usages = BundleLogitechUsages(info); + + wireless_map wireless_devices; + unsigned int device_count = getWirelessDevice(device_usages, dev_pid, &wireless_devices); + + /*-----------------------------------------------------------------*\ + | Lightspeed Receivers will only have one paired /connected device | + | Unifying Receivers can have up to 6 devices paired / connected | + \*-----------------------------------------------------------------*/ + if(device_count > 0) + { + /*-------------------------------------------------*\ + | Create mutex to prevent the controllers sharing a | + | receiver from interfering with each other | + \*-------------------------------------------------*/ + std::shared_ptr logitech_mutex = std::make_shared(); + + for(wireless_map::iterator wd = wireless_devices.begin(); wd != wireless_devices.end(); wd++) + { + CreateLogitechLightspeedDevice(path, device_usages, wd->second, true, logitech_mutex); + } + } +} + +void DetectLogitechWired(hid_device_info* info, const std::string& /*name*/) +{ + /*-----------------------------------------------------------------*\ + | Need to save the device path before iterating | + | over "info" in BundleLogitechUsages() | + \*-----------------------------------------------------------------*/ + char *path = info->path; + usages device_usages = BundleLogitechUsages(info); + + if(device_usages.size() > 0) + { + CreateLogitechLightspeedDevice(path, device_usages, 0xFF, false, nullptr); + } +} + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Lightspeed Receivers (Windows Wireless) | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Logitech Lightspeed Receiver", DetectLogitechLightspeedReceiver, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_RECEIVER_PID, 2, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G Powerplay Mousepad", DetectLogitechLightspeedReceiver, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_POWERPLAY_PID, 2, 0xFF00); + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Lightspeed Devices (Windows Wired) | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IP("Logitech G403 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G403_LIGHTSPEED_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G502 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_PROTEUS_SPECTRUM_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G502 Hero Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_HERO_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G502 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_LIGHTSPEED_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G703 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G703_LIGHTSPEED_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G900 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G900_LIGHTSPEED_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G903 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G903_LIGHTSPEED_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G Pro Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G_PRO_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G Pro (HERO) Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G_PRO_HERO_PID, 1, 0xFF00); +REGISTER_HID_DETECTOR_IP("Logitech G Pro Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G_PRO_WIRED_PID, 2, 0xFF00); + +#endif + +/*---------------------------------------------------------------------------------------------------------*\ +| Linux Lightspeed Detection | +| | +| The Linux kernel handles detecting wireless devices connected to a Unifying Receiver. | +\*---------------------------------------------------------------------------------------------------------*/ +#ifdef __linux__ + +void CreateLogitechLightspeedDevice(hid_device_info* info, bool wireless) +{ + usages device_usages; + + hid_device* dev = hid_open_path(info->path); + + if(dev) + { + device_usages.emplace((uint8_t)info->usage, dev); + } + + if(device_usages.size() > 0) + { + LogitechGProWirelessController* controller = new LogitechGProWirelessController(device_usages.find(0)->second, info->path); + controller->lightspeed = new logitech_device(info->path, device_usages, 0xFF, wireless); + RGBController_LogitechGProWireless* rgb_controller = new RGBController_LogitechGProWireless(controller); + ResourceManager::get()->RegisterRGBController(rgb_controller); + } +} + +void DetectLogitechWireless(hid_device_info* info, const std::string& /*name*/) +{ + CreateLogitechLightspeedDevice(info, true); +} + +void DetectLogitechWired(hid_device_info* info, const std::string& /*name*/) +{ + CreateLogitechLightspeedDevice(info, false); +} + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Lightspeed Devices (Linux Wireless) | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech G403 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G403_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G502 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G502_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G703 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G703_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G900 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G900_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G903 Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_G903_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G Pro Wireless Gaming Mouse", DetectLogitechWireless, LOGITECH_VID, LOGITECH_GPRO_LIGHTSPEED_VIRTUAL_PID, 2, 0xFF00, 2); + +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Lightspeed Devices (Linux Wired) | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech G403 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G403_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G502 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_PROTEUS_SPECTRUM_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G502 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G502 Hero Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G502_HERO_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G703 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G703_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G900 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G900_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G903 Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G903_LIGHTSPEED_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G Pro Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G_PRO_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G Pro (HERO) Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G_PRO_HERO_PID, 1, 0xFF00, 2); +REGISTER_HID_DETECTOR_IPU("Logitech G Pro Wired Gaming Mouse", DetectLogitechWired, LOGITECH_VID, LOGITECH_G_PRO_WIRED_PID, 2, 0xFF00, 2); + +#endif diff --git a/Controllers/LogitechController/LogitechGProWirelessController.cpp b/Controllers/LogitechController/LogitechGProWirelessController.cpp new file mode 100644 index 00000000..3aa5395a --- /dev/null +++ b/Controllers/LogitechController/LogitechGProWirelessController.cpp @@ -0,0 +1,53 @@ +/*-----------------------------------------*\ +| LogitechGProWirelessController.cpp | +| | +| Driver for Logitech G Pro Wireless Gaming| +| Mouse lighting controller | +| | +| TheRogueZeta 8/5/2020 | +\*-----------------------------------------*/ + +#include "LogitechGProWirelessController.h" + +#include + +LogitechGProWirelessController::LogitechGProWirelessController(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; +} + +LogitechGProWirelessController::~LogitechGProWirelessController() +{ + delete lightspeed; +} + +std::string LogitechGProWirelessController::GetDeviceLocation() +{ + return("HID: " + location + " (Receiver) \r\nWireless Index: " + std::to_string(lightspeed->device_index)); +} + +std::string LogitechGProWirelessController::GetSerialString() +{ + wchar_t serial_string[128]; + hid_get_serial_number_string(dev, serial_string, 128); + + std::wstring return_wstring = serial_string; + std::string return_string(return_wstring.begin(), return_wstring.end()); + + return(return_string); +} + +void LogitechGProWirelessController::SendMouseMode + ( + unsigned char mode, + std::uint16_t speed, + unsigned char zone, + unsigned char red, + unsigned char green, + unsigned char blue + // unsigned char brightness + ) +{ + lightspeed->setMode(mode, speed, zone, red, green, blue, 0x64); +} diff --git a/Controllers/LogitechController/LogitechGProWirelessController.h b/Controllers/LogitechController/LogitechGProWirelessController.h new file mode 100644 index 00000000..92ab7115 --- /dev/null +++ b/Controllers/LogitechController/LogitechGProWirelessController.h @@ -0,0 +1,62 @@ +/*-----------------------------------------*\ +| LogitechGProWirelessController.h | +| | +| Definitions and types for Logitech G Pro | +| Wireless Gaming Mouse lighting controller| +| | +| TheRogueZeta 8/5/2020 | +\*-----------------------------------------*/ + +#include "RGBController.h" +#include "LogitechProtocolCommon.h" + +#include +#include + +#pragma once + +enum +{ + LOGITECH_G_PRO_WIRELESS_MODE_OFF = 0x00, + LOGITECH_G_PRO_WIRELESS_MODE_STATIC = 0x01, + LOGITECH_G_PRO_WIRELESS_MODE_CYCLE = 0x02, + LOGITECH_G_PRO_WIRELESS_MODE_BREATHING = 0x03, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are multiplied by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G_PRO_WIRELESS_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G_PRO_WIRELESS_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G_PRO_WIRELESS_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechGProWirelessController +{ +public: + LogitechGProWirelessController(hid_device* dev_handle, const char* path); + ~LogitechGProWirelessController(); + + logitech_device* lightspeed; + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void SendMouseMode + ( + unsigned char mode, + unsigned short speed, + unsigned char zone, + unsigned char red, + unsigned char green, + unsigned char blue +// unsigned char brightness + ); + +private: + hid_device* dev; + std::string location; +}; diff --git a/Controllers/LogitechController/LogitechProtocolCommon.cpp b/Controllers/LogitechController/LogitechProtocolCommon.cpp new file mode 100644 index 00000000..26403049 --- /dev/null +++ b/Controllers/LogitechController/LogitechProtocolCommon.cpp @@ -0,0 +1,587 @@ +/*-------------------------------------------------------------------*\ +| LogitechProtocolCommon.cpp | +| | +| Common Logitech RAP and FAP protocol calls | +| | +| Chris M (Dr_No) 4th May 2021 | +| | +\*-------------------------------------------------------------------*/ + +#include + +static std::vector logitech_RGB_pages = +{ + 0x8070, + 0x8071 +}; + +int getWirelessDevice(usages device_usages, uint16_t pid, wireless_map *wireless_devices) +{ + int result; + hid_device* dev_use1; + usages::iterator find_usage = device_usages.find(1); + if (find_usage == device_usages.end()) + { + LOG_NOTICE("Unable get_Wireless_Device due to missing FAP Short Message (0x10) usage"); + LOG_DEBUG("Dumping device usages:"); + for(usages::iterator dev = device_usages.begin(); dev != device_usages.end(); dev++) + { + LOG_DEBUG("Usage index:\t%i", dev->first); + } + } + else + { + dev_use1 = find_usage->second; + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + shortFAPrequest get_connected_devices; + get_connected_devices.init(LOGITECH_RECEIVER_ADDRESS, LOGITECH_GET_REGISTER_REQUEST); + + result = hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + result = hid_read_timeout(dev_use1, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + bool wireless_notifications = response.data[1] & 1; //Connected devices is a flag + + if (!wireless_notifications) + { + response.init(); //zero out the response + get_connected_devices.init(LOGITECH_RECEIVER_ADDRESS, LOGITECH_SET_REGISTER_REQUEST); + get_connected_devices.data[1] = 1; + result = hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + result = hid_read_timeout(dev_use1, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + + if(get_connected_devices.feature_index == 0x8F) + { + LOG_ERROR("Logitech Protocol error: %02X %02X %02X %02X %02X %02X %02X", get_connected_devices.report_id, get_connected_devices.device_index, get_connected_devices.feature_index, get_connected_devices.feature_command, get_connected_devices.data[0], get_connected_devices.data[1], get_connected_devices.data[2]); + } + } + + response.init(); //zero out the response + get_connected_devices.init(LOGITECH_RECEIVER_ADDRESS, LOGITECH_GET_REGISTER_REQUEST); + get_connected_devices.feature_command = 0x02; //0x02 Connection State register. Essentially asking for count of paired devices + result = hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + result = hid_read_timeout(dev_use1, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + + unsigned int device_count = response.data[1]; + LOG_NOTICE("Count of connected devices to %4X: %i", pid, device_count); + + if (device_count > 0) + { + LOG_NOTICE("Faking a reconnect to get device list"); + device_count++; //Add 1 to the device_count to include the receiver + + response.init(); + get_connected_devices.init(LOGITECH_RECEIVER_ADDRESS, LOGITECH_SET_REGISTER_REQUEST); + get_connected_devices.feature_index = LOGITECH_SET_REGISTER_REQUEST; + get_connected_devices.feature_command = 0x02; //0x02 Connection State register + get_connected_devices.data[0] = 0x02; //Writting 0x02 to the connection state register will ask the receiver to fake a reconnect of paired devices + result = hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + + for(size_t i = 0; i < device_count; i++) + { + blankFAPmessage devices; + devices.init(); + + hid_read_timeout(dev_use1, devices.buffer, devices.size(), LOGITECH_PROTOCOL_TIMEOUT); + unsigned int wireless_PID = (devices.data[2] << 8) | devices.data[1]; + LOG_NOTICE("Connected Device Index %i:\tVirtualID=%04X\t\t%02X %02X %02X %02X %02X %02X %02X", i, wireless_PID, devices.buffer[0], devices.buffer[1], devices.buffer[2], devices.buffer[3], devices.buffer[4], devices.buffer[5], devices.buffer[6]); + + /*-----------------------------------------------------------------*\ + | We need to read the receiver from the HID device queue but | + | there is no need to add it as it's own device | + \*-----------------------------------------------------------------*/ + if(devices.device_index != 0xFF) + { + wireless_devices->emplace(wireless_PID, devices.device_index); + } + } + } + else + { + LOG_WARNING("No devices were found connected to receiver!"); + } + } + + return wireless_devices->size(); +} + +logitech_device::logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless) +{ + device_index = _device_index; + location = path; + device_usages = _usages; + wireless = _wireless; + RGB_feature_index = 0; + LED_count = 0; + mutex = nullptr; + + initialiseDevice(); +} + +logitech_device::logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless, std::shared_ptr mutex_ptr) +{ + device_index = _device_index; + location = path; + device_usages = _usages; + wireless = _wireless; + RGB_feature_index = 0; + LED_count = 0; + mutex = mutex_ptr; + + initialiseDevice(); +} + +logitech_device::~logitech_device() +{ + for(usages::iterator dev = device_usages.begin(); dev != device_usages.end(); dev++) + { + hid_close(dev->second); + } +} + +void logitech_device::initialiseDevice() +{ + flushReadQueue(); + getDeviceName(); //This will get the name of the device if it exists + + for(std::vector::iterator page = logitech_RGB_pages.begin(); page != logitech_RGB_pages.end(); page++) + { + int feature_index = getFeatureIndex(*page); + if(feature_index > 0) + { + feature_list.emplace(*page, feature_index); + RGB_feature_index = feature_index; + } + } + + if (RGB_feature_index == 0) + { + /*-----------------------------------------------------------------*\ + | If there was no RGB Effect Feature page found | + | dump the entire Feature list to log | + \*-----------------------------------------------------------------*/ + getDeviceFeatureList(); //This will populate the feature list + LOG_NOTICE("Unable add this device due to missing RGB Effects Feature"); + for(features::iterator feature = feature_list.begin(); feature != feature_list.end(); feature++) + { + LOG_NOTICE("Feature Index: %02X\tFeature Page: %04X", feature->second, feature->first); + } + } + else + { + getRGBconfig(); + } +} + +bool logitech_device::connected() +{ + bool test = false; + hid_device* dev_use1 = getDevice(1); + + if(dev_use1) + { + blankFAPmessage response; + response.init(); //zero out the response + + shortFAPrequest get_connected_devices; + get_connected_devices.init(device_index, LOGITECH_GET_REGISTER_REQUEST); + get_connected_devices.feature_command = 0x02; //0x02 Connection State register. Essentially asking for count of paired devices + hid_write(dev_use1, get_connected_devices.buffer, get_connected_devices.size()); + hid_read_timeout(dev_use1, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + test = (response.data[1] != 0x09); //ERR_RESOURCE_ERROR i.e. not currently connected + } + return test; +} + +uint8_t logitech_device::getLEDinfo() +{ + /*-----------------------------------------------------------------*\ + | Get all info about the LEDs and Zones | + \*-----------------------------------------------------------------*/ + + return LED_count; +} + +void logitech_device::flushReadQueue() +{ + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + for(usages::iterator dev = device_usages.begin(); dev != device_usages.end(); dev++) + { + //Flush the buffer + int result = 1; + while( result > 0 ) + { + result = hid_read_timeout(dev->second, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + } +} + +hid_device* logitech_device::getDevice(uint8_t usage_index) +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage_index | + | Return the associated device if found otherwise a nullptr | + \*-----------------------------------------------------------------*/ +#ifdef WIN32 + usages::iterator find_usage = device_usages.find(usage_index); +#else + usages::iterator find_usage = device_usages.begin(); +#endif //WIN32 + + if (find_usage == device_usages.end()) + { + LOG_NOTICE("Unable add this device due to missing FAP Message usage %i", usage_index); + return nullptr; + } + else + { + return find_usage->second; + } +} + +uint8_t logitech_device::getFeatureIndex(uint16_t feature_page) +{ + /*-----------------------------------------------------------------*\ + | Get the feature index from the Root Index | + | Return the mapped feature_index of the given feature page | + | for this device or else return 0 | + \*-----------------------------------------------------------------*/ + int result = 0; + uint8_t feature_index = 0; + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + blankFAPmessage response; + longFAPrequest get_index; + get_index.init(device_index, LOGITECH_HIDPP_PAGE_ROOT_IDX, LOGITECH_CMD_ROOT_GET_FEATURE); + get_index.data[0] = feature_page >> 8; + get_index.data[1] = feature_page & 0xFF; + + result = hid_write(dev_use2, get_index.buffer, get_index.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + feature_index = response.data[0]; + } + return feature_index; +} + +int logitech_device::getDeviceFeatureList() +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage1 (0x10 Short FAP Message) & usage2 | + | (0x11 Long FAP Message) then list all features for device | + \*-----------------------------------------------------------------*/ + hid_device* dev_use1 = getDevice(1); + hid_device* dev_use2 = getDevice(2); + + if(dev_use1 && dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Query the root index for the index of the feature list | + | This is done for safety as it is generaly at feature index 0x01 | + \*-----------------------------------------------------------------*/ + shortFAPrequest get_index; + get_index.init(device_index, LOGITECH_HIDPP_PAGE_ROOT_IDX); + get_index.feature_command = LOGITECH_CMD_ROOT_GET_FEATURE; + get_index.data[0] = LOGITECH_HIDPP_PAGE_FEATURE_SET >> 8; //Get feature index of the Feature Set 0x0001 + get_index.data[1] = LOGITECH_HIDPP_PAGE_FEATURE_SET & 0xFF; + + result = hid_write(dev_use1, get_index.buffer, get_index.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + uint8_t feature_index = response.data[0]; + + /*-----------------------------------------------------------------*\ + | Get the count of Features | + \*-----------------------------------------------------------------*/ + shortFAPrequest get_count; + get_count.init(device_index, feature_index); + get_count.feature_command = LOGITECH_CMD_FEATURE_SET_GET_COUNT; + + result = hid_write(dev_use1, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + unsigned int feature_count = response.data[0]; + + shortFAPrequest get_features; + get_features.init(device_index, feature_index); + get_features.feature_command = LOGITECH_CMD_FEATURE_SET_GET_ID; + for(std::size_t i = 1; feature_list.size() < feature_count; i++ ) + { + get_features.data[0] = i; + result = hid_write(dev_use1, get_features.buffer, get_features.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LOG_DEBUG("Reading HID++ Feature %04X at index: %02X", (response.data[0] << 8) | response.data[1], i); + feature_list.emplace((response.data[0] << 8) | response.data[1], i); + } + } + else + { + if(!dev_use1) + { + LOG_NOTICE("Unable add this device due to missing FAP Short Message (0x10) usage"); + } + if(!dev_use2) + { + LOG_NOTICE("Unable add this device due to missing FAP Long Message (0x11) usage"); + } + } + + return feature_list.size(); +} + +int logitech_device::getDeviceName() +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | Then use it to get the name for this device | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + int result; + + /*-----------------------------------------------------------------*\ + | Check if the feature_list contains an index for the Device_name | + | feature otherwise query the root index. If not found return 0 | + \*-----------------------------------------------------------------*/ + uint8_t name_feature_index; + features::iterator find_feature = feature_list.find(LOGITECH_HIDPP_PAGE_DEVICE_NAME_TYPE); + if (find_feature == feature_list.end()) + { + longFAPrequest get_index; + get_index.init(device_index, LOGITECH_HIDPP_PAGE_ROOT_IDX, LOGITECH_CMD_ROOT_GET_FEATURE); + get_index.data[0] = LOGITECH_HIDPP_PAGE_DEVICE_NAME_TYPE >> 8; //Get feature index of the Feature Set 0x0001 + get_index.data[1] = LOGITECH_HIDPP_PAGE_DEVICE_NAME_TYPE & 0xFF; + + result = hid_write(dev_use2, get_index.buffer, get_index.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + name_feature_index = response.data[0]; + } + else + { + name_feature_index = find_feature->second; + } + + /*-----------------------------------------------------------------*\ + | Get the device name length | + \*-----------------------------------------------------------------*/ + longFAPrequest get_length; + get_length.init(device_index, name_feature_index, LOTITECH_CMD_DEVICE_NAME_TYPE_GET_COUNT); + result = hid_write(dev_use2, get_length.buffer, get_length.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + unsigned int name_length = response.data[0]; + + longFAPrequest get_name; + get_name.init(device_index, name_feature_index, LOGITECH_CMD_DEVICE_NAME_TYPE_GET_DEVICE_NAME); + while(device_name.length() < name_length) + { + get_name.data[0] = device_name.length(); //This sets the character index to get from the device + result = hid_write(dev_use2, get_name.buffer, get_name.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + std::string temp = (char *)&response.data; + device_name.append(temp); + } + + get_name.init(device_index, name_feature_index, LOGITECH_CMD_DEVICE_NAME_TYPE_GET_TYPE); + result = hid_write(dev_use2, get_name.buffer, get_name.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + logitech_device_type = response.data[0]; + } + + return device_name.length(); +} + +void logitech_device::getRGBconfig() +{ + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) | + | Then use it to get the name for this device | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + int result; + + longFAPrequest get_count; + get_count.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_GET_COUNT); + + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + LED_count = response.data[0]; + + get_count.feature_command = LOGITECH_CMD_RGB_EFFECTS_GET_INFO; + for(std::size_t i = 0; i < LED_count; i++ ) + { + //TODO: Push this info into a vector for later enumeration by the RGBController + get_count.data[0] = i; + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + + /*get_count.feature_command = LOGITECH_CMD_RGB_EFFECTS_GET_STATE; + for(std::size_t i = 0; i < feature_count; i++ ) + { + get_count.data[0] = i; + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + + get_count.feature_command = LOGITECH_CMD_RGB_EFFECTS_GET_CONFIG; + for(std::size_t i = 0; i < feature_count; i++ ) + { + get_count.data[0] = i; + result = hid_write(dev_use2, get_count.buffer, get_count.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + }*/ + } +} + +uint8_t logitech_device::setDirectMode(bool direct) +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage1 (0x10 Short FAP Message) & usage2 | + | (0x11 Long FAP Message) then set the device into direct mode | + | via register 0x8A | + \*-----------------------------------------------------------------*/ + hid_device* dev_use1 = getDevice(1); + hid_device* dev_use2 = getDevice(2); + + if(dev_use1 && 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_direct; + set_direct.init(device_index, RGB_feature_index); + set_direct.feature_command = LOGITECH_CMD_RGB_EFFECTS_UNKNOWN; + set_direct.data[0] = (direct) ? 1 : 0; + set_direct.data[1] = set_direct.data[0]; + + /*-----------------------------------------------------*\ + | 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_use1, set_direct.buffer, set_direct.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + result = hid_write(dev_use1, set_direct.buffer, set_direct.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + } + return result; +} + +uint8_t logitech_device::setMode(uint8_t mode, uint16_t speed, uint8_t zone, uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) +{ + int result = 0; + + /*-----------------------------------------------------------------*\ + | Check the usage map for usage2 (0x11 Long FAP Message) then | + | set the device mode via LOGITECH_CMD_RGB_EFFECTS_SET_CONTROL | + \*-----------------------------------------------------------------*/ + hid_device* dev_use2 = getDevice(2); + + if(dev_use2) + { + /*-----------------------------------------------------------------*\ + | Create a buffer for reads | + \*-----------------------------------------------------------------*/ + blankFAPmessage response; + response.init(); + + /*-----------------------------------------------------------------*\ + | Set the mode via the RGB_feature_index | + \*-----------------------------------------------------------------*/ + longFAPrequest set_mode; + set_mode.init(device_index, RGB_feature_index, LOGITECH_CMD_RGB_EFFECTS_SET_CONTROL); + set_mode.data[0] = zone; + set_mode.data[1] = mode; + + set_mode.data[2] = red; + set_mode.data[3] = green; + set_mode.data[4] = blue; + + speed *= 100; + if(mode == 1) //Static + { + set_mode.data[5] = zone; + } + else if(mode == 2) //Spectrum Cycle + { + set_mode.data[7] = speed >> 8; + set_mode.data[8] = speed & 0xFF; + set_mode.data[9] = brightness; + } + else if(mode == 3) //Breathing + { + set_mode.data[5] = speed >> 8; + set_mode.data[6] = speed & 0xFF; + set_mode.data[8] = brightness; + } + + /*-----------------------------------------------------*\ + | 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_mode.buffer, set_mode.size()); + result = hid_read_timeout(dev_use2, response.buffer, response.size(), LOGITECH_PROTOCOL_TIMEOUT); + } + else + { + result = hid_write(dev_use2, set_mode.buffer, set_mode.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 new file mode 100644 index 00000000..48be1606 --- /dev/null +++ b/Controllers/LogitechController/LogitechProtocolCommon.h @@ -0,0 +1,218 @@ +/*-------------------------------------------------------------------*\ +| LogitechProtocolCommon.h | +| | +| Common Logitech RAP and FAP protocol calls | +| | +| Chris M (Dr_No) 4th May 2021 | +| | +\*-------------------------------------------------------------------*/ + +#include +#include +#include "LogManager.h" + +#pragma once + +#define LOGITECH_PROTOCOL_TIMEOUT 300 //Timeout in ms +#define LOGITECH_HEADER_SIZE 3 +#define LOGITECH_SHORT_MESSAGE 0x10 +#define LOGITECH_SHORT_MESSAGE_LEN 7 +#define LOGITECH_LONG_MESSAGE 0x11 +#define LOGITECH_LONG_MESSAGE_LEN 20 +#define LOGITECH_FAP_RESPONSE_LEN 64 //Define a universal response buffer and allow the hidapi to determine the size + +#define LOGITECH_RECEIVER_ADDRESS 0xFF //The Unifying receiver uses RAP or register access protocol +#define LOGITECH_SET_REGISTER_REQUEST 0x80 +#define LOGITECH_GET_REGISTER_REQUEST 0x81 + +#define LOGITECH_HIDPP_PAGE_ROOT_IDX 0x00 //Used for querying the feature index +#define LOGITECH_CMD_ROOT_GET_FEATURE 0x01 +#define LOGITECH_CMD_ROOT_GET_PROTOCOL 0x11 + +#define LOGITECH_HIDPP_PAGE_FEATURE_SET 0x0001 +#define LOGITECH_CMD_FEATURE_SET_GET_COUNT 0x01 +#define LOGITECH_CMD_FEATURE_SET_GET_ID 0x11 + +#define LOGITECH_HIDPP_PAGE_DEVICE_NAME_TYPE 0x0005 +#define LOTITECH_CMD_DEVICE_NAME_TYPE_GET_COUNT 0x01 +#define LOGITECH_CMD_DEVICE_NAME_TYPE_GET_DEVICE_NAME 0x11 +#define LOGITECH_CMD_DEVICE_NAME_TYPE_GET_TYPE 0x21 + +#define LOGITECH_HIDPP_PAGE_RGB_EFFECTS1 0x8070 +#define LOGITECH_HIDPP_PAGE_RGB_EFFECTS2 0x8071 +#define LOGITECH_CMD_RGB_EFFECTS_GET_COUNT 0x00 +#define LOGITECH_CMD_RGB_EFFECTS_GET_INFO 0x10 +#define LOGITECH_CMD_RGB_EFFECTS_GET_CONTROL 0x20 +#define LOGITECH_CMD_RGB_EFFECTS_SET_CONTROL 0x30 +#define LOGITECH_CMD_RGB_EFFECTS_GET_STATE 0x40 +#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 +{ + LOGITECH_DEVICE_TYPE_KEYBOARD = 0, + LOGITECH_DEVICE_TYPE_REMOTECONTROL =1, + LOGITECH_DEVICE_TYPE_NUMPAD = 2, + LOGITECH_DEVICE_TYPE_MOUSE = 3, + LOGITECH_DEVICE_TYPE_TOUCHPAD = 4, + LOGITECH_DEVICE_TYPE_TRACKBALL = 5, + LOGITECH_DEVICE_TYPE_PRESENTER = 6, + LOGITECH_DEVICE_TYPE_RECEIVER = 7 +}; + +// Used for: {GET,SET}_REGISTER_{REQ,RSP}, SET_LONG_REGISTER_RSP, GET_LONG_REGISTER_REQ +struct message_short +{ + unsigned char address; + unsigned char data[3]; +}; + +// Used for: SET_LONG_REGISTER_REQ, GET_LONG_REGISTER_RSP +struct message_long +{ + unsigned char address; + unsigned char data[16]; +}; + +// Used for: ERROR_MSG +struct message_error +{ + unsigned char sub_id; + unsigned char address; + unsigned char error_code; + unsigned char padding; /* set to 0 */ +}; + +union shortFAPrequest +{ + uint8_t buffer[LOGITECH_SHORT_MESSAGE_LEN]; + struct + { + uint8_t report_id; + uint8_t device_index; + uint8_t feature_index; + uint8_t feature_command; + uint8_t data[LOGITECH_SHORT_MESSAGE_LEN - 4]; + }; + + void init(uint8_t device_index, uint8_t feature_index) + { + this->report_id = LOGITECH_SHORT_MESSAGE; + this->device_index = device_index; + this->feature_index = feature_index; + this->feature_command = 0; + for(size_t i = 0; i < sizeof(data); i++) + { + this->data[i] = 0; + } + }; + + int size() + { + return LOGITECH_SHORT_MESSAGE_LEN; + }; +}; + +union longFAPrequest +{ + uint8_t buffer[LOGITECH_LONG_MESSAGE_LEN]; + struct + { + uint8_t report_id; + uint8_t device_index; + uint8_t feature_index; + uint8_t feature_command; + uint8_t data[LOGITECH_LONG_MESSAGE_LEN - 4]; + }; + + void init(uint8_t device_index, uint8_t feature_index, uint8_t feature_command) + { + this->report_id = LOGITECH_LONG_MESSAGE; + this->device_index = device_index; + this->feature_index = feature_index; + this->feature_command = feature_command; + for(size_t i = 0; i < sizeof(data); i++) + { + this->data[i] = 0; + } + }; + + int size() + { + return LOGITECH_LONG_MESSAGE_LEN; + }; +}; + +union blankFAPmessage +{ + uint8_t buffer[LOGITECH_FAP_RESPONSE_LEN]; + struct + { + uint8_t command; + uint8_t device_index; + uint8_t feature_index; + uint8_t feature_command; + uint8_t data[LOGITECH_FAP_RESPONSE_LEN - 4]; + }; + + //blank this buffer entirely + void init() + { + for(size_t i = 0; i < sizeof(buffer); i++) + { + this->buffer[i] = 0; + } + }; + + int size() + { + return LOGITECH_FAP_RESPONSE_LEN; + }; +}; + +typedef std::map usages; +typedef std::map features; +typedef std::map wireless_map; + +int getWirelessDevice(usages _usages, uint16_t pid, wireless_map *wireless_devices); //Helper function needed outside of class + +class logitech_device +{ +public: + logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless); + logitech_device(char *path, usages _usages, uint8_t _device_index, bool _wireless, std::shared_ptr mutex_ptr); + + ~logitech_device(); + + /*-----------------------------------------------------------------*\ + | usages is a std::map that stores all the devices HID usages | + | This is to ensure that we can communicate to all windows usages | + \*-----------------------------------------------------------------*/ + usages device_usages; + features feature_list; + uint8_t device_index; + uint8_t RGB_feature_index; //Stored for quick use + uint8_t logitech_device_type; + bool wireless; + std::string device_name; + std::string location; + std::string protocol_version; + + bool connected(); + void flushReadQueue(); + uint8_t getFeatureIndex(uint16_t feature_page); + uint8_t getLEDinfo(); + 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); + int getDeviceName(); +private: + uint8_t LED_count; + std::shared_ptr mutex; + + hid_device* getDevice(uint8_t usage_index); + int getDeviceFeatureList(); + void getRGBconfig(); + void initialiseDevice(); +}; diff --git a/Controllers/LogitechController/RGBController_LogitechGProWireless.cpp b/Controllers/LogitechController/RGBController_LogitechGProWireless.cpp new file mode 100644 index 00000000..a7f40bc1 --- /dev/null +++ b/Controllers/LogitechController/RGBController_LogitechGProWireless.cpp @@ -0,0 +1,177 @@ +/*-----------------------------------------*\ +| RGBController_LogitechGProWireless.cpp | +| | +| Generic RGB Interface for | +| Logitech G Pro Wireless Gaming Mouse | +| | +| TheRogueZeta 8/5/2020 | +\*-----------------------------------------*/ + +#include "RGBController_LogitechGProWireless.h" + +RGBController_LogitechGProWireless::RGBController_LogitechGProWireless(LogitechGProWirelessController* logitech_ptr) +{ + logitech = logitech_ptr; + bool connected = logitech->lightspeed->connected(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G_PRO_WIRELESS_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_NONE; + modes.push_back(Off); + + if(connected) + { + name = logitech->lightspeed->device_name; + vendor = "Logitech"; + description = "Logitech Wireless Lightspeed Device"; + location = logitech->GetDeviceLocation(); + serial = logitech->GetSerialString(); + + switch(logitech->lightspeed->logitech_device_type) + { + case LOGITECH_DEVICE_TYPE_KEYBOARD: + type = DEVICE_TYPE_KEYBOARD; + break; + + case LOGITECH_DEVICE_TYPE_MOUSE: + type = DEVICE_TYPE_MOUSE; + break; + + default: + type = DEVICE_TYPE_UNKNOWN; + LOG_NOTICE("Logitech device type not known: %i", logitech->lightspeed->logitech_device_type); + } + + mode Direct; + Direct.name = "Direct"; + Direct.value = 0xFF; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + + mode Static; + Static.name = "Static"; + Static.value = LOGITECH_G_PRO_WIRELESS_MODE_STATIC; + Static.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Static.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Static); + + mode Cycle; + Cycle.name = "Spectrum Cycle"; + Cycle.value = LOGITECH_G_PRO_WIRELESS_MODE_CYCLE; + Cycle.flags = MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Cycle.color_mode = MODE_COLORS_NONE; + Cycle.speed_min = LOGITECH_G_PRO_WIRELESS_SPEED_SLOWEST; + Cycle.speed_max = LOGITECH_G_PRO_WIRELESS_SPEED_FASTEST; + Cycle.speed = LOGITECH_G_PRO_WIRELESS_SPEED_NORMAL; + modes.push_back(Cycle); + + mode Breathing; + Breathing.name = "Breathing"; + Breathing.value = LOGITECH_G_PRO_WIRELESS_MODE_BREATHING; + Breathing.flags = MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_HAS_SPEED | MODE_FLAG_HAS_BRIGHTNESS; + Breathing.color_mode = MODE_COLORS_PER_LED; + Breathing.speed_min = LOGITECH_G_PRO_WIRELESS_SPEED_SLOWEST; + Breathing.speed_max = LOGITECH_G_PRO_WIRELESS_SPEED_FASTEST; + Breathing.speed = LOGITECH_G_PRO_WIRELESS_SPEED_NORMAL; + modes.push_back(Breathing); + + SetupZones(); + } + else + { + name = "Idle Lightspeed Device"; + vendor = "Logitech"; + type = DEVICE_TYPE_UNKNOWN; + description = "Idle Logitech Wireless Lightspeed Device"; + location = logitech->GetDeviceLocation(); + serial = logitech->GetSerialString(); + } +} + +RGBController_LogitechGProWireless::~RGBController_LogitechGProWireless() +{ + delete logitech; +} + +void RGBController_LogitechGProWireless::SetupZones() +{ + uint8_t LED_count = logitech->lightspeed->getLEDinfo(); + + if(LED_count > 0) + { + for(size_t i = 0; i < LED_count; i++) + { + zone GProWireless_logo_zone; + GProWireless_logo_zone.name = "Zone " + std::to_string(i); + GProWireless_logo_zone.type = ZONE_TYPE_SINGLE; + GProWireless_logo_zone.leds_min = 1; + GProWireless_logo_zone.leds_max = 1; + GProWireless_logo_zone.leds_count = 1; + GProWireless_logo_zone.matrix_map = NULL; + zones.push_back(GProWireless_logo_zone); + + led GProWireless_logo_led; + GProWireless_logo_led.name = "LED " + std::to_string(i); + GProWireless_logo_led.value = i; + leds.push_back(GProWireless_logo_led); + } + } + + SetupColors(); +} + +void RGBController_LogitechGProWireless::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechGProWireless::DeviceUpdateLEDs() +{ + for(std::vector::iterator led_index = leds.begin(); led_index != leds.end(); led_index++) + { + UpdateZoneLEDs(led_index->value); + } +} + +void RGBController_LogitechGProWireless::UpdateZoneLEDs(int zone) +{ + unsigned char red = RGBGetRValue(colors[zone]); + unsigned char grn = RGBGetGValue(colors[zone]); + unsigned char blu = RGBGetBValue(colors[zone]); + + /*---------------------------------------------------------*\ + | Replace direct mode with static when sending to controller| + \*---------------------------------------------------------*/ + unsigned char temp_mode = (modes[active_mode].value == 0xFF) ? LOGITECH_G_PRO_WIRELESS_MODE_STATIC : modes[active_mode].value; + + logitech->SendMouseMode(temp_mode, modes[active_mode].speed, zone, red, grn, blu); +} + +void RGBController_LogitechGProWireless::UpdateSingleLED(int led) +{ + UpdateZoneLEDs(led); +} + +void RGBController_LogitechGProWireless::SetCustomMode() +{ + if(logitech->lightspeed->connected()) + { + active_mode = 1; + } +} + +void RGBController_LogitechGProWireless::DeviceUpdateMode() +{ + /*---------------------------------------------------------*\ + | If direct mode is true, then sent the packet to put the | + | mouse in direct mode. This code will only be called when | + | we change modes as to not spam the device. | + \*---------------------------------------------------------*/ + logitech->lightspeed->setDirectMode(modes[active_mode].value == 0xFF); + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/RGBController_LogitechGProWireless.h b/Controllers/LogitechController/RGBController_LogitechGProWireless.h new file mode 100644 index 00000000..14b41e01 --- /dev/null +++ b/Controllers/LogitechController/RGBController_LogitechGProWireless.h @@ -0,0 +1,33 @@ +/*-----------------------------------------*\ +| RGBController_LogitechGProWireless.h | +| | +| Generic RGB Interface for | +| Logitech G Pro Wireless Gaming Mouse | +| | +| TheRogueZeta 8/5/2020 | +\*-----------------------------------------*/ + +#pragma once +#include "RGBController.h" +#include "LogitechGProWirelessController.h" + +class RGBController_LogitechGProWireless : public RGBController +{ +public: + RGBController_LogitechGProWireless(LogitechGProWirelessController* logitech_ptr); + ~RGBController_LogitechGProWireless(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void DeviceUpdateMode(); + +private: + LogitechGProWirelessController* logitech; +}; diff --git a/OpenRGB.pro b/OpenRGB.pro index a61e796c..29019675 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -129,6 +129,7 @@ INCLUDEPATH += qt/ HEADERS += \ + Controllers/LogitechController/LogitechProtocolCommon.h \ dependencies/ColorWheel/ColorWheel.h \ dependencies/json/json.hpp \ dependencies/libcmmk/include/libcmmk/libcmmk.h \ @@ -284,6 +285,7 @@ HEADERS += Controllers/LogitechController/LogitechG910Controller.h \ Controllers/LogitechController/LogitechG815Controller.h \ Controllers/LogitechController/LogitechGLightsyncController.h \ + Controllers/LogitechController/LogitechGProWirelessController.h \ Controllers/LogitechController/RGBController_LogitechG203L.h \ Controllers/LogitechController/RGBController_LogitechG213.h \ Controllers/LogitechController/RGBController_LogitechG560.h \ @@ -293,6 +295,7 @@ HEADERS += Controllers/LogitechController/RGBController_LogitechGLightsync.h \ Controllers/LogitechController/RGBController_LogitechGLightsync1zone.h \ Controllers/LogitechController/RGBController_LogitechGPowerPlay.h \ + Controllers/LogitechController/RGBController_LogitechGProWireless.h \ Controllers/MSI3ZoneController/MSI3ZoneController.h \ Controllers/MSI3ZoneController/RGBController_MSI3Zone.h \ Controllers/MSIGPUController/MSIGPUController.h \ @@ -370,6 +373,7 @@ HEADERS += RGBController/RGBController_Network.h \ SOURCES += \ + Controllers/LogitechController/LogitechProtocolCommon.cpp \ dependencies/dmiinfo.cpp \ dependencies/ColorWheel/ColorWheel.cpp \ dependencies/libe131/src/e131.c \ @@ -565,6 +569,7 @@ SOURCES += Controllers/LogitechController/LogitechG910Controller.cpp \ Controllers/LogitechController/LogitechG815Controller.cpp \ Controllers/LogitechController/LogitechGLightsyncController.cpp \ + Controllers/LogitechController/LogitechGProWirelessController.cpp \ Controllers/LogitechController/RGBController_LogitechG203L.cpp \ Controllers/LogitechController/RGBController_LogitechG213.cpp \ Controllers/LogitechController/RGBController_LogitechG560.cpp \ @@ -574,6 +579,7 @@ SOURCES += Controllers/LogitechController/RGBController_LogitechGLightsync.cpp \ Controllers/LogitechController/RGBController_LogitechGLightsync1zone.cpp \ Controllers/LogitechController/RGBController_LogitechGPowerPlay.cpp \ + Controllers/LogitechController/RGBController_LogitechGProWireless.cpp \ Controllers/MSI3ZoneController/MSI3ZoneController.cpp \ Controllers/MSI3ZoneController/MSI3ZoneControllerDetect.cpp \ Controllers/MSI3ZoneController/RGBController_MSI3Zone.cpp \ diff --git a/cli.cpp b/cli.cpp index 823271f0..309716c1 100644 --- a/cli.cpp +++ b/cli.cpp @@ -1167,7 +1167,7 @@ unsigned int cli_pre_detection(int argc, char *argv[]) print_help = true; break; } - cfg_args++; + cfg_args+= 2; arg_index++; }