Skip to content

Commit 8801225

Browse files
committed
feat: probe all unknown 0x8xxx HID++ features for LED control
The G923 Xbox has no standard LED features (0x8070, 0x1300). Now probes ALL unknown features in the 0x8xxx gaming range, especially 0x807A which is closest to ColorLEDEffects (0x8070). Calls func0-3 with LED bitmask on each unknown feature.
1 parent f3eecb4 commit 8801225

File tree

1 file changed

+102
-71
lines changed

1 file changed

+102
-71
lines changed

Common Files/LogitechLED.cpp

Lines changed: 102 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ bool LogitechLED::TryHIDPPDiscovery(HANDLE h, USHORT outLen, USHORT inLen)
281281
// --- Enumerate ALL features ---
282282
BYTE bestLedIdx = 0;
283283
USHORT bestLedFid = 0;
284+
BYTE probeIdx[8] = {0};
285+
USHORT probeFid[8] = {0};
286+
int numProbe = 0;
284287

285288
for (int fi = 1; fi <= featureCount && fi < 128; fi++)
286289
{
@@ -301,17 +304,24 @@ bool LogitechLED::TryHIDPPDiscovery(HANDLE h, USHORT outLen, USHORT inLen)
301304
BYTE ftype = resp[6];
302305

303306
const char* name = "";
307+
bool isKnown = true;
304308
switch (fid)
305309
{
306310
case 0x0001: name = " (IFeatureSet)"; break;
307311
case 0x0003: name = " (DeviceInfo)"; break;
308312
case 0x0005: name = " (DeviceName)"; break;
309313
case 0x0007: name = " (FriendlyName)"; break;
314+
case 0x00C1: name = " (DfuControlUnsigned)"; break;
310315
case 0x00C2: name = " (DfuControl)"; break;
311316
case 0x00D0: name = " (Dfu)"; break;
312317
case 0x1000: name = " (BatteryStatus)"; break;
313318
case 0x1300: name = " (LEDControl)"; break;
319+
case 0x1800: name = " (GenericTest)"; break;
320+
case 0x1802: name = " (DeviceReset)"; break;
314321
case 0x1814: name = " (ChangeHost)"; break;
322+
case 0x1BC0: name = " (ReportHIDUsage)"; break;
323+
case 0x1E00: name = " (EnableHiddenFeatures)"; break;
324+
case 0x1F1F: name = " (FirmwareProperties)"; break;
315325
case 0x1982: name = " (Backlight2)"; break;
316326
case 0x1B04: name = " (SpecialKeys)"; break;
317327
case 0x2201: name = " (AdjDPI)"; break;
@@ -321,116 +331,137 @@ bool LogitechLED::TryHIDPPDiscovery(HANDLE h, USHORT outLen, USHORT inLen)
321331
case 0x8071: name = " (RGBEffects)"; break;
322332
case 0x8081: name = " (PerKeyLighting)"; break;
323333
case 0x8100: name = " (OnboardProfiles)"; break;
334+
case 0x8120: name = " (GamingAttachments)"; break;
335+
case 0x8123: name = " (ForceFeedback)"; break;
336+
case 0x8127: name = " (ForceFeedbackG923)"; break;
337+
default: isKnown = false; break;
324338
}
325339

326340
Log(" [%02d] 0x%04X type=0x%02X%s", fi, fid, ftype, name);
327341

328-
// Track LED-related features
342+
// Track candidate features to probe for LED control:
343+
// 1. Known LED features
344+
// 2. ANY unknown feature in 0x80xx range (could be wheel-specific LEDs)
329345
if (fid == 0x8070 || fid == 0x1300 || fid == 0x8071 ||
330346
fid == 0x8040 || fid == 0x1982)
331347
{
348+
// Known LED features - highest priority
332349
if (bestLedIdx == 0 || fid == 0x8070 ||
333350
(bestLedFid != 0x8070 && fid == 0x1300))
334351
{
335352
bestLedIdx = (BYTE)fi;
336353
bestLedFid = fid;
337354
}
338355
}
356+
357+
// Store ALL unknown 0x8xxx features for probing
358+
if (!isKnown && (fid & 0xF000) == 0x8000 && numProbe < 8)
359+
{
360+
probeIdx[numProbe] = (BYTE)fi;
361+
probeFid[numProbe] = fid;
362+
numProbe++;
363+
}
339364
}
340365

