Skip to content

Commit 41b4d4d

Browse files
committed
fix: try multiple HID report methods for G923 LEDs
- Try HidD_SetOutputReport with report ID 0xF8 first (extended cmd) - Fallback to report ID 0x00 - Fallback to WriteFile with 0xF8 - Log all Logitech HID collections found - Pick collection with largest OutputReportByteLength
1 parent 16520ba commit 41b4d4d

File tree

1 file changed

+95
-30
lines changed

1 file changed

+95
-30
lines changed

Common Files/LogitechLED.cpp

Lines changed: 95 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -95,39 +95,88 @@ bool LogitechLED::SetLEDs(BYTE ledMask)
9595
if (!report)
9696
return false;
9797

98-
// First byte = Report ID (0x00 for default)
99-
// Then the Logitech extended command
100-
report[0] = 0x00; // Report ID
101-
report[1] = LOGITECH_CMD_SET_LED; // 0xF8
102-
report[2] = LOGITECH_LED_SUBCMD; // 0x12
103-
report[3] = ledMask & 0x1F; // 5 LEDs, bits 0-4
98+
// For Logitech wheels, the extended command IS the report.
99+
// The Report ID byte is implicit in the HID descriptor.
100+
// Use HidD_SetOutputReport which handles report ID routing properly.
101+
report[0] = LOGITECH_CMD_SET_LED; // 0xF8 - this IS the report ID for extended commands
102+
report[1] = LOGITECH_LED_SUBCMD; // 0x12
103+
report[2] = ledMask & 0x1F; // 5 LEDs, bits 0-4
104+
report[3] = 0x00;
104105
report[4] = 0x00;
105106
report[5] = 0x00;
106107
report[6] = 0x00;
107108
report[7] = 0x01;
108109

109-
DWORD bytesWritten = 0;
110110
char buf[256];
111-
sprintf_s(buf, "LogitechLED: SetLEDs mask=0x%02X reportLen=%u", ledMask, m_outputReportLength);
111+
sprintf_s(buf, "LogitechLED: SetLEDs mask=0x%02X reportLen=%u report[0]=0x%02X",
112+
ledMask, m_outputReportLength, report[0]);
112113
LEDLog(buf);
113114

114-
BOOL result = WriteFile(m_deviceHandle, report, m_outputReportLength, &bytesWritten, NULL);
115+
// Try HidD_SetOutputReport first (more reliable for HID devices with specific report IDs)
116+
BOOL result = HidD_SetOutputReport(m_deviceHandle, report, m_outputReportLength);
115117

116118
if (!result)
117119
{
118120
DWORD err = GetLastError();
119-
sprintf_s(buf, "LogitechLED: WriteFile FAILED, error=%lu", err);
121+
sprintf_s(buf, "LogitechLED: HidD_SetOutputReport FAILED (reportID=0xF8), error=%lu", err);
120122
LEDLog(buf);
121123

122-
if (err == ERROR_DEVICE_NOT_CONNECTED || err == ERROR_GEN_FAILURE)
124+
// Fallback: try with report ID 0x00 (some devices expect this)
125+
memset(report, 0, m_outputReportLength);
126+
report[0] = 0x00; // Report ID 0
127+
report[1] = LOGITECH_CMD_SET_LED; // 0xF8
128+
report[2] = LOGITECH_LED_SUBCMD; // 0x12
129+
report[3] = ledMask & 0x1F;
130+
report[4] = 0x00;
131+
report[5] = 0x00;
132+
report[6] = 0x00;
133+
report[7] = 0x01;
134+
135+
result = HidD_SetOutputReport(m_deviceHandle, report, m_outputReportLength);
136+
if (!result)
123137
{
124-
Close();
138+
err = GetLastError();
139+
sprintf_s(buf, "LogitechLED: HidD_SetOutputReport FAILED (reportID=0x00), error=%lu", err);
140+
LEDLog(buf);
141+
142+
// Last resort: try WriteFile with report ID = 0xF8
143+
memset(report, 0, m_outputReportLength);
144+
report[0] = LOGITECH_CMD_SET_LED;
145+
report[1] = LOGITECH_LED_SUBCMD;
146+
report[2] = ledMask & 0x1F;
147+
report[3] = 0x00;
148+
report[4] = 0x00;
149+
report[5] = 0x00;
150+
report[6] = 0x00;
151+
report[7] = 0x01;
152+
153+
DWORD bytesWritten = 0;
154+
result = WriteFile(m_deviceHandle, report, m_outputReportLength, &bytesWritten, NULL);
155+
if (!result)
156+
{
157+
err = GetLastError();
158+
sprintf_s(buf, "LogitechLED: WriteFile FAILED (reportID=0xF8), error=%lu", err);
159+
LEDLog(buf);
160+
161+
if (err == ERROR_DEVICE_NOT_CONNECTED || err == ERROR_GEN_FAILURE)
162+
{
163+
Close();
164+
}
165+
}
166+
else
167+
{
168+
sprintf_s(buf, "LogitechLED: WriteFile OK (reportID=0xF8), bytes=%lu", bytesWritten);
169+
LEDLog(buf);
170+
}
171+
}
172+
else
173+
{
174+
LEDLog("LogitechLED: HidD_SetOutputReport OK (reportID=0x00)");
125175
}
126176
}
127177
else
128178
{
129-
sprintf_s(buf, "LogitechLED: WriteFile OK, bytesWritten=%lu", bytesWritten);
130-
LEDLog(buf);
179+
LEDLog("LogitechLED: HidD_SetOutputReport OK (reportID=0xF8)");
131180
}
132181

