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