Skip to content

fix force feedback arithmetic and wheel input filtering - #211

Merged
Kimplul merged 4 commits into
Kimplul:masterfrom
kakra:t300-correctness-fixes
Aug 9, 2026
Merged

fix force feedback arithmetic and wheel input filtering#211
Kimplul merged 4 commits into
Kimplul:masterfrom
kakra:t300-correctness-fixes

Conversation

@kakra

@kakra kakra commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

This series contains four independent correctness changes in force feedback and wheel input handling:

  • restrict the configured driver gain to its documented unsigned 16-bit range
  • avoid signed integer overflow when combining application and driver gain
  • preserve the full signed effect range during direction scaling
  • remove generic HID fuzz and flat filtering from wheel and pedal axes

The commits are intentionally kept separate so each change remains bisectable and can be reviewed or reverted independently.

No gain or rotation-range defaults are changed.

Gain configuration and scaling

The configured driver gain is documented as an unsigned 16-bit value, but was stored and parsed as int. Values outside 0..65535 were accepted and silently clamped later.

The configured gain is now stored as u16. Module-parameter and sysfs values outside the documented range are rejected as invalid user input.

Both FF_GAIN and the driver gain use the full unsigned 16-bit range. Their product can exceed INT_MAX, so the existing signed multiplication invokes undefined behaviour for common configurations such as 75 percent application gain combined with 75 percent driver gain.

Gain multiplication now uses a u32 intermediate, which can hold the product of two full-range 16-bit values. The initial and sysfs paths pass the already validated gain directly instead of using redundant multiply-divide expressions which could overflow before cancelling out.

Signed effect scaling

The driver uses a symmetric fixed-point sine range of -32767..32767. Scaling the valid input value -32768 by -32767 therefore produces 32768.

The old code stored that result in s16 before completing the effect conversion, wrapping it to -32768. At full scale this could reverse the force direction instead of producing the expected positive force.

Direction-scaled values are now kept in s32 until the destination range is known. Periodic magnitudes are made positive in the wider type and clamped to S16_MAX, preventing both the endpoint wrap and the subsequent invalid headroom calculation.

Constant and ramp effects retain their wider intermediates until their final conversion as well.

Input filtering

The generic HID input setup derives fuzz and flat values from the logical axis range. On the tested T300 this produces:

ABS_X: fuzz=255 flat=4095
pedals: fuzz=3 flat=63

The steering values suppress and smooth small corrections before an application receives them. This is particularly noticeable when changing steering direction.

flat represents a centered deadzone, which is also unsuitable for pedals whose rest position is an endpoint. Pedal fuzz can suppress small changes near an endpoint, causing the reported value to remain slightly short of the hardware endpoint.

The input setup now sets fuzz=0 and flat=0 for every available wheel and pedal axis on devices handled by hid-tmff2. Missing axes are skipped through the input device's absbit mask.

This follows the existing hid-universal-pidff precedent of overriding generic HID filtering for high-resolution racing devices.

Related issues

Testing

Completed on a T300RS GT:

  • make and make W=1 against Linux 6.18.38
  • git diff --check
  • numerical validation across the complete signed effect input range
  • loaded and tested the module built from this exact branch
  • verified fuzz=0 and flat=0 on the steering and pedal axes
  • regression-tested steering, pedals and Force Feedback in ETS2 under Proton
  • exercised Constant, Ramp, Periodic, Spring, Damper and Friction effects
  • exercised 100 percent driver master gain during normal gameplay

Damper and Friction effects were accepted without an observed error, although their individual contribution was subjectively subtle. Damper appeared to reduce wheel run-on, consistent with velocity-dependent damping.

Still pending before marking this PR ready:

  • regression-test Forza Horizon 5 with the module built from this exact branch

Deferred hardware validation:

  • related Thrustmaster wheel models are not locally available and will require testing by the maintainer or community
  • additional master-gain values may be tested, but the scaling arithmetic has already been validated numerically over its full input range

Comment thread src/hid-tmff2.c Outdated
Comment thread src/hid-tmff2.c Outdated
Comment thread src/hid-tmff2.c Outdated
@Kimplul

Kimplul commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Thanks for the PR, appreciate it! I commented on a couple minor things, although really only #211 (comment) is a functional change, the other ones are kind of personal preference I guess.

