Enumerate Wireless connected Logitech Lightspeed (Unifying) devices

+ Added common library for Logitech Protocol
    + Moved wireless detection to the LogitechProtocolCommon.cpp
    + Adding Direct Mode to the wireless control
    + Copying the mutex from Lightsync controller to avoid interference
    + Adding LED count info to controller constructor

+ Created a new Logitech class
    + Added Feature list enumeration
    + Added DeviceName detection from device

* Changed LogitechGProWirelessController to accomodate generic devices
    * LED count from device is now used in RGBController setup

+ Adding Windows specific detection as Linux Kernel handles this already.
+ Adding virtual PIDS for wireless detection
    * LOGITECH G403
    * LOGITECH G502
    * LOGITECH G703
    * LOGITECH G900
    * LOGITECH G903
    * LOGITECH GPRO
+ Adding Logitech G900 Wired Gaming Mouse PID
+ Adding other all lightspeed mice to wired detector for testing
    * Genericised and optimised code paths
    * Speed up wireless detection

Commit amended for code style by Adam Honse <calcprogrammer1@gmail.com>
This commit is contained in:
Chris
2021-05-30 17:39:07 +10:00
committed by Adam Honse
parent d8252281ce
commit 259ba898b0
9 changed files with 1353 additions and 107 deletions

View File

@@ -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 <vector>
#include <hidapi/hidapi.h>
#include <LogManager.h>
/*-----------------------------------------------------*\
| 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<std::mutex> logitech_mutex = std::make_shared<std::mutex>();
/*---------------------------------------------*\
| 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<std::mutex> 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<std::mutex> logitech_mutex = std::make_shared<std::mutex>();
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

View File

@@ -0,0 +1,53 @@
/*-----------------------------------------*\
| LogitechGProWirelessController.cpp |
| |
| Driver for Logitech G Pro Wireless Gaming|
| Mouse lighting controller |
| |
| TheRogueZeta 8/5/2020 |
\*-----------------------------------------*/
#include "LogitechGProWirelessController.h"
#include <cstring>
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);
}

View File

@@ -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 <string>
#include <hidapi/hidapi.h>
#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;
};

View File

@@ -0,0 +1,587 @@
/*-------------------------------------------------------------------*\
| LogitechProtocolCommon.cpp |
| |
| Common Logitech RAP and FAP protocol calls |
| |
| Chris M (Dr_No) 4th May 2021 |
| |
\*-------------------------------------------------------------------*/
#include <LogitechProtocolCommon.h>
static std::vector<uint16_t> 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<std::mutex> 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<uint16_t>::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<std::mutex> 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<std::mutex> 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;
}

View File

@@ -0,0 +1,218 @@
/*-------------------------------------------------------------------*\
| LogitechProtocolCommon.h |
| |
| Common Logitech RAP and FAP protocol calls |
| |
| Chris M (Dr_No) 4th May 2021 |
| |
\*-------------------------------------------------------------------*/
#include <map>
#include <hidapi/hidapi.h>
#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<uint8_t, hid_device*> usages;
typedef std::map<uint16_t, uint8_t> features;
typedef std::map<uint16_t, uint8_t> 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<std::mutex> 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<std::mutex> mutex;
hid_device* getDevice(uint8_t usage_index);
int getDeviceFeatureList();
void getRGBconfig();
void initialiseDevice();
};

View File

@@ -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<led>::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();
}

View File

@@ -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;
};

View File

@@ -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 \

View File

@@ -1167,7 +1167,7 @@ unsigned int cli_pre_detection(int argc, char *argv[])
print_help = true;
break;
}
cfg_args++;
cfg_args+= 2;
arg_index++;
}