hid: tmff2: add Thrustmaster T500RS wheel base driver#186
hid: tmff2: add Thrustmaster T500RS wheel base driver#186cazzoo wants to merge 15 commits intoKimplul:masterfrom
Conversation
2ae18a3 to
bd43bb9
Compare
|
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 |
Kimplul
left a comment
There was a problem hiding this comment.
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.
|
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). |
9e6319b to
b7d188d
Compare
|
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. |
|
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. |
|
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. Cheers |
408f5bf to
1c5af2c
Compare
That's the responsibility of |
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. 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) |
Yeah, I intended to merge |
|
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 |
Kimplul
left a comment
There was a problem hiding this comment.
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
1bf936c to
5c31c0e
Compare
|
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. 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. |
5015d21 to
3e8c027
Compare
Kimplul
left a comment
There was a problem hiding this comment.
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.
docs/T500RS_FFBEFFECTS.md
Outdated
| 41 00 41 01 - START command | ||
| ``` | ||
| - **Packet Type:** 0x41 (Command) | ||
| - **Effect ID:** 0x00 (always 0x00 for T500RS) |
There was a problem hiding this comment.
I believe we've already established that the effect ID varies depending on what effects are loaded, no?
docs/T500RS_FFBEFFECTS.md
Outdated
| 41 00 00 01 - STOP command | ||
| ``` | ||
| - **Packet Type:** 0x41 (Command) | ||
| - **Effect ID:** 0x00 (always 0x00 for T500RS) |
docs/T500RS_FFBEFFECTS.md
Outdated
|
|
||
| **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. |
There was a problem hiding this comment.
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.
docs/T500RS_FFBEFFECTS.md
Outdated
| 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!) |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
docs/T500RS_FFBEFFECTS.md
Outdated
|
|
||
| **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) |
There was a problem hiding this comment.
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 :)
docs/T500RS_FFBEFFECTS.md
Outdated
| - 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!) |
| - 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. |
There was a problem hiding this comment.
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). |
There was a problem hiding this comment.
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.
docs/T500RS_FFBEFFECTS.md
Outdated
| - **Device Format:** 16-bit little-endian (0-35999 in 0.01 degree units) | ||
| - **Conversion:** `device_dir = (os_ffb_dir * 36000) / 65536` | ||
| - **Examples:** | ||
| - 0degrees = 0x0000 |
There was a problem hiding this comment.
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.
|
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. |
|
Good catch! Thank you very much for the report |
|
@SeitzB I think you have the wrong branch checked out, |
Kimplul
left a comment
There was a problem hiding this comment.
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.
src/tmt500rs/hid-tmt500rs.c
Outdated
| 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, |
There was a problem hiding this comment.
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. |
src/tmt500rs/hid-tmt500rs.c
Outdated
| offset = (s8)((end_level - start_level) / 512); | ||
|
|
||
| /* | ||
| * Phase encodes ramp direction per FFEdit captures: |
| */ | ||
| phase = (start_level < end_level) ? 0x7f : 0x00; | ||
|
|
||
| /* Byte order per USB captures: b0=id, b1=code, b2=reserved1, b3=mag, |
| p->id = 0x02; | ||
| p->subtype = subtype; | ||
|
|
||
| /* |
|
|
||
| T500RS_DBG(t500rs, "Sending initialization sequence...\n"); | ||
|
|
||
| /* Report 0x42 - Init/status commands (2 bytes each) |
| hid_warn(t500rs->hdev, "Init command 0x42 0x00 failed: %d\n", | ||
| ret); | ||
|
|
||
| /* Report 0x40 - Enable FFB (4 bytes) |
| hid_warn(t500rs->hdev, | ||
| "Init command 3 (0x40 config) failed: %d\n", ret); | ||
|
|
||
| /* Report 0x43 - Set global gain (2 bytes) |
src/tmt500rs/hid-tmt500rs.c
Outdated
| u8 right_sat = (cond->right_saturation * 100) / 65535; | ||
| u8 left_sat = (cond->left_saturation * 100) / 65535; | ||
|
|
||
| struct t500rs_pkt_r05_condition *p = |
There was a problem hiding this comment.
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: { |
There was a problem hiding this comment.
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].
|
Commit c6d27c3 says:
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? |
|
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 |
ab8801b to
8d749d8
Compare
|
thanks @cazzoo! I need a bit of help pulling the correct version. I'll clone the repo as usual and then.. how do I switch to the commit ID you mention above? Would git checkout <commit_hash> do the trick, and then build the module as usual from there in my current src/hid-tmff2 directory that already exists? Sorry I'm a complete git noob (well except for the standard git clone stuff, that is :-)) thanks, Uwe |
|
Nevermind, I think I managed to get the current version with some help from gpt... the module builds now, I hope I didn't do anything wrong: (bash history) |
|
ok here's what happens after building & installing the module & udev-rules, unplugging the wheel and rebooting. plug in the wheel: reports "no device available". sudo oversteer still reports "no device available". After running "tmdrv" above, I can now run "sudo oversteer" but I can no longer change any settings like wheel range, ffb and so on (which worked with the old module version I was running before). I'd be very happy to hear what I'm doing wrong (for one, there should be no need to modprobe the modules manually, right? Also I'm still confused why I still need to run tmdrv in order for oversteer to recognize the wheel. I'll test if ffb still works in any of the titles where it worked before updating to the current build. Thanks in advance & all the best, Uwe |
|
Hi folks, FFB effects no longer work in AM2 and AC Rally, both only display a constant centering force now. rfactor2 also has the same constant centering force now which is an improvement of sorts because I could no longer get the ffb to work even once (as described) using the procedure outlined above with the old module version I was running. The wheel would go completely limp in rfactor2. All the best, Uwe |
|
Hey, probably my final report for tonight :-) after reverting back to version 0.82 that I was running before, FFB effects are back in AC Rally, also I can set the wheel range again using "sudo oversteer". When I ran the "tests" oversteer thingy with the most recent version of the module I got a python error (didn't catch it sadly) about a "missing effect wheel left" or similar in the console where I started sudo oversteer from, so it looks like the input event device doesn't advertise the FFB caps of the wheel. This is no wonder because at no point did I see this message with the current version of the module: which I am only seeing with the older version 0.82. If there's any other info I can provide please let me know & thanks for your hard work! All the best, Uwe |
|
Hi @hoover67 , I think you are pulling the master branch from my fork repository, that may explain the loss of FFB. Then, build & insert module, either using dmks script or You shall get FFB back on AMS2, Dirt Rally2, ...etc. In parallel, I've tried to get FFB in RFactor2 (that I just bought to test FFB) and got none. I haven't review the python script you shared, but that definitely sound a bit OTT to get FFB working. Please note that I encountered the same issue in RRRE (Raceroom) and haven't find any solution yet. |
|
Hey @cazzoo, thanks much for the update, I'll try it later tonight or over the weekend. As for rfactor2, I got FFB working exactly once with version 0.82 using the method described above (unplug -> replug -> run tmdrv -> run oversteer as root -> start rfactor2), but following a reboot or other (sorry, I don't know exactly what happened in between sessions) I was not able to get it working in rfactor2 again. Sadly I also don't recall if SteamInput was disabled or any of the HIDRAW runcommand magic was configured in Steam. Could it be possible that other ffb sims accessing the wheel before starting rf2 could have "paved the way" for rfactor2 to pick up the wheel correctly? AFAIK the tmdrv script only initializes the wheel & pedals so they can be used as a Joystick. I've used it mainly in past so I could assign the toe brakes and rudder axis to the T500RS pedals. All the best, Uwe |
|
After numerous tests, I haven't found a solution for that issue that happens for both rfactor2 and RRRE. I believe that may be related to Wine/Proton and could be managed by a fix introduced here (but don't know what). @Kimplul have you ever heard about that issue. When entering in-game (3D) first, FFB works, when getting to menu FFB drops, and then when getting back to in-game it doesn't work anymore. I noticed already during implementation that the games usually don't send start commands. So the wheel is playing once after enumeration, then the packets are being updated in-place (which work for most of the games, but RRRE and Rfactor2). All of this is assumptions, but this is what I roughly feel |
Can't say I really have. You can try using If the game keeps uploading effects but nothing is happening with the wheel, check that the driver is actually sending out packets, for example by just adding some print statement to If the driver keeps sending packets, then the driver is doing something that the wheel doesn't like, but no clue what that could be. |
Oh, I suppose you already checked Updating effects in-place is explicitly allowed by the FFB API, so it maybe seems like there's something going wrong in |
|
Hi folks, I used the git command above to follow the hardened branch in the hid-tmff2 directory, recompiled the modules (which worked) and installed them using sudo make install and install-udev. I still have to use the "tmdrv" command or "oversteer" will complain about "no device found". Once I run tmdrv to init the wheel, the driver gets loaded according to "dmesg". FFB is back in AC Rally and Automobilista 2. I had to re-assign the controls in AC Rally but a major patch was released today (0.3, it's still in early access) so that may have been the cause. AM2 did not require any reassignments. in rfactor2, I selected "disable SteamInput" and checked the Proton version I'm using (9.0) with this title. I'm happy to report that this time, rfactor2 had FFB until I returned from 3d on-track to the 2d UI. Re-entering the track a 2nd time, FFB was gone again. I have not rebooted the machine in between game launches, but I'll try that next (hopefully not losing my comment here for the umpteenth time :-)) and report back. All the best, Uwe @cazzoo It's very kind of you the shell out real money in order to test rfactor2. If you'd like to be re-imbursed for your troubles I'm happy to contribute :-) |
|
I nailed the issue a bit down and found some more clues. So confirming the issue happens for both games, I found that the game (when doing alt+tab or get back to pits) is stopping effects. When entering back in race, somehow, the effects are not started back.. So I tested adding an extra re-init script (manually triggered) : Stop work handler, Reset all effect states and then Reinitialize wheel (tmff->open), and that worked! So I don't exactly know if there's a bug to fix in Wine/Proton, but that workaround worked. I'm trying to get it added to the base driver but I'm not entirely convinced this is the right place, especially knowing this is shared code with other wheel bases... |
I don't think that's a good approach. From the logs it looks like effects 0-3 are all requesting a start, which the driver should be able to handle just fine. Not sure why the game seems to be sending multiple stop signals, but that shouldn't matter. Looks like all the messages are printed within ~50 milliseconds. Is it maybe possible that the driver handles the STOP requests, and then ignores the START requests because they arrived 'too soon'? |
|
Thanks for putting in the work debugging this folks. rfactor2 supports plugins. Is this something that could be handled on a plugin level maybe? rfactor2 even has an FFB reset function (I wonder why) but sadly this does nothing to fix the problem in this case (I've tried :-)) Sorry in advance if this is a stupid idea and / or won't work, but maybe a few lines in an rf2 / RR plugin is all that is needed... All the best, Uwe |
|
@hoover67 As far as I can tell, the game itself (+Proton+SDL etc.) is behaving as it should and is following the FFB API, but the driver is not responding correctly, therefore the driver should be fixed. I'm sure there are all kinds of workarounds that could be implemented, but I don't want to accept clearly incorrect code into this repository. I hope there's no rush here and we can take the time to do things properly :) |
|
Maybe it could be helpful if we looked at what a working driver does? I have a Logitech G27 which has relatively mature drivers afaik. I could buy rfactor and try it with the g27 and provide you with whatever logs might be helpful to you. And if it also doesn't work with the G27 we would have confirmation that it may not be a driver issue after all? Just an offer |
That's an interesting offer, if you mind that could definitely help. Rfactor2 is not overpriced, and you can get refund if you manage to capture information rather quick |
|
@SeitzB Appreciate the offer, can't hurt to compare against another implementation but at least my T300 works with RRRE and rFactor2. Or, at the very least has worked, haven't played either in a while but either way the games themselves are likely working as intended. |
|
Just to let you know, I figured that if I restrict the driver to use only 1 effect slot, I don't have the issue. |
|
@Kimplul, I managed to understand what was going wrong, and it's not particularly due to t500RS code, but due to the fact I am sending interrupts and not HID commands, more particularly it's how the base driver (tmff2) is handling multiple stop/start events while focusing out and in the game. I tested multiple times and this completely fixed the issue. The rationale is: Because this is impacting the base driver, I decided not to commit directly into my branch, but created dedicated branch and a PR from my branch towards my branch, with the fix included: cazzoo@83bbd95 Would you mind checking the code and telling me if you see anything anti-pattern or against the base code, that may break the other wheels. |
That doesn't make sense to me. Setting and clearing the flags is done within a spinlock, where exactly would the race condition be? If you mean that the worker sees a STOP, and while handling the STOP, a START arrives, I could see that the START is not picked up. However, since the worker is running, How is the interrupt/hid messaging stuff related? Is the interupt just slower, meaning a greater chance of mismatched start/stop? |
I don't really know why this happens, but as soon as we have multiple effects handled by the driver, it does fail to get all started effects to be stopped, and new effects started. We can see the sequence is slightly different between focuses-in that works and the ones that doesn't work (the pattern is always the same when failing or succeeding). From my attempts, getting all the effects handled with the 3 phases sequence ensures the STOPS are all processed and then the STARTS can start. I will definitely check the work handler, see if it stops or not when focus out.
I don't really know, but the game send the exact same command in the same order to T500 and T300, and T300 handles it properly. T300 uses HID layer AFAIK whereas T500 uses USB interrupts, so my assumption is that HID layer is handling this. |
|
So, I figured there's no issue with the work handler, it's still processing after game started, no matter an ALT+TAB has been pressed or whatsoever (getting back to game's pits menu). After some more analysis, the T500RS hardware requires all effects to be uploaded before any are started. This is a device protocol requirement, not a software bug: When doing Sequential, it failed: Upload effect0 → Start effect0 → Upload effect1 → Start effect1 → ... When we interleave uploads and starts, the device gets confused and FFB doesn't work. The three-pass approach ensures the device is in a consistent state before starting effects. This corroborate with my previous finding: T500RS only upload and play once, then it's continuously sending updates. I am happy to revisit the way the t500rs driver works, but I think we're getting to an edge-case that need to be dealt in a particular way, and I'm getting to the end of my knowledge to troubleshoot this case. @Kimplul, If you have any other suggestion, my ears are open and I'm fine to work back on the logic, just let me know. From all my attempts, without that three-pass it was randomly failing (unless I forced only 1 effect slot for the driver), and at the moment I am putting in place that workaround, it's working all the time long (100% success rate). |
|
EDIT: I tried the "git checkout" command mentioned above and I receive an error that the branch named already exists. Should I just delete everything and repeat the process? Hey @cazzoo, thanks for the update. Is there a way for me to test your "workaround" & maybe provide (hopefully helpful) feedback? All the best, Uwe |
|
@hoover67 you have to do git fetch cazzoo origin (commands are not correct), then checkout branch "hardening/fix-multi-stop-start-race-condition" from cazzoo origin as well, and then build&install driver. |
|
EDIT: Never mind, managed to (hopefully) checkout the correct version, will try it later on. Sorry for the noise! :-) Uwe |
|
@cazzoo Looking great! Just tried rfactor2 with the latest version of your driver including the race condition workaround and I was able to enter the track several times without the wheel losing FFB effects... thanks so much guys for all your hard work. |
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).