@kakra

kakra commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback. I addressed your commets with fixup commits for now. Before merge, I'll rebase and reorder the commits.

@kakra kakra changed the title fix force feedback arithmetic and T300 input filtering fix force feedback arithmetic and wheel input filtering Aug 8, 2026
@kakra

kakra commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

@Kimplul BTW: I have a few Proton commits pending (not related to your code, just informational): Games in Proton do not detect the wheel when cold plugged while they use the raw Gaming Input interfaces. There's a missing index reset in the interface. Also, for games using that interface, the force values seem to be sent inverted. I'm still in the process of testing those commits here before submitting them upstream to wine, so nothing to see yet. I just wanted to let you know because it looks like some issues mention exactly these bugs.

@kakra

kakra commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

I updated the initial PR text to reflect the current state of the code.

kakra added 4 commits August 9, 2026 00:20
+ Gain is documented and sent to the hardware as a value in the range 0 to
  65535, but the module parameter and sysfs attribute accept wider integer
  values. Store gain as a u16 and parse sysfs writes with kstrtou16 so
  out-of-range values are rejected instead of being silently changed.

+ This constrains only the configured value. Calculations combining gain
  values still require a wider intermediate to cover the product of two
  full-range 16-bit values.
+ FF_GAIN and the driver gain both use the full 16-bit range. Their product
  can exceed INT_MAX. Signed overflow is undefined and happens for common
  settings such as 75 percent times 75 percent. Use a u32 intermediate for
  the scaling operation.

+ Pass the configured gain directly when initializing or updating the
  hardware. This also removes two redundant multiply-divide expressions
  which could overflow before cancelling out.
+ Direction scaling can produce 32768 for the valid input pair -32768 and a
  negative full-scale sine value. Storing the intermediate result in s16
  wraps it before constant, ramp or periodic effects are converted.

+ Keep direction-scaled values in s32 until their destination range is
  known. Periodic effects cannot represent a magnitude above S16_MAX, so
  clamp the asymmetric endpoint after making the magnitude positive. This
  also prevents the following headroom calculation from using a negative
  wrapped magnitude.
+ HID derives fuzz and flat from the logical axis range. On the tested T300
  this gives steering fuzz=255 and flat=4095. It filters small corrections
  and adds a large centered deadzone before applications apply their own
  steering model.

+ Flat is also centered for pedal axes, while their rest position is an
  endpoint. Fuzz without an endpoint deadzone can retain a small nonzero
  value after the hardware has returned to rest. Both behaviours are
  inappropriate for wheel and pedal input, and applications already provide
  the required steering and endpoint deadzones.

+ Override fuzz and flat for every available wheel and pedal axis in
  input_configured on devices handled by hid-tmff2, following the existing
  hid-universal-pidff precedent for high-resolution racing devices. Skip
  absent axes through the input device's absbit mask.
@kakra
kakra force-pushed the t300-correctness-fixes branch from 4a02f58 to 4c142e1 Compare August 8, 2026 22:22
@kakra
kakra marked this pull request as ready for review August 8, 2026 22:22
@Kimplul

Kimplul commented Aug 8, 2026

Copy link
Copy Markdown
Owner

@Kimplul BTW: I have a few Proton commits pending (not related to your code, just informational): Games in Proton do not detect the wheel when cold plugged while they use the raw Gaming Input interfaces. There's a missing index reset in the interface. Also, for games using that interface, the force values seem to be sent inverted. I'm still in the process of testing those commits here before submitting them upstream to wine, so nothing to see yet. I just wanted to let you know because it looks like some issues mention exactly these bugs.

Cool! Please do ping me when you're sending the patches, I can relay the info to the relevant threads and see what happens.

Thanks a lot for the helpful pointers as well.

@kakra

kakra commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

Please do ping me

Noted, I've set a reminder to do that when the Valve PRs are created.

@Kimplul
Kimplul merged commit c5b9b79 into Kimplul:master Aug 9, 2026
@Kimplul

Kimplul commented Aug 9, 2026

Copy link
Copy Markdown
Owner

I checked that my T300RS and T248 behaved properly and my nitpicks were taken care of, so I went ahead and merged. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants