Skip to content

Commit 5a02832

Browse files
committed
fix: correct HID output report format for G923 LEDs
- First byte must be Report ID (0x00), not the command directly - Buffer size must match device OutputReportByteLength - Add diagnostic logging via OutputDebugString
1 parent 761ef82 commit 5a02832

File tree

2 files changed

+68
-14
lines changed

2 files changed

+68
-14
lines changed

Common Files/LogitechLED.cpp

Lines changed: 67 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <setupapi.h>
55
#include <hidsdi.h>
66
#include <hidpi.h>
7+
#include <stdio.h>
78

89
#pragma comment(lib, "hid.lib")
910
#pragma comment(lib, "setupapi.lib")
@@ -21,9 +22,20 @@ static const USHORT G29_PID = 0xC24F;
2122
static const BYTE LOGITECH_CMD_SET_LED = 0xF8;
2223
static const BYTE LOGITECH_LED_SUBCMD = 0x12;
2324

25+
// Forward declaration for logging (defined in DllMain.cpp)
26+
extern void LogMessage(const char* fmt, ...);
27+
28+
// Simple log helper - uses OutputDebugString if LogMessage is not available
29+
static void LEDLog(const char* msg)
30+
{
31+
OutputDebugStringA(msg);
32+
OutputDebugStringA("\n");
33+
}
34+
2435
LogitechLED::LogitechLED()
2536
: m_deviceHandle(INVALID_HANDLE_VALUE)
2637
, m_available(false)
38+
, m_outputReportLength(0)
2739
{
2840
}
2941

@@ -49,6 +61,7 @@ void LogitechLED::Close()
4961
m_deviceHandle = INVALID_HANDLE_VALUE;
5062
}
5163
m_available = false;
64+
m_outputReportLength = 0;
5265
}
5366

5467
bool LogitechLED::IsAvailable() const
@@ -58,34 +71,43 @@ bool LogitechLED::IsAvailable() const
5871

5972
bool LogitechLED::SetLEDs(BYTE ledMask)
6073
{
61-
if (!m_available || m_deviceHandle == INVALID_HANDLE_VALUE)
74+
if (!m_available || m_deviceHandle == INVALID_HANDLE_VALUE || m_outputReportLength == 0)
6275
return false;
6376

64-
// Logitech extended HID report for RPM LEDs
65-
// Format: [ReportID, CMD, SUBCMD, LED_MASK, 0, 0, 0, 1]
66-
BYTE report[8] = { 0 };
67-
report[0] = LOGITECH_CMD_SET_LED; // 0xF8
68-
report[1] = LOGITECH_LED_SUBCMD; // 0x12
69-
report[2] = ledMask & 0x1F; // 5 LEDs, bits 0-4
70-
report[3] = 0x00;
77+
// Allocate buffer matching the device's expected output report length
78+
BYTE* report = (BYTE*)calloc(m_outputReportLength, 1);
79+
if (!report)
80+
return false;
81+
82+
// First byte = Report ID (0x00 for default)
83+
// Then the Logitech extended command
84+
report[0] = 0x00; // Report ID
85+
report[1] = LOGITECH_CMD_SET_LED; // 0xF8
86+
report[2] = LOGITECH_LED_SUBCMD; // 0x12
87+
report[3] = ledMask & 0x1F; // 5 LEDs, bits 0-4
7188
report[4] = 0x00;
7289
report[5] = 0x00;
7390
report[6] = 0x00;
7491
report[7] = 0x01;
7592

7693
DWORD bytesWritten = 0;
77-
BOOL result = WriteFile(m_deviceHandle, report, sizeof(report), &bytesWritten, NULL);
94+
BOOL result = WriteFile(m_deviceHandle, report, m_outputReportLength, &bytesWritten, NULL);
7895

7996
if (!result)
8097
{
81-
// Device may have been disconnected
8298
DWORD err = GetLastError();
99+
char buf[256];
100+
sprintf_s(buf, "LogitechLED: WriteFile failed, error=%lu, reportLen=%u, mask=0x%02X",
101+
err, m_outputReportLength, ledMask);
102+
LEDLog(buf);
103+
83104
if (err == ERROR_DEVICE_NOT_CONNECTED || err == ERROR_GEN_FAILURE)
84105
{
85106
Close();
86107
}
87108
}
88109

110+
free(report);
89111
return result == TRUE;
90112
}
91113