341366
Log("");
342367

343-
if (bestLedIdx == 0)
368+
// --- Probe known LED features first ---
369+
if (bestLedIdx != 0)
344370
{
345-
Log(" No LED-related feature found");
346-
Log(" Check feature list above for alternative LED features");
347-
continue;
371+
Log(" >>> Known LED feature: 0x%04X at index %d", bestLedFid, bestLedIdx);
372+
}
373+
else
374+
{
375+
Log(" No standard LED feature found");
376+
if (numProbe > 0)
377+
Log(" Will probe %d unknown 0x8xxx features", numProbe);
348378
}
349379

350-
Log(" >>> LED feature: 0x%04X at index %d", bestLedFid, bestLedIdx);
351-
Log("");
352-
353-
// --- Probe LED feature: call func0 (getInfo) ---
354-
memset(req, 0, sizeof(req));
355-
req[0] = reportId;
356-
req[1] = devIdx;
357-
req[2] = bestLedIdx;
358-
req[3] = (0 << 4) | 0x01;
380+
// If no known LED feature, use the first unknown 0x8xxx feature
381+
if (bestLedIdx == 0 && numProbe > 0)
382+
{
383+
bestLedIdx = probeIdx[0];
384+
bestLedFid = probeFid[0];
385+
Log(" >>> Trying unknown feature 0x%04X at index %d", bestLedFid, bestLedIdx);
386+
}
359387

360-
Log(" LED func0 (getInfo):");
361-
if (SendReport(h, req, outLen))
388+
if (bestLedIdx == 0)
362389
{
363-
memset(resp, 0, sizeof(resp));
364-
if (ReadReport(h, resp, inLen, 500))
365-
{
366-
if (resp[2] == 0xFF)
367-
Log(" error: 0x%02X", resp[5]);
368-
else
369-
Log(" data: %02X %02X %02X %02X %02X %02X %02X %02X",
370-
resp[4], resp[5], resp[6], resp[7],
371-
resp[8], resp[9], resp[10], resp[11]);
372-
}
373-
else Log(" no response");
390+
Log(" Nothing to probe");
391+
continue;
374392
}
375393

