Logitech G560 initial support

Logitech G560 Speaker working with Direct Mode using same bits as the Logitech Ambilight and Audioviz. Other modes are not working (get) as expected.

Note: G560 Direct mode is slow because Logitech hardware implementation but it is working.

Changing volume at the same time colors are changing can reduce FPS because G560 uses the same channel for led and audio modes commands.

Commits squashed and amended for code style by Adam Honse <calcprogrammer1@gmail.com>
This commit is contained in:
Cheerpipe 2021-02-13 00:57:53 -03:00 committed by Adam Honse
parent 00b8e74239
commit e9f3a1b3b9
12 changed files with 427 additions and 1 deletions

View file

@ -7,6 +7,7 @@
#include "LogitechG213Controller.h"
#include "LogitechGProWirelessController.h"
#include "LogitechGPowerPlayController.h"
#include "LogitechG560Controller.h"
#include "RGBController.h"
#include "RGBController_LogitechG203.h"
#include "RGBController_LogitechG203L.h"
@ -16,6 +17,7 @@
#include "RGBController_LogitechG213.h"
#include "RGBController_LogitechGProWireless.h"
#include "RGBController_LogitechGPowerPlay.h"
#include "RGBController_LogitechG560.h"
#include <vector>
#include <hidapi/hidapi.h>
@ -50,6 +52,10 @@
| Mousemat product IDs |
\*-----------------------------------------------------*/
#define LOGITECH_G_LIGHTSPEED_POWERPLAY_PID 0xC53A
/*-----------------------------------------------------*\
| Speaker product IDs |
\*-----------------------------------------------------*/
#define LOGITECH_G560_PID 0x0A78
void DetectLogitechKeyboardG810(hid_device_info* info, const std::string& name)
{
@ -199,6 +205,19 @@ void DetectLogitechMouseGLS(hid_device_info* info, const std::string& name)
}
}
void DetectLogitechG560(hid_device_info* info, const std::string& name)
{
hid_device* dev = hid_open_path(info->path);
if(dev)
{
//Add G560 Speaker
LogitechG560Controller* speaker_controller = new LogitechG560Controller(dev, info->path);
RGBController_LogitechG560* speaker_rgb_controller = new RGBController_LogitechG560(speaker_controller);
speaker_rgb_controller->name = name;
ResourceManager::get()->RegisterRGBController(speaker_rgb_controller);
}
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
| Keyboards |
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
@ -226,3 +245,7 @@ REGISTER_HID_DETECTOR_IPU("Logitech G Pro Wireless Gaming Mouse (Wired)", Detec
| Mousemats |
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
REGISTER_HID_DETECTOR_IPU("Logitech G Powerplay Mousepad with Lightspeed", DetectLogitechMouseGLS, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_POWERPLAY_PID, 2, 0xFF00, 2);
/*-------------------------------------------------------------------------------------------------------------------------------------------------*\
| Speakers |
\*-------------------------------------------------------------------------------------------------------------------------------------------------*/
REGISTER_HID_DETECTOR_IPU("Logitech G560 Lightsync Speaker", DetectLogitechG560, LOGITECH_VID, LOGITECH_G560_PID, 2, 0xFF43, 514);

View file

@ -0,0 +1,147 @@
/*-----------------------------------------*\
| LogitechG560Controller.cpp |
| |
| Driver for Logitech G560 RGB Speaker |
| Charging System |
| |
| Cheerpipe 10/28/2020 |
| Based on |
| TheRogueZeta 8/31/2020 |
\*-----------------------------------------*/
#include "LogitechG560Controller.h"
#include <cstring>
#include <stdio.h>
#include <stdlib.h>
using namespace std::chrono_literals;
LogitechG560Controller::LogitechG560Controller(hid_device* dev_handle, const char* path)
{
dev = dev_handle;
location = path;
}
LogitechG560Controller::~LogitechG560Controller()
{
hid_close(dev);
}
std::string LogitechG560Controller::GetDeviceLocation()
{
return("HID: " + location);
}
void LogitechG560Controller::SetDirectMode(uint8_t zone)
{
unsigned char usb_buf[20];
usb_buf[0x00] = 0x11;
usb_buf[0x01] = 0xFF;
usb_buf[0x02] = 0x04;
usb_buf[0x03] = 0xCA;
usb_buf[0x04] = zone;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
fail_retry_write(dev, usb_buf, 20);
}
void LogitechG560Controller::SetOffMode(uint8_t zone)
{
unsigned char usb_buf[20];
usb_buf[0x00] = 0x11;
usb_buf[0x01] = 0xFF;
usb_buf[0x02] = 0x04;
usb_buf[0x03] = 0x3F;
usb_buf[0x04] = zone;
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
fail_retry_write(dev, usb_buf, 20);
}
void LogitechG560Controller::SendSpeakerMode
(
unsigned char zone, //0x04
unsigned char mode, //0x05
unsigned char speed, //0x0B // not working so far
unsigned char red, //0x06
unsigned char green, //0x07
unsigned char blue //0x08
)
{
unsigned char usb_buf[20];
/*-----------------------------------------------------*\
| Zero out buffer |
\*-----------------------------------------------------*/
memset(usb_buf, 0x00, sizeof(usb_buf));
/*-----------------------------------------------------*\
| Set up Lighting Control |
\*-----------------------------------------------------*/
usb_buf[0x00] = 0x11;
usb_buf[0x01] = 0xFF;
usb_buf[0x02] = 0x04;
/*-----------------------------------------------------*\
| This packet sets speaker into direct mode. This mode |
| is used by Lightsync Ambilight and Music Visualizer |
| realtime effect. |
\*-----------------------------------------------------*/
usb_buf[0x03] = 0x3A;
/*-----------------------------------------------------*\
| Set up mode and speed |
\*-----------------------------------------------------*/
usb_buf[0x04] = zone;
usb_buf[0x05] = mode;
/*-----------------------------------------------------*\
| And set up the colors |
\*-----------------------------------------------------*/
usb_buf[0x06] = red;
usb_buf[0x07] = green;
usb_buf[0x08] = blue;
if(mode == LOGITECH_G560_MODE_DIRECT) //G560 only has Direct Mode.
{
usb_buf[0x09] = 0x02;
}
/*-----------------------------------------------------*\
| Send packet |
\*-----------------------------------------------------*/
fail_retry_write(dev, usb_buf, 20);
}
void LogitechG560Controller::fail_retry_write(hid_device *device, const unsigned char *data, size_t length)
{
unsigned char usb_buf_out[33];
uint8_t write_max_retry=3;
do
{
std::this_thread::sleep_for(1ms);
int ret = hid_write(device, data, length);
/*-------------------------------------------------------------------------------------*\
| HID write fails if a change led color and set volume command are sent at |
| the same time because RGB controller and volume control shares the same interface. |
\*-------------------------------------------------------------------------------------*/
if (ret > 0)
{
std::this_thread::sleep_for(1ms);
hid_read_timeout(dev, usb_buf_out, 33, 100);
break;
}
else
{
write_max_retry--;
}
}while (write_max_retry > 0);
}

View file

@ -0,0 +1,62 @@
/*-----------------------------------------*\
| LogitechG560Controller.h |
| |
| Definitions and types for Logitech G560 |
| RGB Speaker |
| |
| Cheerpipe 10/28/2020 |
| Based on |
| TheRogueZeta 8/31/2020 |
\*-----------------------------------------*/
#include "RGBController.h"
#include <string>
#include <hidapi/hidapi.h>
#pragma once
enum
{
LOGITECH_G560_MODE_OFF = 0x00,
LOGITECH_G560_MODE_DIRECT = 0x01,
LOGITECH_G560_MODE_CYCLE = 0x02,
LOGITECH_G560_MODE_BREATHING = 0x03,
};
/*---------------------------------------------------------------------------------------------*\
| Speed is 1000 for fast and 20000 for slow. |
| Values are mutipled by 100 later to give lots of GUI steps. |
\*---------------------------------------------------------------------------------------------*/
enum
{
LOGITECH_G560_SPEED_SLOWEST = 0xC8, /* Slowest speed */
LOGITECH_G560_SPEED_NORMAL = 0x32, /* Normal speed */
LOGITECH_G560_SPEED_FASTEST = 0x0A, /* Fastest speed */
};
class LogitechG560Controller
{
public:
LogitechG560Controller(hid_device* dev_handle, const char* path);
~LogitechG560Controller();
std::string GetDeviceLocation();
void SetDirectMode(uint8_t zone);
void SetOffMode(uint8_t zone);
void SendSpeakerMode
(
unsigned char zone, //0x04
unsigned char mode, //0x05
unsigned char speed, //0x0B
unsigned char red, //0x06
unsigned char green, //0x07
unsigned char blue //0x08
);
private:
hid_device* dev;
std::string location;
void fail_retry_write(hid_device *device, const unsigned char *data, size_t length);
};

View file

@ -0,0 +1,148 @@
/*-----------------------------------------*\
| RGBController_LogitechG560.cpp |
| |
| Generic RGB Interface for |
| Logitech G560 RGB Speaker |
| |
| Cheerpipe 10/28/2020 |
| Based on |
| TheRogueZeta 8/31/2020 |
\*-----------------------------------------*/
#include "RGBController_LogitechG560.h"
RGBController_LogitechG560::RGBController_LogitechG560(LogitechG560Controller* logitech_ptr)
{
logitech = logitech_ptr;
name = "Logitech G560 Lightsync Speaker";
vendor = "Logitech";
type = DEVICE_TYPE_SPEAKER;
description = "Logitech G560 Lightsync Speaker";
location = logitech->GetDeviceLocation();
mode Off;
Off.name = "Off";
Off.value = LOGITECH_G560_MODE_OFF;
Off.flags = 0;
Off.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Off);
mode Direct;
Direct.name = "Direct";
Direct.value = LOGITECH_G560_MODE_DIRECT;
Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR;
Direct.color_mode = MODE_COLORS_PER_LED;
modes.push_back(Direct);
SetupZones();
}
void RGBController_LogitechG560::SetupZones()
{
zone G560_left_front;
G560_left_front.name = "Left Front";
G560_left_front.type = ZONE_TYPE_SINGLE;
G560_left_front.leds_min = 1;
G560_left_front.leds_max = 1;
G560_left_front.leds_count = 1;
G560_left_front.matrix_map = NULL;
zones.push_back(G560_left_front);
led G560_left_front_led;
G560_left_front_led.name = "Left Front";
G560_left_front_led.value = 0x00;
leds.push_back(G560_left_front_led);
zone G560_right_front;
G560_right_front.name = "Right Front";
G560_right_front.type = ZONE_TYPE_SINGLE;
G560_right_front.leds_min = 1;
G560_right_front.leds_max = 1;
G560_right_front.leds_count = 1;
G560_right_front.matrix_map = NULL;
zones.push_back(G560_right_front);
led G560_right_front_led;
G560_right_front_led.name = "Right Front";
G560_right_front_led.value = 0x01;
leds.push_back(G560_right_front_led);
zone G560_left_rear;
G560_left_rear.name = "Left Rear";
G560_left_rear.type = ZONE_TYPE_SINGLE;
G560_left_rear.leds_min = 1;
G560_left_rear.leds_max = 1;
G560_left_rear.leds_count = 1;
G560_left_rear.matrix_map = NULL;
zones.push_back(G560_left_rear);
led G560_left_read_led;
G560_left_read_led.name = "Left Rear";
G560_left_read_led.value = 0x02;
leds.push_back(G560_left_read_led);
zone G560_right_rear;
G560_right_rear.name = "Right Rear";
G560_right_rear.type = ZONE_TYPE_SINGLE;
G560_right_rear.leds_min = 1;
G560_right_rear.leds_max = 1;
G560_right_rear.leds_count = 1;
G560_right_rear.matrix_map = NULL;
zones.push_back(G560_right_rear);
led G560_right_rear_led;
G560_right_rear_led.name = "Right Rear";
G560_right_rear_led.value = 0x03;
leds.push_back(G560_right_rear_led);
SetupColors();
}
void RGBController_LogitechG560::ResizeZone(int /*zone*/, int /*new_size*/)
{
/*---------------------------------------------------------*\
| This device does not support resizing zones |
\*---------------------------------------------------------*/
}
void RGBController_LogitechG560::DeviceUpdateLEDs()
{
for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++)
{
unsigned char red = RGBGetRValue(colors[led_idx]);
unsigned char grn = RGBGetGValue(colors[led_idx]);
unsigned char blu = RGBGetBValue(colors[led_idx]);
if (modes[active_mode].value==LOGITECH_G560_MODE_DIRECT)
{
logitech->SetDirectMode(leds[led_idx].value); //Required to "reset" RGB controller and start receiving color in direct mode
}
else if (modes[active_mode].value==LOGITECH_G560_MODE_OFF)
{
logitech->SetOffMode(leds[led_idx].value);
}
logitech->SendSpeakerMode((unsigned char)leds[led_idx].value, modes[active_mode].value, modes[active_mode].speed,red, grn, blu);
}
}
void RGBController_LogitechG560::UpdateZoneLEDs(int zone)
{
DeviceUpdateLEDs();
}
void RGBController_LogitechG560::UpdateSingleLED(int led)
{
DeviceUpdateLEDs();
}
void RGBController_LogitechG560::SetCustomMode()
{
active_mode = 1;
}
void RGBController_LogitechG560::DeviceUpdateMode()
{
DeviceUpdateLEDs();
}

