Skip to content

hid: tmff2: add Thrustmaster T500RS wheel base driver#186

Open
cazzoo wants to merge 14 commits intoKimplul:masterfrom
cazzoo:hardening/t500rs-driver
Open

hid: tmff2: add Thrustmaster T500RS wheel base driver#186
cazzoo wants to merge 14 commits intoKimplul:masterfrom
cazzoo:hardening/t500rs-driver

Conversation

@cazzoo
Copy link

@cazzoo cazzoo commented Dec 9, 2025

This driver has been built from scratch based the previous dirty driver (see #175) and on captures from ffbsdl tool ran in windows for all possible effects (through SDL2 library).

@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from 2ae18a3 to bd43bb9 Compare December 9, 2025 00:21
@cazzoo cazzoo marked this pull request as ready for review December 9, 2025 00:23
@cazzoo
Copy link
Author

cazzoo commented Dec 9, 2025

Hi @Kimplul ,

Here is a new version of the driver, fully rebuilt from scratch. I've been trying to do my best to get it done the right way, steering its content the best I could (with my knowledge). I'll discard the previous PR that is not optimal. This one should be supporting more effects, and should be better designed. I've been also working on documentation to make it more comprehensive and based on SDL2 real examples.

Again, I am fully open to your feedback (and also @MmAaXx500 ones) that were really valuable by the other PR, so when you will have time, I'd really happy to get to your review.

Thanks

Copy link
Owner

@Kimplul Kimplul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for continuing on this project, I was getting worried that we scared you off :)

I did a quick overview. The documentation in particular looks a lot better, having clear examples of what each packet does and how they build up to a full effect upload helped greatly. I haven't done any cross-referencing between the docs and implementation, as I still found quite a lot of smaller issues I'd like taken care of first. I'll do a more thorough review after that.

Not entirely sure what you mean by 'from scratch', for instance the comment explaining the set_gain rationale is identical between this and the previous PR.

@cazzoo
Copy link
Author

cazzoo commented Dec 11, 2025

Got the code and the PR updated based on all your feedback. All were valid, the only ones I haven't worked on are the SQUARE effect I plan to work on later and the git version (that I can easily remove).
Let me know any other findings or your thoughts on this updated code

Add support for the Thrustmaster T500RS wheel base in the hid-tmff2 driver.

