Skip to content

Commit 0127783

Browse files
committed
Candidate/driver alignment on par other drivers and captures (#11)
Driver alignment on par other drivers and captures Motivation - Align the T500RS driver’s runtime behavior with the T300/TS‑X pattern and with Windows usbmon captures to improve correctness and reduce in‑game dropouts. - Ensure parameter updates don’t force re‑uploads, and preserve the device’s idiosyncrasies (EffectID=0 for 0x01/0x41, fixed constant linkage). What’s changed - Update path parity (parameter‑only updates; no STOP/0x01 re‑upload): * FF_CONSTANT: send only Report 0x03 (code=0x0e) with new level. * FF_PERIODIC: send only Report 0x04 with new magnitude + frequency (Hz×100). * FF_SPRING/FF_DAMPER/FF_FRICTION/FF_INERTIA (conditions): send only two Report 0x05 packets (coeff/saturation via param_sub; deadband/center via env_sub_first). * FF_RAMP: send only Report 0x04 with start/cur_val/duration. - Subtype rules: * Constant force uses fixed linkage (Report 0x03 code=0x0e; 0x01 b9=0x0e, b11=0x1c; 0x02 subtype=0x1c). * Periodic/ramp/condition continue to use per‑effect subtypes (param_sub = 0x0e + 0x1c×index; env_sub_first = 0x1c + 0x1c×index). - Upload sequence parity with captures: * Pre‑upload STOP (0x41 00 01), dual 0x02 envelopes, 0x01 first/second, and effect‑specific parameter packets (0x03/0x04/0x05) as observed on Windows. - Protocol correctness fixes and clarifications: * Periodic parameter uses frequency in Hz×100 (not period in ms). * EffectID semantics enforced: 0x01/0x41 use EffectID=0 (exception: init STOP for autocenter). - Quality‑of‑life: * Range setting simplified to a single 0x40 0x11 command (removed smoothing). * Wine compatibility: ignore AC=100% requests to avoid permanent autocenter in titles like LFS; documented rationale.
1 parent dcb3704 commit 0127783

File tree

4 files changed

+469
-210
lines changed

4 files changed

+469
-210
lines changed

docs/FFB_T500RS.md

Lines changed: 84 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
This page documents the packet formats and ordering used by the T500RS when driven via the USB interrupt endpoint (0x01 OUT). It mirrors the style of docs/FFBEFFECTS.md for other wheels.
44

5+
6+
Quick reference (Windows parity, at-a-glance)
7+
- EffectID rule: All 0x01 uploads and 0x41 START/STOP use EffectID=0x00
8+
- Subtypes per effect index n: param = 0x0e + 0x1c×n; envelope = 0x1c + 0x1c×n; second envelope = first + 0x1c
9+
- Periodic 0x04 uses frequency (Hz×100), not period (ms)
10+
- Upload sequences (per effect):
11+
- Constant: STOP → 0x02 → 0x01 → 0x02 → 0x03 → 0x01 → START
12+
- Periodic (sine/square/triangle/saw): STOP → 0x02 → 0x01 → 0x02 → 0x04 → 0x01 → START
13+
- Ramp: STOP → 0x02 → 0x01 → 0x02 → 0x04 → 0x01 → START
14+
- Condition (spring/damper/friction/inertia): STOP → 0x05(set1) → 0x05(set2) → 0x01 → START
15+
516
Key rules
617
- Little‑endian for 16‑bit fields
718
- DMA‑safe buffer length 32 bytes; typical messages are 2, 4, 9 or 15 bytes
@@ -10,6 +21,14 @@ Key rules
1021
- Report 0x41 START/STOP also uses EffectID = 0x00, except the init‑time STOP for autocenter which targets a fixed ID (15)
1122
- Using per‑effect IDs with 0x01 breaks playback (constant force fails completely)
1223

24+
Special case — constant force subtypes
25+
26+
- For FF_CONSTANT, the device uses fixed subtypes to link the 0x03 level update:
27+
- Report 0x02 subtype = 0x1c
28+
- Report 0x01 b9 = 0x0e (parameter), b11 = 0x1c (envelope)
29+
- Report 0x03 code = 0x0e
30+
- Using per‑effect subtypes for constant breaks level updates (no force felt).
31+
1332
Report glossary
1433
- 0x01 main upload (15 bytes)
1534
- Layout (unknown bytes kept for completeness):
@@ -22,9 +41,9 @@ Report glossary
2241
- b6 0x00
2342
- b7 0xff
2443
- b8 0xff
25-
- b9 0x0e (parameter subtype)
44+
- b9 param subtype = 0x0e + 0x1c×index
2645
- b10 0x00
27-
- b11 0x1c (envelope subtype)
46+
- b11 envelope subtype = 0x1c + 0x1c×index
2847
- b12 0x00
2948
- b13 0x00
3049
- b14 0x00
@@ -41,7 +60,7 @@ Report glossary
4160
- b0 0x04, b1 0x0e, b2 0x00
4261
- b3 magnitude u7 (0..127)
4362
- b4 offset = 0, b5 phase = 0
44-
- b6..7 period (le16, ms)
63+
- b6..7 frequency (le16, Hz×100) (e.g., 10 Hz → 0x03e8)
4564
- 0x04 ramp params (9 bytes)
4665
- b0 0x04, b1 0x0e
4766
- b2..3 start (le16), b4..5 cur_val (le16), b6..7 duration (le16), b8 0x00
@@ -56,27 +75,76 @@ Report glossary
5675
- 0x42 apply/apply‑like, 0x43 gain
5776

5877
Effect upload and play ordering
78+
79+
- Subtype system (applies to 0x01/0x02/0x04/0x05 references)
80+
- Envelope subtype base = 0x1c; Parameter subtype base = 0x0e
81+
- For effect index n: envelope = 0x1c + 0x1c×n; parameter = 0x0e + 0x1c×n
82+
- In 0x01: b9 = parameter subtype; b11 = envelope subtype
83+
84+
- Global (Windows parity)
85+
- Precede uploads with 0x41 STOP (command=0x00, arg=0x01), effect_id=0x00, to clear state
86+
5987
- Constant (FF_CONSTANT)
60-
1) 0x02 envelope
61-
2) 0x01 main (type=0x00, effect_id=0x00)
62-
Play: 0x03 level, then 0x41 START (effect_id=0x00)
88+
1) 0x41 STOP
89+
2) 0x02 envelope (first; subtype = 0x1c + 0x1c×index)
90+
3) 0x01 main (first; b9/b11 reference current param/envelope subtypes)
91+
4) 0x02 envelope (second; subtype += 0x1c)
92+
5) 0x03 level
93+
6) 0x01 main (second; update references)
94+
Play: 0x41 START (effect_id=0x00)
95+
6396
- Periodic (FF_SINE/FF_SQUARE/FF_TRIANGLE/FF_SAW)
64-
1) 0x02 envelope
65-
2) 0x01 main (type=0x20..0x24, effect_id=0x00)
66-
3) 0x04 periodic params (magnitude/period)
67-
4) 0x01 main (type repeated, effect_id=0x00)
97+
1) 0x41 STOP
98+
2) 0x02 envelope (first)
99+
3) 0x01 main (first; type=0x20..0x24, effect_id=0x00)
100+
4) 0x02 envelope (second; subtype += 0x1c)
101+
5) 0x04 periodic params (magnitude/frequency)
102+
6) 0x01 main (second; type repeated)
68103
Play: 0x41 START (effect_id=0x00)
104+
69105
- Condition (FF_SPRING/FF_DAMPER/FF_FRICTION/FF_INERTIA)
70-
1) 0x05 coeff/saturation (0x0e)
71-
2) 0x05 deadband/center (0x1c)
72-
3) 0x01 main (type reflects subkind, effect_id=0x00)
106+
1) 0x41 STOP
107+
2) 0x05 coeff/saturation (param subtype = 0x0e + 0x1c×index)
108+
3) 0x05 deadband/center (envelope subtype = 0x1c + 0x1c×index)
109+
4) 0x01 main (type reflects subkind, effect_id=0x00; b9/b11 reference above)
73110
Play: 0x41 START (effect_id=0x00)
111+
74112
- Ramp (FF_RAMP)
75-
1) 0x02 envelope
76-
2) 0x04 ramp params (device behaves like hold of start level with duration)
77-
3) 0x01 main (type=0x24, effect_id=0x00)
113+
1) 0x41 STOP
114+
2) 0x02 envelope (first)
115+
3) 0x01 main (first; type=0x24, effect_id=0x00)
116+
4) 0x02 envelope (second; subtype += 0x1c)
117+
5) 0x04 ramp params (device behaves like hold of start level with duration)
118+
6) 0x01 main (second)
78119
Play: 0x41 START (effect_id=0x00)
79120