View file

@ -0,0 +1,34 @@
/*-----------------------------------------*\
| RGBController_LogitechG560.h |
| |
| Generic RGB Interface for |
| G560 RGB Speaker |
| |
| Cheerpipe 10/28/2020 |
| Based on |
| TheRogueZeta 8/31/2020 |
\*-----------------------------------------*/
#pragma once
#include "RGBController.h"
#include "LogitechG560Controller.h"
class RGBController_LogitechG560 : public RGBController
{
public:
RGBController_LogitechG560(LogitechG560Controller* logitech_ptr);
void SetupZones();
void ResizeZone(int zone, int new_size);
void DeviceUpdateLEDs();
void UpdateZoneLEDs(int zone);
void UpdateSingleLED(int led);
void SetCustomMode();
void DeviceUpdateMode();
private:
LogitechG560Controller* logitech;
};

View file

@ -229,7 +229,7 @@ HEADERS +=
Controllers/HyperXMouseController/HyperXPulsefireDartController.h \
Controllers/HyperXMouseController/RGBController_HyperXPulsefireFPSPro.h \
Controllers/HyperXMouseController/RGBController_HyperXPulsefireSurge.h \
Controllers/HyperXMouseController/RGBController_HyperXPulsefireDart.h \
Controllers/HyperXMouseController/RGBController_HyperXPulsefireDart.h \
Controllers/HyperXMousematController/HyperXMousematController.h \
Controllers/HyperXMousematController/RGBController_HyperXMousemat.h \
Controllers/LEDStripController/LEDStripController.h \
@ -238,6 +238,7 @@ HEADERS +=
Controllers/LogitechController/LogitechG203LController.h \
Controllers/LogitechController/LogitechG403Controller.h \
Controllers/LogitechController/LogitechG502PSController.h \
Controllers/LogitechController/LogitechG560Controller.h \
Controllers/LogitechController/LogitechG810Controller.h \
Controllers/LogitechController/LogitechG213Controller.h \
Controllers/LogitechController/LogitechGPowerPlayController.h \
@ -246,6 +247,7 @@ HEADERS +=
Controllers/LogitechController/RGBController_LogitechG203L.h \
Controllers/LogitechController/RGBController_LogitechG403.h \
Controllers/LogitechController/RGBController_LogitechG502PS.h \
Controllers/LogitechController/RGBController_LogitechG560.h \
Controllers/LogitechController/RGBController_LogitechG810.h \
Controllers/LogitechController/RGBController_LogitechG213.h \
Controllers/LogitechController/RGBController_LogitechGPowerPlay.h \
@ -473,6 +475,7 @@ SOURCES +=
Controllers/LogitechController/LogitechG203LController.cpp \
Controllers/LogitechController/LogitechG403Controller.cpp \
Controllers/LogitechController/LogitechG502PSController.cpp \
Controllers/LogitechController/LogitechG560Controller.cpp \
Controllers/LogitechController/LogitechG810Controller.cpp \
Controllers/LogitechController/LogitechG213Controller.cpp \
Controllers/LogitechController/LogitechGPowerPlayController.cpp \
@ -481,6 +484,7 @@ SOURCES +=
Controllers/LogitechController/RGBController_LogitechG203L.cpp \
Controllers/LogitechController/RGBController_LogitechG403.cpp \
Controllers/LogitechController/RGBController_LogitechG502PS.cpp \
Controllers/LogitechController/RGBController_LogitechG560.cpp \
Controllers/LogitechController/RGBController_LogitechG810.cpp \
Controllers/LogitechController/RGBController_LogitechG213.cpp \
Controllers/LogitechController/RGBController_LogitechGPowerPlay.cpp \

View file

@ -1457,6 +1457,8 @@ std::string device_type_to_str(device_type type)
return "Gamepad";
case DEVICE_TYPE_LIGHT:
return "Light";
case DEVICE_TYPE_SPEAKER:
return "Speaker";
default:
return "Unknown";
}

View file

@ -117,6 +117,7 @@ enum
DEVICE_TYPE_HEADSET_STAND,
DEVICE_TYPE_GAMEPAD,
DEVICE_TYPE_LIGHT,
DEVICE_TYPE_SPEAKER,
DEVICE_TYPE_UNKNOWN
};

View file

@ -61,6 +61,9 @@ static QString GetIconString(device_type type, bool dark)
case DEVICE_TYPE_LIGHT:
filename = "light";
break;
case DEVICE_TYPE_SPEAKER:
filename = "speaker";
break;
default:
filename = "unknown";
break;

View file

@ -34,5 +34,7 @@
<file>arrow-up.png</file>
<file>plugin.png</file>
<file>plugin_dark.png</file>
<file>speaker.png</file>
<file>speaker_dark.png</file>
</qresource>
</RCC>

BIN
qt/speaker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
qt/speaker_dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B