@@ -121,13 +143,21 @@ bool LogitechLED::FindAndOpenDevice()
121143
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
122144

123145
if (deviceInfoSet == INVALID_HANDLE_VALUE)
146+
{
147+
LEDLog("LogitechLED: SetupDiGetClassDevs failed");
124148
return false;
149+
}
125150

126151
SP_DEVICE_INTERFACE_DATA interfaceData;
127152
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
128153

154+
int deviceCount = 0;
155+
int logitechCount = 0;
156+
129157
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &hidGuid, i, &interfaceData); i++)
130158
{
159+
deviceCount++;
160+
131161
// Get required buffer size
132162
DWORD requiredSize = 0;
133163
SetupDiGetDeviceInterfaceDetailA(deviceInfoSet, &interfaceData, NULL, 0, &requiredSize, NULL);
@@ -145,7 +175,7 @@ bool LogitechLED::FindAndOpenDevice()
145175
continue;
146176
}
147177

148-
// Open the device to check VID/PID
178+
// Open the device
149179
HANDLE handle = CreateFileA(
150180
detailData->DevicePath,
151181
GENERIC_READ | GENERIC_WRITE,
@@ -168,21 +198,39 @@ bool LogitechLED::FindAndOpenDevice()
168198
attrs.ProductID == G923_PID_PS ||
169199
attrs.ProductID == G29_PID))
170200
{
171-
// Check this is the right HID interface for output reports
172-
// by verifying the output report length
201+
logitechCount++;
202+
173203
PHIDP_PREPARSED_DATA preparsedData = NULL;
174204
if (HidD_GetPreparsedData(handle, &preparsedData))
175205
{
176206
HIDP_CAPS caps;
177207
if (HidP_GetCaps(preparsedData, &caps) == HIDP_STATUS_SUCCESS)
178208
{
179-
// We need an output report length that can hold our 8-byte command
209+
char buf[512];
210+
sprintf_s(buf,
211+
"LogitechLED: Found Logitech VID=0x%04X PID=0x%04X "
212+
"UsagePage=0x%04X Usage=0x%04X "
213+
"InputReportLen=%u OutputReportLen=%u FeatureReportLen=%u "
214+
"Path=%s",
215+
attrs.VendorID, attrs.ProductID,
216+
caps.UsagePage, caps.Usage,
217+
caps.InputReportByteLength,
218+
caps.OutputReportByteLength,
219+
caps.FeatureReportByteLength,
220+
detailData->DevicePath);
221+
LEDLog(buf);
222+
223+
// We need an output report length that can hold our command
180224
if (caps.OutputReportByteLength >= 8)
181225
{
226+
m_outputReportLength = caps.OutputReportByteLength;
182227
HidD_FreePreparsedData(preparsedData);
183228
free(detailData);
184229
SetupDiDestroyDeviceInfoList(deviceInfoSet);
185230
m_deviceHandle = handle;
231+
232+
sprintf_s(buf, "LogitechLED: Using device with OutputReportLen=%u", m_outputReportLength);
233+
LEDLog(buf);
186234
return true;
187235
}
188236
}
@@ -195,6 +243,11 @@ bool LogitechLED::FindAndOpenDevice()
195243
free(detailData);
196244
}
197245

246+
char buf[128];
247+
sprintf_s(buf, "LogitechLED: Enumerated %d HID devices, found %d Logitech matches, none suitable",
248+
deviceCount, logitechCount);
249+
LEDLog(buf);
250+
198251
SetupDiDestroyDeviceInfoList(deviceInfoSet);
199252
return false;
200253
}

Common Files/LogitechLED.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class LogitechLED
3535
private:
3636
HANDLE m_deviceHandle;
3737
bool m_available;
38+
USHORT m_outputReportLength;
3839

3940
// Find the G923 HID device path and open it
4041
bool FindAndOpenDevice();

0 commit comments

Comments
 (0)