This driver has been built from scratch based the previous dirty driver (see Kimplul#175) on captures from ffbsdl tool ran in windows for all possible effects (through SDL2 library).
- Simplified effect slot system
- Added support of direction
- Tidy-up code here and there
- Reduced complexity overhead
- Code standardization (in regards of the base repo)
- Documentation updates
Add support for FF_SQUARE periodic waveform based on FFEdit USB captures
@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from d258e05 to 9e6319b Compare December 12, 2025 22:59
Corrected packet structure and scaling formulas
Added ramp support
@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from 9e6319b to b7d188d Compare December 12, 2025 23:01
@cazzoo
Copy link
Author

cazzoo commented Dec 12, 2025

Hi @Kimplul ,

I've been working again on the PR, responding to all your points (almost all -- if not all -- were relevant), and introducing some missing features (FF_SQUARE missing and 0x05 conditional effect that was incorrectly done). If you mind having another pass, I'd be really happy.
Thanks

@Kimplul
Copy link
Owner

Kimplul commented Dec 17, 2025

Sorry about the delay, bit busy before the end of the year. I had a look through the resolved comments, most looked good but there were a few that I decided to unresolve, please have a second look at them.

I still haven't cross-referenced documentation to the code, I'll try and get that done by the end of next week, but based on a cursory look the code looks a lot better now. Seems you managed to shave off ~400 lines of code, well done.

Please also add a copyright statement to the start of the files you created, something like https://elixir.bootlin.com/linux/v6.18.1/source/drivers/hid/hid-retrode.c for example. I really should do that as well to the bits I wrote, but this is the first 'major' new addition to the driver so I've never really had any reason to think about it before.

@cazzoo
Copy link
Author

cazzoo commented Dec 17, 2025

No worries, thank you for the feedback you provided. Take the necessary time for whatever need review. I am not in the hurry, and this driver does work for me already so It can last as long as necessary from your end.

I'm happy we trend to get somewhat a stable mergeable version.
I just pushed a new part of code that handled previously the mode switch (from boot/init to normal mode). This is the part where I have the least confidence. The logic is fully fine, it's working well, but I'm not sure at all on code quality and the compliance/integration with the base driver. If you may give a glance when you can, I'd be happy to send some more time if needed.

Cheers

@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from 408f5bf to 1c5af2c Compare December 17, 2025 23:02
@Kimplul
Copy link
Owner

Kimplul commented Dec 18, 2025

I just pushed a new part of code that handled previously the mode switch (from boot/init to normal mode). This is the part where I have the least confidence.

That's the responsibility of hid-tminit, is it not working? Regardless, please revert and if needed, open up a separate PR in https://github.com/Kimplul/hid-tminit/tree/tspc.

@cazzoo
Copy link
Author

cazzoo commented Dec 18, 2025

I just pushed a new part of code that handled previously the mode switch (from boot/init to normal mode). This is the part where I have the least confidence.

That's the responsibility of hid-tminit, is it not working? Regardless, please revert and if needed, open up a separate PR in https://github.com/Kimplul/hid-tminit/tree/tspc.

It is indeed not working with the current code. I'll revert this. Not sure how I should update the init driver yet. Maybe just specifying the boot mode, will test.
Otherwise, if you have any idea based on my commit, let me know


EDIT: I just open a PR against hid-tminit for TSCP branch (even though it's t500rs code update): Kimplul/hid-tminit#2

I tested and it appears to work fine with that code and my driver (and the mode switch reverted from within the driver)

@Kimplul
Copy link
Owner

Kimplul commented Dec 18, 2025

EDIT: I just open a PR against hid-tminit for TSCP branch (even though it's t500rs code update)

Yeah, I intended to merge tspc into usb as part of adding support for the TS-PC, but seems I forgot. I'll sort it out once we deal with this PR, don't want to start changing too many moving bits at the same time.

@cazzoo
Copy link
Author

cazzoo commented Dec 18, 2025

There we are @Kimplul , I've been addressing all the points I guess, and introduced some fix also here or there (you can check latest commits, small scoped). Take your time for the documentation cross-check and let me know, in parallel I'm trying to improve the bits where I can

+ Removal of trailing whitespace, unnecessary unicode chars, small
  formatting changes (though a larger kernel style check should probably
  be performed)
Copy link
Owner

@Kimplul Kimplul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about the delay, I've been busier than expected. I mainly focused on the docs and found some inaccuracies and points of improvement, please have a look at those.

I applied some style fixes myself, please cherry-pick them from https://github.com/Kimplul/hid-tmff2/tree/cazzoo-hardening/t500rs-driver. I don't have push access to your branch and opening a PR for a PR seemed a bit silly, but the changes were trivial enough that I didn't really want to request them explicitly. Kind of a clumsy part of the PR flow, but eh.

Regarding style, I've tried to follow the Linux Kernel style guide in this repo. Most significantly, it dictates a tab width of 8 spaces, whereas this has a width of 2, please fix that. You might also want to try running your code through clang-format with the kernel's style configuration, see https://www.kernel.org/doc/html/next/dev-tools/clang-format.html

- Unicode char replacement
- Documented duration/delays with packet upload
- Code tidy-up (parameters renaming)
- Set structs for init sequence
- Invalid conditional effect types now cause an error instead of silently defaulting
- Ported check for modified parameters to all effects
- Fix saturation handling in T500RS driver & update documentation to reflect it
- Update documentation to clarify dynamic packet code values
- Update examples to use hardware ID 1 instead of 0
- Moved Subtype system section to the bottom
- Hardened documentation style
- Provide details for "DC bias" terminology
- Do not discard 100% gain set but kept warning, more explicit.
@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from 1bf936c to 5c31c0e Compare January 16, 2026 13:41
@cazzoo
Copy link
Author

cazzoo commented Jan 16, 2026

Thank you @Kimplul for all the feedback. No worries for the delay, I also had a lot of parallel work to take care of in the meantime.
Your feedback were valuable, as usual, and it helped to find some odd things (fixed Saturation for instance) that I fixed I believe, making the driver more stable and robust.

Now I believe the code and much more ready, and the documentation updated appropriately and more accurately. Please have another pass and I'd be happy to change anything that's not fine by your POV.

@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from 9c50dc4 to 5015d21 Compare January 16, 2026 14:04
@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from 5015d21 to 3e8c027 Compare January 16, 2026 14:12
Copy link
Owner

@Kimplul Kimplul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least the documentation still needs a bit of work. I would suggest maybe to read through everything a couple of times from a perspective of someone who doesn't know anything about the wheel, reading from top to bottom to get a good overall picture of what the wheel does and how. Present things in the order that they're needed, lots of examples, try to maintain a consistent way to present information, stuff like that.

41 00 41 01 - START command
```
- **Packet Type:** 0x41 (Command)
- **Effect ID:** 0x00 (always 0x00 for T500RS)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we've already established that the effect ID varies depending on what effects are loaded, no?

41 00 00 01 - STOP command
```
- **Packet Type:** 0x41 (Command)
- **Effect ID:** 0x00 (always 0x00 for T500RS)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.


**Important Notes:**
- **Envelope Support:** Envelope packets (0x02) have limited support. Non-zero envelope values cause EPROTO errors on periodic and constant effects. Always send zeros for envelope parameters on these effect types.
- **Runtime Updates:** Effect updates (via `update_effect` callback) only modify parameter-specific packets (0x03, 0x04, 0x05). Duration and delay changes require re-uploading the entire effect.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clarify that this is only a limitation of this driver, and not of the FFB subsystem in general. Just in case someone reference this documentation when trying to figure out how FFB works.

4 | 2 | duration_ms | Duration in milliseconds, little-endian
6 | 2 | delay_ms | Delay before start, little-endian
8 | 1 | reserved1 | 0x00
9 | 2 | packet_code_1 | Code for subsequent packet type (variable!)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

packet_code_1 and packet_code_2 are the same as param_sub and envelope_sub, are they not? I think param_sub and envelope_sub are more descriptive terms, the code uses them as well.

| 0x40 | Spring | Windows driver captures |
| 0x41 | Damper/Friction/Inertia | Windows driver + FFEdit captures |

**Note:** Square wave (0x20) was discovered in FFEdit captures. The Windows driver may not expose this effect type through the standard API.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The windows driver most definitely exposes the square wave. FFEdit uses the Windows API, so I'm not entirely sure what this is referencing anyway?


**Parameter Details:**
- Magnitude: 0-127 (scaled from Linux 0-32767)
- Offset: s8 (-128 to +127, DC bias (Direct Current bias - a constant force offset), scaled from Linux -32768 to +32767)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct Current bias - a constant force offset I mean I know what you're trying to say, but you're still not saying very well and I'm not sure a first-time reader would really get what you're trying to say.

A graphical example would probably be pretty good here, maybe you could try for some ASCII art? At the very least, I would like to see it explained out in full, something like A constant offset means that the periodic effect is stronger when twisting the wheel to one side and weaker to the other. Feel free to come with your own suggestions, I'm not much of a wordsmith :)

- Magnitude: 0-127 (scaled from Linux 0-32767)
- Offset: s8 (-128 to +127, DC bias (Direct Current bias - a constant force offset), scaled from Linux -32768 to +32767)
- Phase: 0-255 (256 steps for 360 degrees, scaled from Linux 0-35999)
- Period: Direct milliseconds (no Hz conversion!)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this Hz conversion?

- effect_type = 0x21 (triangle wave)
- Same envelope and periodic packet structure as sine wave

**Note:** Waveform type determined by effect_type in main packet, not in periodic packet parameters.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be correct to say that each waveform type is its own effect_type?

| 6 | 0x00b6 | 0x00c4 |

### Driver Implementation Notes
- **Effect ID Handling:** The driver uses hardware IDs 1-15 to avoid quirky behavior with hardware index 0 (only valid for constant effects).
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already mentioned above. Sometimes repetition can be useful, but there's only like 15 lines between each occurence so one or the other should probably be removed.

- **Device Format:** 16-bit little-endian (0-35999 in 0.01 degree units)
- **Conversion:** `device_dir = (os_ffb_dir * 36000) / 65536`
- **Examples:**
- 0degrees = 0x0000
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it's customary to put a space between the number and degrees, presumably because degree is a whole word. Short forms, such as ms for millisecond, can be written without a space between.

@SeitzB
Copy link

SeitzB commented Jan 20, 2026

I have been following this development with some excitement and just tried to build and run the current PR. I do not know how to properly mention files in a comment, but in the hid-tmt500rs-usb.c file there is a capital i after a semicolon.

@cazzoo
Copy link
Author

cazzoo commented Jan 20, 2026

Good catch! Thank you very much for the report

@Kimplul
Copy link
Owner

Kimplul commented Jan 20, 2026

@SeitzB I think you have the wrong branch checked out, master seems to have the spurious I while the most recent changes are in hardening/t500rs-driver.

Copy link
Owner

@Kimplul Kimplul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments about the most recent stylistic changes. Looks like clang-format did something weird with the whitespacing in multi-line comments, or maybe they were weird to begin with and clang-format just choked. Dunno.

u8 phase, u16 period_ms)
{
/* Byte order per Windows USB captures (example: 04 2a 00 06 00 3f 0a 00):
* b0=T500RS_PKT_PERIODIC, b1=code, b2=reserved1, b3=mag, b4=offset,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick nitpick: Something funky with the comments, seems to use mixed spaces and tabs?

/* Wheel handles positive magnitudes only */
projected = -projected;

/* Add 180 degrees to phase to maintain correct force direction.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

offset = (s8)((end_level - start_level) / 512);

/*
* Phase encodes ramp direction per FFEdit captures:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

*/
phase = (start_level < end_level) ? 0x7f : 0x00;

/* Byte order per USB captures: b0=id, b1=code, b2=reserved1, b3=mag,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

p->id = 0x02;
p->subtype = subtype;

/*
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same


T500RS_DBG(t500rs, "Sending initialization sequence...\n");

/* Report 0x42 - Init/status commands (2 bytes each)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More comment whitespace issue

hid_warn(t500rs->hdev, "Init command 0x42 0x00 failed: %d\n",
ret);

/* Report 0x40 - Enable FFB (4 bytes)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment whitespace issue

hid_warn(t500rs->hdev,
"Init command 3 (0x40 config) failed: %d\n", ret);

/* Report 0x43 - Set global gain (2 bytes)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

u8 right_sat = (cond->right_saturation * 100) / 65535;
u8 left_sat = (cond->left_saturation * 100) / 65535;

struct t500rs_pkt_r05_condition *p =
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not required to get merged, but the high level of indent here makes the code a bit annoying to read, some kind of refactoring could be useful here, such as instead of t500rs_build_r05_condition and t500rs_send_hid you could instead just have t500rs_send_condition.

break;
}

case T500RS_SEQ_CONDITION_Y: {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does anyone use the second axis? The T300 only uses effect->u.condition[0], and I don't remember seeing kernel drivers use condition[1].

@Kimplul
Copy link
Owner

Kimplul commented Feb 2, 2026

Commit c6d27c3 says:

Addresses all 38 unresolved review comments from Kimplul's Jan 2026 review

That's not true. Most of the issues I pointed out still remain. Did you push the commit by accident or what's the deal here?

@cazzoo
Copy link
Author

cazzoo commented Feb 3, 2026

That was indeed not intended to be pushed straight away, I'm still having a few time to work on it and probably pressed the wrong button. Sorry for the inconvenience

@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch 3 times, most recently from c106fae to cb6305e Compare February 5, 2026 14:41
…d examples

Major changes:
- Add QUICK START section for immediate practical learning
- Add Understanding Effects section with real-world analogies
- Reorganize documentation: practice-first, reference-later
- Standardize terminology: param_sub/env_sub instead of packet_code
- Fix degrees spacing and improve parameter descriptions
- Add consistent example format with packet-by-packet breakdowns
- Fix code comment formatting to kernel style
- Remove redundant & 0xff casts when casting to u8
  The cast to u8 already truncates to 8 bits, making & 0xff redundant
- Fix SDL references to Linux FFB subsystem in header file comments
- Verify no unicode characters in source files
- Remove unnecessary braces
- Added helper functions for build&send packets
@cazzoo cazzoo force-pushed the hardening/t500rs-driver branch from cb6305e to ab8801b Compare February 5, 2026 16:14
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.

3 participants