133182
free(report);
@@ -176,12 +225,14 @@ bool LogitechLED::FindAndOpenDevice()
176225

177226
int deviceCount = 0;
178227
int logitechCount = 0;
228+
HANDLE bestHandle = INVALID_HANDLE_VALUE;
229+
USHORT bestReportLen = 0;
230+
char bestPath[512] = { 0 };
179231

180232
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &hidGuid, i, &interfaceData); i++)
181233
{
182234
deviceCount++;
183235

184-
// Get required buffer size
185236
DWORD requiredSize = 0;
186237
SetupDiGetDeviceInterfaceDetailA(deviceInfoSet, &interfaceData, NULL, 0, &requiredSize, NULL);
187238

@@ -198,7 +249,6 @@ bool LogitechLED::FindAndOpenDevice()
198249
continue;
199250
}
200251

201-
// Open the device
202252
HANDLE handle = CreateFileA(
203253
detailData->DevicePath,
204254
GENERIC_READ | GENERIC_WRITE,
@@ -233,44 +283,59 @@ bool LogitechLED::FindAndOpenDevice()
233283
sprintf_s(buf,
234284
"LogitechLED: Found Logitech VID=0x%04X PID=0x%04X "
235285
"UsagePage=0x%04X Usage=0x%04X "
236-
"InputReportLen=%u OutputReportLen=%u FeatureReportLen=%u "
286+
"InLen=%u OutLen=%u FeatLen=%u "
287+
"NumOutputValueCaps=%u "
237288
"Path=%s",
238289
attrs.VendorID, attrs.ProductID,
239290
caps.UsagePage, caps.Usage,
240291
caps.InputReportByteLength,
241292
caps.OutputReportByteLength,
242293
caps.FeatureReportByteLength,
294+
caps.NumberOutputValueCaps,
243295
detailData->DevicePath);
244296
LEDLog(buf);
245297

246-
// We need an output report length that can hold our command
298+
// Log ALL collections, pick the best one for output
247299
if (caps.OutputReportByteLength >= 8)
248300
{
249-
m_outputReportLength = caps.OutputReportByteLength;
250-
HidD_FreePreparsedData(preparsedData);
251-
free(detailData);
252-
SetupDiDestroyDeviceInfoList(deviceInfoSet);
253-
m_deviceHandle = handle;
254-
255-
sprintf_s(buf, "LogitechLED: Using device with OutputReportLen=%u", m_outputReportLength);
256-
LEDLog(buf);
257-
return true;
301+
// Prefer larger OutputReportByteLength (more likely the right interface)
302+
if (bestHandle == INVALID_HANDLE_VALUE || caps.OutputReportByteLength > bestReportLen)
303+
{
304+
if (bestHandle != INVALID_HANDLE_VALUE)
305+
CloseHandle(bestHandle);
306+
bestHandle = handle;
307+
bestReportLen = caps.OutputReportByteLength;
308+
strcpy_s(bestPath, detailData->DevicePath);
309+
handle = INVALID_HANDLE_VALUE; // don't close below
310+
}
258311
}
259312
}
260313
HidD_FreePreparsedData(preparsedData);
261314
}
262315
}
263316
}
264317

265-
CloseHandle(handle);
318+
if (handle != INVALID_HANDLE_VALUE)
319+
CloseHandle(handle);
266320
free(detailData);
267321
}
268322

323+
SetupDiDestroyDeviceInfoList(deviceInfoSet);
324+
325+
if (bestHandle != INVALID_HANDLE_VALUE)
326+
{
327+
m_deviceHandle = bestHandle;
328+
m_outputReportLength = bestReportLen;
329+
char buf[512];
330+
sprintf_s(buf, "LogitechLED: Selected device OutputReportLen=%u Path=%s",
331+
m_outputReportLength, bestPath);
332+
LEDLog(buf);
333+
return true;
334+
}
335+
269336
char buf[128];
270337
sprintf_s(buf, "LogitechLED: Enumerated %d HID devices, found %d Logitech matches, none suitable",
271338
deviceCount, logitechCount);
272339
LEDLog(buf);
273-
274-
SetupDiDestroyDeviceInfoList(deviceInfoSet);
275340
return false;
276341
}

0 commit comments

Comments
 (0)