121+
122+
Live update behavior (parameter‑only; Windows parity)
123+
124+
- Overview
125+
- Parameter updates do not re‑upload effects or send STOP; they modify parameters in place.
126+
- This reduces USB traffic and avoids momentary dropouts during gameplay.
127+
- 0x01/0x41 still follow the EffectID=0 rule; 0x04/0x05 carry subtypes, not effect IDs.
128+
129+
- Constant (FF_CONSTANT)
130+
- Send only 0x03 with new level; code must be 0x0e (fixed linkage to constant path).
131+
- No 0x41 STOP and no 0x01 re‑upload for updates.
132+
133+
- Periodic (FF_SINE/FF_SQUARE/FF_TRIANGLE/FF_SAW)
134+
- Send only 0x04 with new magnitude and frequency.
135+
- Frequency field is Hz×100 (e.g., 10 Hz → 1000); offset/phase remain 0 unless changed.
136+
- Use per‑effect param_sub = 0x0e + 0x1c×index.
137+
138+
- Condition (FF_SPRING/FF_DAMPER/FF_FRICTION/FF_INERTIA)
139+
- Send 0x05 (coeff/saturation) with param_sub, then 0x05 (deadband/center) with env_sub_first.
140+
- Subtypes are the same as in the upload path; spring uses subtype bytes 0x54, others 0x64 for the tail fields.
141+
- No 0x01 re‑upload.
142+
143+
- Ramp (FF_RAMP)
144+
- Send only 0x04 (start/cur_val/duration, last byte 0x00) using param_sub.
145+
- Device behavior approximates ramp via holding/stepping the start value over duration; matching observed Windows behavior.
146+
147+
80148
Notes
81149
- All 0x01 uploads must use EffectID=0x00. This was validated on hardware; using per‑effect IDs causes constant force to fail completely and can break other effects.
82150
- 16‑bit values are little‑endian (lo byte first).

