-
Notifications
You must be signed in to change notification settings - Fork 5
Protocol Force Feedback
How force reaches the motor: the dedicated interface-2 endpoint stream, the packet encoding, and the driver's software effect engine. Part of the Protocol Specification; the audio-haptic layer that shares this endpoint is in TrueForce Protocol.
Endpoint 0x03 OUT (interface 2) carries one 64-byte type-0x01 packet
family. Byte 10, the "new samples this packet" count, demultiplexes it:
-
0: a pure constant-force ("KF") update, documented here. -
4: a unified force+audio ("TF") packet whose bytes 12-63 carry a rolling haptic sample window on top of the same force field; see TrueForce Protocol for the full framing.
Offset Size Type Description
------ ---- ---- -----------
0 1 u8 Report ID (0x01)
1-3 3 - Reserved (0x00 0x00 0x00)
4 1 u8 Command type (0x01 = stream/sample packet)
5 1 u8 Sequence counter (0x00-0xFF, wraps)
6-7 2 u16 LE Force value / motor torque target ("cur")
8-9 2 u16 LE Duplicate (must match bytes 6-7)
10 1 u8 New-sample count (0 here; 4 = TF audio packet)
11-63 53 - Zero for a constant-force packet
Bytes 6-9 are the motor torque target ("cur" in TrueForce terms) and are honoured whether or not the packet also carries audio: a constant-force packet is a TF stream packet with an empty sample window. The sequence counter is a single byte wrapping at 255 (unlike the two-byte HID++ sequence) and is shared across all interface-2 packet types.
| Value | Force |
|---|---|
0x0000 |
maximum LEFT |
0x8000 |
neutral |
0xFFFF |
maximum RIGHT |
uint16_t offset_binary = (int16_t)signed_value + 0x8000;Games stream at 1000 Hz on Windows (median inter-packet gap ~1.0 ms in every
gameplay capture); the kernel driver's own force stream runs at 500 Hz.
There is no idle keepalive: the endpoint is silent when no effect plays.
(An earlier revision documented an 05 07 "refresh" packet; capture
re-analysis proved that was a DualShock 4 lightbar report to a different
device that was plugged in at capture time, and the driver does not send it.)
The wheel firmware only understands raw constant forces on this endpoint, so the driver emulates the complete Linux FF effect set on top of it:
-
FF_CONSTANT,FF_RAMP,FF_PERIODIC(sine, square, triangle, saw up, saw down), and the four condition effectsFF_SPRING,FF_DAMPER,FF_FRICTION,FF_INERTIA. - A 500 Hz timer walks the active-effect slots, sums each effect's
instantaneous contribution, applies
FF_GAIN, and sends one net force value. The timer keeps running whenever any effect is playing, even if the current net force is zero (a spring at exact centre must push the moment the wheel moves). - Condition effects read the live wheel state (position from the interface-0
report, velocity and acceleration derived at the tick) and apply the
standard
ff_condition_effectformula; waveform and envelope semantics matchDocumentation/input/ff.rst. - Emulated springs get synthetic damping (
wheel_spring_damping) because an undamped host-side spring on a low-friction direct-drive motor rings; see the Sysfs API Reference. - Vibration-class effects (rumble, periodics at 20 Hz or faster) can route
to the TrueForce audio channel instead of the steering sum
(
wheel_texture_route), matching the Windows KF/TF split. - FFB must be sent from process context (workqueue); the driver uses
hid_hw_output_report()on interface 2.
The direct-drive wheels expose two independent force paths, and which one a host uses is a choice, not a hardware constraint:
-
HID++ feature
0x8123fn2: the path Logitech's own Windows runtime uses for normal game FFB on these wheels. Report 0x11/0x12, device index 0xFF, signed int16 BE motor target at payload offset 10-11, sent at the game's rate (~140-333 Hz observed). Set-and-hold: the wheel maintains the last commanded force indefinitely with no keepalive. - Endpoint 0x03 (this page): the TrueForce session channel, used by SDK-native games and by this Linux driver for ALL force output. While a TrueForce session is active, the packet's "cur" field OVERRIDES the 0x8123 path, and the sample window plays additively on top.
The G920/G923 comparison in the Protocol Specification therefore describes defaults, not capabilities: the older wheels have only 0x8123, while the DD wheels have both. A minimal 0x8123-fn2 sender remains an untested alternative transport for this driver (potentially relevant if the wheel's FFB-filter smoothing turns out to apply only to that path); the G920-class slot-based 0x8123 engine, however, saturates the queue on these wheels and cannot be reused as-is.
#define HIDPP_DD_FF_REPORT_ID 0x01
#define HIDPP_DD_FF_REPORT_SIZE 64
struct hidpp_dd_ff_report {
u8 report_id; /* 0x01 */
u8 reserved[3]; /* 0x00, 0x00, 0x00 */
u8 effect_type; /* 0x01 = constant force */
u8 sequence; /* 0x00-0xFF, wraps */
__le16 force; /* 0x0000=left, 0x8000=center, 0xFFFF=right */
__le16 force_dup; /* duplicate of force value */
u8 padding[54]; /* zeros */
} __packed;These wheels have no hardware autocenter setting (G HUB exposes none); the
driver provides a real host-side centring spring instead, driven by the
autocenter sysfs attribute or the standard evdev FF_AUTOCENTER control,
summed in the same 500 Hz loop.