-
Notifications
You must be signed in to change notification settings - Fork 5
Protocol Specification
The USB / HID++ protocol of the Logitech direct-drive wheel family, reverse-engineered from USB captures of G HUB on Windows and verified against live hardware. This page covers device identification, the USB interface layout, input reports, and the HID++ configuration protocol. The other transports have their own pages:
- Protocol Force Feedback: the dedicated force endpoint and the driver's effect engine.
- Protocol LIGHTSYNC: the LED feature pair (0x807A / 0x807B).
- TrueForce Protocol: the audio-haptic stream on interface 2.
Most of this applies to both the RS50 and the G Pro Racing Wheel; differences are called out inline.
| Property | RS50 | G Pro Racing Wheel |
|---|---|---|
| Vendor ID | 0x046D |
0x046D |
| Product ID | 0xC276 |
0xC272 (Xbox/PC), 0xC268 (PS/PC) |
| Device name | RS50 Base for PlayStation/PC | Logitech G Pro Racing Wheel |
| Max packet size | 64 bytes | 64 bytes |
The RS50 can also re-enumerate as 046d:c272 ("G PRO compatibility mode",
via its OLED menu) while keeping its own product string, which is how the
driver tells the two apart on that PID. The G Pro exposes additional HID++
sub-devices (0x01, 0x02, 0x05 over the shared interface 1); the RS50
answers the same sub-device indices (see section 4).
Three HID interfaces:
| Interface | Endpoints | Report size | Purpose |
|---|---|---|---|
| 0 - Joystick |
0x81 IN (interrupt, 1 ms) |
30 bytes | wheel position, pedals, buttons |
| 1 - HID++ |
0x82 IN; OUT via control SET_REPORT |
64 bytes | configuration, settings, feature queries |
| 2 - Force feedback |
0x83 IN / 0x03 OUT (interrupt) |
64 bytes | real-time force + TrueForce stream |
Important: force output uses dedicated endpoint 0x03 OUT, not the
HID++ protocol.
30-byte report:
Offset Size Type Description
------ ---- ---- -----------
0-1 2 u16 LE Button bitmask, buttons 0-15
2-3 2 - Button bitmask high bytes (byte 3 bit 7 = G button)
4-5 2 u16 LE Wheel position
6-7 2 u16 LE Accelerator pedal
8-9 2 u16 LE Brake pedal
10-11 2 u16 LE Clutch pedal
12-17 6 - Reserved (zeros)
18 1 u8 Always 0x01
19-29 11 - Reserved (zeros)
Button bitmask, byte 0: bits 0-3 are the D-pad hat nibble, bit 4 = A, bit 5 = X, bit 6 = B, bit 7 = Y. Byte 1: bit 0 = right paddle, 1 = left paddle, 2 = LT, 3 = RT, 4 = View, 5 = Menu, 6 = LB, 7 = RB. Byte 3 bit 7 = the G (logo) button. The user-facing index table is in Button Reference.
The D-pad nibble is a standard HID hat switch (usage 0x39, logical
0-7, 45° steps clockwise from Up; 8-15 = released), decoded natively by the
kernel to ABS_HAT0X/ABS_HAT0Y; the driver does no custom D-pad decoding.
Wheel position: 0x0000 full left, 0x8000 centre, 0xFFFF full right
(resolution depends on the configured rotation range). Pedals: 0x0000
released to 0xFFFF fully pressed.
The wheels speak the HID++ 2.0-family protocol (protocol number 4).
Commands are sent via USB control transfer (SET_REPORT) to endpoint 0;
responses arrive via interrupt IN on 0x82.
| Report ID | Size | Layout |
|---|---|---|
0x10 short |
7 B | id, device index, feature index, fn<<4 | sw_id, 3 params |
0x11 long |
20 B | same header, 16 params |
0x12 very long |
64 B | same header, 60 params |
- G Hub sends short (0x10) reports for all commands.
- The wheel base always responds with very-long (0x12) reports, regardless of the request type. This is officially specified: the HID vendor-collection usage's capability bitmask advertises very-long support, so a driver can detect it from the report descriptor.
-
Exception: sub-device responses (device index 0x01/0x02/0x05) arrive
as
0x11, not0x12.
The low nibble of the function byte correlates requests with responses; the
firmware echoes it. SW_ID 0 is reserved for broadcasts, which is exactly how
the wheel's unsolicited events arrive (settings broadcasts, profile and
rotation events). The Linux driver uses SW_ID 0x0a; it must not use 0x01
because the pedal sub-device's MCU silently drops that sw-id.
Feature indices are discovered at runtime (IRoot getFeatureIndex, or
IFeatureSet enumeration); typical RS50-native indices shown. The real
G PRO's catalog holds the same feature IDs at shifted indices, which dynamic
discovery absorbs.
| Index | Feature ID | Name | Purpose |
|---|---|---|---|
| 0x00 | 0x0000 |
IRoot | feature discovery |
| 0x01 | 0x0001 |
IFeatureSet | list all features |
| 0x02 | 0x0003 |
DeviceInfo | serial, firmware entities |
| 0x03 | 0x0005 |
DeviceNameType | device name string |
| 0x04 | 0x00C3 |
SecureDFU | firmware update |
| 0x09 | 0x1BC0 |
ReportHidUsages | optional; the driver omits it |
| 0x0A | 0x8040 |
Brightness | LED brightness only (the Sensitivity slider is a 0x80A4 curve upload, not this) |
| 0x0B | 0x807A |
LIGHTSYNC | LED effects; see Protocol LIGHTSYNC |
| 0x0C | 0x807B |
RGBZoneConfig | LED RGB data; see Protocol LIGHTSYNC |
| 0x0D | 0x80A4 |
AxisResponseCurve | per-axis 64-point response curves |
| 0x10 | 0x8123 |
ForceFeedback | the Windows game-FFB path (unused by this driver; see Protocol Force Feedback) |
| 0x14 | 0x8133 |
Damping | damping slider |
| 0x15 | 0x8134 |
BrakeForce | brake force slider |
| 0x16 | 0x8136 |
FFBStrength | strength slider |
| 0x17 | 0x8137 |
Profile | profile / mode switching, slot names |
| 0x18 | 0x8138 |
RotationRange | rotation range |
| 0x19 | 0x8139 |
TRUEFORCE | TrueForce intensity |
| 0x1A | 0x8140 |
FFBFilter | FFB filter + auto toggle |
| - | 0x80D0 |
CombinedPedals | boolean, also emits profile-change broadcasts |
GET semantics are mostly fn=0 capabilities, fn=1 current value; the SET
function varies per feature (do not assume fn=2):
| Feature | Page | SET fn | Value encoding |
|---|---|---|---|
| Rotation range | 0x8138 | 2 | degrees, u16 BE (90-2700 in 10° steps) |
| FFB strength | 0x8136 | 2 |
Nm × 8192, u16 BE (0xFFFF = 8.0 Nm) |
| FFB filter | 0x8140 | 2 | flags byte (bit 0 = user set now, bit 2 = auto mode), level 1-15 in byte 6 |
| Brake force | 0x8134 | 2 | u16 BE, 0-0xFFFF = 0-100% |
| Brightness | 0x8040 | 2 | 0-100 in byte 5 |
| Damping | 0x8133 | 1 | u16 BE; fn=1 is both GET and SET, disambiguated by payload length |
| TRUEFORCE | 0x8139 | 3 | u16 BE, 0-0xFFFF = 0-100% |
| Profile / mode | 0x8137 | 2 |
[profile, 0, 0]; fn=1 GET returns [profile, mode], fn=3 [slot] returns the slot's name, fn=4 renames a slot |
| Centre calibration | 0x812C (sub-device 0x05) | 3 | absolute encoder position u16 BE; G Hub's calibrate is fn=1 GET position then fn=3 SET |
Mode differences: Sensitivity is desktop-only, Brake Force onboard-only (with capture evidence that compat firmware also accepts it in desktop), full LIGHTSYNC color control is desktop-only.
A per-axis 64-point curve store, used on three axis groups:
-
Steering: base device 0xFF, axis 0 (
wheel_response_curve,wheel_sensitivity). - Pedals: sub-device 0x02, axes 0/1/2 = throttle/brake/clutch.
-
Analog handbrake: base, axis 4 (HID usage 0x32, evdev
ABS_Z).
An upload is fn3 [axis] (open), 22 fn4 chunks of up to 3 (in,out)
u16-BE pairs (64 monotonic points from (0,0) to (0xFFFF,0xFFFF)), then
fn5 commit; fn6 [axis] reverts to the built-in curve. The axis MCU
applies the curve to what it reports to the PC. Note the wheel emits no HID
reports while an axis is still, so read the loaded point count back with
fn1 for a motion-free check.
Besides the base at 0xFF, three sub-devices carry real HID++ traffic on
both wheels, each with its own feature catalog:
-
0x01: display / rim module (
0x8091per-key LED matrix,0x18A2,0x9315). -
0x02: pedal base (
0x80A4axis curves,0x80D0,0x8134brake force,0x8135,0x9209/0x9215). -
0x05: motor / base unit: the
0x812xcalibration cluster including0x812Ccentre calibration.
Quirks: sub-device responses arrive as report ID 0x11; the pedal MCU
silently drops sw-id 0x01.
Errors arrive with 0xFF in the feature-index byte, then the failing
feature index, the failing fn|sw byte, and a code: 1 Unknown,
2 InvalidArgument, 3 OutOfRange, 4 HWError, 5 internal, 6
InvalidFeatureIndex, 7 InvalidFunctionId, 8 Busy, 9 Unsupported.
Recommended bring-up order: enumerate, claim interfaces 0/1/2, discover feature indices via IRoot/IFeatureSet, read current settings (range, strength, damping, TrueForce, filter, brightness) so sysfs reflects device state, then start the force loop (Protocol Force Feedback).
The driver also reads DeviceInfo once at init: feature 0x0003 fn2 returns
the real 12-character serial; fn1 enumerates firmware entities (the base's
bootloader, active main firmware and hardware entity, plus the motor unit's
servo firmware on sub-device 0x05). Exposed as wheel_serial /
wheel_firmware.
| Feature | G920/G923 | RS50 / G PRO |
|---|---|---|
| FFB method | HID++ feature 0x8123 | dedicated endpoint 0x03 (plus 0x8123 as an alternative transport) |
| Report ID for FFB | 0x11/0x12 | 0x01 (custom) |
| Sequence field | 2 bytes | 1 byte |
| Max rotation | 900° | 2700° |
| Motor | belt/gear | direct drive |
| USB interfaces | unified HID++ | 3 separate (joystick, HID++, FFB) |
The direct-drive wheels expose two independent force transports and privilege the endpoint stream; see the architecture note in Protocol Force Feedback.
Some HID++ features relate to firmware update or critical configuration. Use GET (read-only) functions when probing unknown features; never blindly send SET commands to uncharacterized features.