src/hid-tmff2.c

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,6 @@
66
#include "hid-tmff2.h"
77

88

9-
/* Known vendor/opcode IDs managed by the driver (TX side) */
10-
static inline int tmff2_is_known_vendor_id(unsigned char id)
11-
{
12-
switch (id) {
13-
case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
14-
case 0x40: case 0x41: case 0x42: case 0x43:
15-
case 0x0a:
16-
return 1;
17-
default:
18-
return 0;
19-
}
20-
}
21-
22-
239
int open_mode = 1;
2410
module_param(open_mode, int, 0660);
2511
MODULE_PARM_DESC(open_mode,
@@ -256,6 +242,22 @@ static ssize_t gain_store(struct device *dev,
256242
}
257243

258244
gain = value;
245+
246+
/* Rationale: two-level gain model
247+
* - The input API's set_gain (pg) is the in-game gain (0..GAIN_MAX).
248+
* - This driver also exposes a device/system gain via sysfs param `gain`.
249+
* - The device callback receives the product: (pg * gain) / GAIN_MAX.
250+
* See worker at tmff2->set_gain(... (pg * gain) / GAIN_MAX ).
251+
* When the sysfs `gain` changes, we trigger a recompute by pushing
252+
* pending_gain_value = GAIN_MAX here so the effective device gain becomes
253+
* exactly the sysfs value (GAIN_MAX * gain / GAIN_MAX == gain) and future
254+
* in-game set_gain calls continue to multiply in.
255+
*
256+
* References:
257+
* - docs/FFBEFFECTS.md: section "FF_GAIN" shows a dedicated device gain path.
258+
* - docs/FFB_T500RS.md: Report glossary mentions 0x43 (gain), i.e., device-side
259+
* gain separate from per-effect magnitudes; drivers should expose both levels.
260+
*/
259261
if (tmff2->set_gain) {
260262
unsigned long flags;
261263
spin_lock_irqsave(&tmff2->lock, flags);
@@ -376,7 +378,7 @@ static void tmff2_work_handler(struct work_struct *w)
376378
effect_length = state->effect.replay.length;
377379
/* If playing with a finite length, stop when (delay + length) elapses */
378380
if (test_bit(FF_EFFECT_PLAYING, &state->flags) && effect_length) {
379-
if ((time_now - state->start_time) >=
381+
if ((time_now - state->start_time) >=
380382
(effect_delay + effect_length) * state->count) {
381383
__clear_bit(FF_EFFECT_PLAYING, &state->flags);
382384
__clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags);
@@ -853,7 +855,6 @@ static void tmff2_remove(struct hid_device *hdev)
853855
if (tmff2->params & PARAM_DAMPER_LEVEL)
854856
device_remove_file(dev, &dev_attr_damper_level);
855857

856-
857858
if (tmff2->params & PARAM_SPRING_LEVEL)
858859
device_remove_file(dev, &dev_attr_spring_level);
859860

@@ -889,6 +890,7 @@ static const struct hid_device_id tmff2_devices[] = {
889890

890891
{}
891892
};
893+
MODULE_DEVICE_TABLE(hid, tmff2_devices);
892894

893895
static struct hid_driver tmff2_driver = {
894896
.name = "tmff2",

0 commit comments

Comments
 (0)