376-
// --- Try LED control commands ---
394+
// --- Probe ALL unknown 0x8xxx features with getInfo ---
377395
Log("");
378-
Log(" === LED ATTEMPTS (watch the wheel!) ===");
379-
380-
struct LedTry { BYTE func; BYTE p[8]; const char* desc; };
381-
LedTry tries[] = {
382-
{ 1, {0x1F,0x00,0,0,0,0,0,0}, "func1(mask=0x1F)" },
383-
{ 1, {0x1F,0xFF,0,0,0,0,0,0}, "func1(0x1F,0xFF)" },
384-
{ 2, {0x1F,0x00,0,0,0,0,0,0}, "func2(mask=0x1F)" },
385-
{ 2, {0x1F,0xFF,0,0,0,0,0,0}, "func2(0x1F,0xFF)" },
386-
{ 3, {0x1F,0x00,0,0,0,0,0,0}, "func3(mask=0x1F)" },
387-
{ 1, {0x00,0x01,0,0,0,0,0,0}, "func1(led=0,on)" },
388-
{ 2, {0x00,0x01,0,0,0,0,0,0}, "func2(led=0,on)" },
389-
{ 1, {0xFF,0x00,0,0,0,0,0,0}, "func1(0xFF)" },
390-
{ 2, {0xFF,0x00,0,0,0,0,0,0}, "func2(0xFF)" },
391-
{ 1, {0x64,0x00,0,0,0,0,0,0}, "func1(rpm=100)" },
392-
};
393-
int numTries = sizeof(tries) / sizeof(tries[0]);
394-
BYTE workingFunc = 0;
395-
396-
for (int t = 0; t < numTries; t++)
396+
Log(" === Probing unknown features ===");
397+
for (int pi = 0; pi < numProbe; pi++)
397398
{
398399
memset(req, 0, sizeof(req));
399400
req[0] = reportId;
400401
req[1] = devIdx;
401-
req[2] = bestLedIdx;
402-
req[3] = (tries[t].func << 4) | 0x01;
403-
memcpy(&req[4], tries[t].p, 8);
404-
405-
bool ok = SendReport(h, req, outLen);
406-
407-
BYTE tryResp[64] = {0};
408-
bool gotResp = ok ? ReadReport(h, tryResp, inLen, 300) : false;
402+
req[2] = probeIdx[pi];
403+
req[3] = (0 << 4) | 0x01; // func0 = getInfo
409404

410-
if (!ok)
411-
Log(" [%02d] %-22s SEND FAIL", t, tries[t].desc);
412-
else if (gotResp && tryResp[2] == 0xFF)
413-
Log(" [%02d] %-22s ERR 0x%02X", t, tries[t].desc, tryResp[5]);
414-
else if (gotResp)
405+
if (SendReport(h, req, outLen))
415406
{
416-
Log(" [%02d] %-22s OK resp=%02X %02X %02X %02X",
417-
t, tries[t].desc,
418-
tryResp[4], tryResp[5], tryResp[6], tryResp[7]);
419-
if (workingFunc == 0) workingFunc = tries[t].func;
407+
memset(resp, 0, sizeof(resp));
408+
if (ReadReport(h, resp, inLen, 500))
409+
{
410+
if (resp[2] == 0xFF)
411+
Log(" 0x%04X[%d] func0: ERR 0x%02X",
412+
probeFid[pi], probeIdx[pi], resp[5]);
413+
else
414+
Log(" 0x%04X[%d] func0: %02X %02X %02X %02X %02X %02X %02X %02X",
415+
probeFid[pi], probeIdx[pi],
416+
resp[4], resp[5], resp[6], resp[7],
417+
resp[8], resp[9], resp[10], resp[11]);
418+
}
419+
else
420+
Log(" 0x%04X[%d] func0: no response", probeFid[pi], probeIdx[pi]);
420421
}
421-
else
422+
}
423+
424+
// --- Try LED commands on each unknown 0x8xxx feature ---
425+
Log("");
426+
Log(" === LED attempts on unknown features (WATCH THE WHEEL!) ===");
427+
428+
for (int pi = 0; pi < numProbe; pi++)
429+
{
430+
Log("");
431+
Log(" --- Feature 0x%04X at index %d ---", probeFid[pi], probeIdx[pi]);
432+
433+
// Try func1-3 with LED bitmask 0x1F (all 5 LEDs)
434+
for (BYTE funcId = 1; funcId <= 3; funcId++)
422435
{
423-
Log(" [%02d] %-22s OK (no resp)", t, tries[t].desc);
424-
if (workingFunc == 0) workingFunc = tries[t].func;
425-
}
436+
memset(req, 0, sizeof(req));
437+
req[0] = reportId;
438+
req[1] = devIdx;
439+
req[2] = probeIdx[pi];
440+
req[3] = (funcId << 4) | 0x01;
441+
req[4] = 0x1F; // all LEDs on
442+
443+
bool ok = SendReport(h, req, outLen);
444+
BYTE tryResp[64] = {0};
445+
bool gotResp = ok ? ReadReport(h, tryResp, inLen, 300) : false;
446+
447+
if (!ok)
448+
Log(" func%d(0x1F): SEND FAIL", funcId);
449+
else if (gotResp && tryResp[2] == 0xFF)
450+
Log(" func%d(0x1F): ERR 0x%02X", funcId, tryResp[5]);
451+
else if (gotResp)
452+
Log(" func%d(0x1F): OK resp=%02X %02X %02X %02X",
453+
funcId, tryResp[4], tryResp[5], tryResp[6], tryResp[7]);
454+
else
455+
Log(" func%d(0x1F): OK (no resp)", funcId);
426456

427-
Sleep(400);
457+
Sleep(400);
458+
}
428459
}
429460

430-
// Save whatever we found
461+
// Save best guess
431462
m_method = METHOD_HIDPP;
432463
m_ledFeatureIdx = bestLedIdx;
433-
m_ledFunctionId = (workingFunc > 0) ? workingFunc : 1;
464+
m_ledFunctionId = 1;
434465
m_deviceIdx = devIdx;
435466
Log("");
436467
Log(" HID++ saved: feat=0x%04X idx=%d func=%d devIdx=0x%02X",

0 commit comments

Comments
 (0)