Effect corrections part2
Part1: #105
Updated the documentation that I forgot in part1, all the indicated values are sent by the Windows driver. Same for part2.
- [x] Gain (no changes required)
- [x] Autocenter (found no way to adjust that through Direct Input, only a GUI slider is available in the driver, based on that seems to be ok)
- [x] Spring
- [x] Damper, Friction, Inertia
Condition effects required more changes than I expected, there are 4 new fields and 4 new messages that I added to the documentation and implemented in the driver.
Summary of what Windows and what this PR uses:
| Windows | This PR | |
|---|---|---|
| Coefficient | [-32767; 32764] |
[-32767; 32767] |
| Deadband | [-32767; 32767] |
[-32767; 32767] |
| Saturation (damper) | [3; 32764] |
[0; 32764] |
| Saturation (spring) | [0; 27302] |
[0; 27302] |
Nothing outstanding come in my mind about the changes, if something is not clear I'll clarify it.
Spreadsheet: ffb.zip (had to be compressed to fit) Windows test program: ffbtest_win.cpp.txt Linux test program: ffbtest_linux.cpp.txt
thanks for your work, this looks great. I checked charts and since there are some differences and between win, linux and wine I am wondering if those are before your changes were applied? and those corr values are values with your changes? since corr values are missing in most charts I am wondering what else is missing. thank you.
I checked charts and since there are some differences and between win, linux and wine I am wondering if those are before your changes were applied?
On the charts, the X axis (horizontal) shows the OS specific API input value and the values on the Y axis (vertical) shows the value that was sent to the wheel. The X and Y values should match between Windows and Wine if the driver is working correctly, while the Linux values should match only on the Y axis exactly and be proportional on the X axis.
and those corr values are values with your changes?
Yes, the corr (corrected) columns and lines are generated using this PR applied.
since corr values are missing in most charts I am wondering what else is missing.
I'm not aware of any missing values, I think you just need to scroll down a fair bit to see them. There are about 65000 rows on each sheet.
Thanks for explanation. Will check that with a wheel some time 😀
Kind of interesting that the FEdit program I used to do most of the reverse engineering never even touched the saturation.
I had a cursory glance at code, looks pretty good to me. I'll play around with it over the weekend, test out some games and have a closer look at the details before merging. In any case, really nice work, well done.
Kind of interesting that the FEdit program I used to do most of the reverse engineering never even touched the saturation.
I had tried FEdit, but I was unable to get it to work reliably under my Win 10 vm, so I have no experience with it. Maybe they forgot to implement it. The saturation fields of the DICONDITION structure of dinput was unchanged from at least DirectX 5.
I think I found something (apart from a missing title). There are 33 possible update packets. The type of the update is coded in the 4th and 5th bytes as bit fields.
Do you want to have them all implemented? For me, it seems to be little benefit compared to the implementation complexity if you are not aware of any issue that caused by that these are not implemented. This PR in the current state already reduces the worst case of 5 update packets to 3 (0e 43 and 0e 70 are implemented).
Here is the table as context (same in the docs)
4th byte 5th byte hex
00001110 01000001 0e 41 positive coefficient
00001110 01000010 0e 42 negative coefficient
00001110 01000011 0e 43 both coefficint
00001110 01001100 0e 4c right and left deadband
00001110 01010000 0e 50 positive saturation
00001110 01100000 0e 60 negative saturation
00001110 01110000 0e 70 both saturation
00001110 01001111 0e 4f coefficient + deadband
00001110 01111100 0e 7c deadband + saturation
00001110 01110011 0e 73 coefficient + saturation
00001110 01101101 0e 6d positive coefficient + deadband + negative saturation
00001100 0c everything
The original reason why I started to write this comment, to document here that these type of packets can be discovered by updating multiple values with one call of SetParameters. The other way is to issue these calls in short succession because the Windows driver merges updates that are happened in a short timeframe.
Came up with a simpler implementation than I thought before. Made it as a separate commit to be easily removable if it is not desired.
It can be made even simpler if we only use the 0x0e message type, which results in losing the equivalent behavior with the Windows driver and adds an extra byte to the message. (0x0e can include 6 values, not just 5)
It can be made even simpler if we only use the 0x0e message type, which results in losing the equivalent behavior with the Windows driver and adds an extra byte to the message. (0x0e can include 6 values, not just 5)
Would it be easier to just dump all parameters in a single message if one/multiple of them has changed? I don't necessarily think we have any massive bandwidth limitations, but sending fewer packets with more data is probably preferable over sending multiple smaller packets, and minimizing branches tends to make code easier to read. I don't think the wheel itself has significant issues dealing with a bit of 'unnecessary' data in the packets, but that should of course be tested.
When I started this project, before I had done any proper USB reverse engineering, I was sure that update packets would just dump the new effect state as a whole because that just seemed like the sensible thing to do, and was sorely disappointed when the tools I was using seemed to just send a single parameter per packet. Looking at the data with new eyes, it seems like at least the periodic effect also uses a bitfield, and might also benefit from merging updates.
The order in which fields are populated is a bit weird, I would've expected the packet to have fixed slots for each parameter, but apparently they act as a kind of priority stack?
Would it be easier to just dump all parameters in a single message if one/multiple of them has changed?
That's a good idea! I have just checked if this has any side effects like stutter or something on the not changed parameters, but I don't feel any, so it's probably safe to do that.
Looking at the data with new eyes, it seems like at least the periodic effect also uses a bitfield, and might also benefit from merging updates.
Yes, there is! It's 0e 0f. It's going to be a separate PR to don't mix them.
The order in which fields are populated is a bit weird, I would've expected the packet to have fixed slots for each parameter, but apparently they act as a kind of priority stack?
Yes, the order is fixed, the location in the packet is not. Probably easier for them to parse that way.
I saw some packets that merged duration update with the other parameters, I will check it tomorrow.
I'm currently wondering about why does the duration updated with every update request, even it is unchanged.
I saw https://github.com/Kimplul/hid-tmff2/commit/0e47624ebbed48e922fe27170364aebf79217e7b that led me to #43. There is a comment from berarma that states:
Updating an effect with the same duration is still an update and it should make the effect last longer since the length counter is reset again to its initial value.
I don't know what is expected under Linux or how other wheels behave, but SDL does not do any special calculations neither on Windows [1] nor Linux [2] while handling uploads [3,4] or updates [5,6].
The documentation for Direct Input states:
If the effect is playing while the parameters are changed, the new parameters take effect as if they were the parameters when the effect started.
For example, suppose a periodic effect with a duration of three seconds is started. After two seconds, the direction of the effect is changed. The effect then continues for one additional second in the new direction. The envelope, phase, amplitude, and other parameters of the effect continue smoothly, as if the direction had not changed.
In the same situation, if after two seconds the duration of the effect were changed to 1.5 seconds, the effect would stop.
The wheel's behavior matches the Direct Input documentation: when a 10-second effect is uploaded and then updated to a 2-second effect after 5 seconds, it stops immediately instead of playing for an additional 2 seconds.
What is your opinion about this? Can we include the duration update in the new 4c update packet together with the other values, or you want to keep the current behavior?
[1]: windows conversion: https://github.com/libsdl-org/SDL/blob/main/src/haptic/windows/SDL_dinputhaptic.c#L656 [2]: linux conversion: https://github.com/libsdl-org/SDL/blob/main/src/haptic/linux/SDL_syshaptic.c#L744 [3]: windows upload: https://github.com/libsdl-org/SDL/blob/main/src/haptic/windows/SDL_dinputhaptic.c#L933 [4]: linux upload: https://github.com/libsdl-org/SDL/blob/main/src/haptic/linux/SDL_syshaptic.c#L935 [5]: windows update: https://github.com/libsdl-org/SDL/blob/main/src/haptic/windows/SDL_dinputhaptic.c#L961 [6]: linux update: https://github.com/libsdl-org/SDL/blob/main/src/haptic/linux/SDL_syshaptic.c#L968
I'm currently wondering about why does the duration updated with every update request, even it is unchanged
Heh, now I'm a bit embarrassed. I thought that re-sending the duration would reset the length counter, but I just tried it out again and you're right, the effect stops once the internal length counter reaches the specified duration. I thought I tested it, but I must've done a really poor job of it. But the idea was to extend the effect duration for each update.
What is your opinion about this? Can we include the duration update in the new 4c update packet together with the other values, or you want to keep the current behavior?
If Logitech wheels extend the duration, I suppose we should too. Just call it a de-facto standard ;)
I'm still a bit annoyed how poorly documented this aspect of the Linux FFB subsystem is, I could easily see both approaches being the more 'intuitive' option.
In addition to the SDL sources you linked, Wine doesn't seem to do any adjustments to the duration, possibly indicating that Wine assumes that the DInput duration handling would also apply to Linux: https://gitlab.winehq.org/wine/wine/-/blob/master/dlls/dinput/joystick_hid.c?ref_type=heads#L2482 (kind of hard to link to code that doesn't exists, but you know)
~~On the other hand, the driver for memoryless wheels seems to treat an upload as a duration extension (as do Logitech wheels, don't know if there's some ancestry there): https://elixir.bootlin.com/linux/latest/source/drivers/input/ff-memless.c#L462~~
~~The memoryless driver could arguably be seen as the source of truth for how wheels should behave on Linux, as the driver from what I can tell is the earliest FFB anything in the kernel. Which would mean that SDL and Wine are buggy, I guess?~~
Scratch that, I got them reversed, hid-lg(ff/whatever).c is older than ff-memless.c so the Logitech wheels treating an update as a duration extension is the 'original' behaviour, as far as I can tell. Assuming we treat it as the One True Behaviour on Linux, SDL and Wine are still buggy I guess?
It might not even be that significant, I can't remember seeing any games that would require one or the other, generally most games just set effects to be of infinite length and just send a STOP signal when they should end.
I have taken a look at some projects to see what they are doing. The Fanatec driver works with the same logic as Windows does, it doesn't extend the duration. Stop time is calculated from the start time, which is set only at the start of the effect.
To make the things more interesting, the in kernel hid-pidff doesn't even allow sending an update with the same duration parameter. Depending on the interpretation, the effect duration cannot be doubled, or it expects the Windows way of doing it.
The OpenFFBoard project using this driver (based on this comment) and they are doing it in the Windows way. Duration is updated directly without any calculations and the start time is only updated at the start of the effect.
(I hope I read them correctly)
Out of curiosity, do you know any driver that does the ff-memless way of the duration update and does not depend on ff-memless?
I'm still a bit annoyed how poorly documented this aspect of the Linux FFB subsystem is
I feel it :)
I'm in favor of keeping the current (Windows) behavior that maybe incorrect, but it does not break SDL, Wine, and probably more programs that expect the Windows behavior.
Whatever is the decision is going to be, I want to push a commit to use the 4c packet for the duration updates too before this gets merged.
Out of curiosity, do you know any driver that does the ff-memless way of the duration update and does not depend on ff-memless?
Logitech wheels are the only ones that I'm sure of.
I'm in favor of keeping the current (Windows) behavior that maybe incorrect, but it does not break SDL, Wine, and probably more programs that expect the Windows behavior.
I'm fine with that.
@berarma Sorry to ping you this late into a thread but TL;DR: It seems like most projects assume DirectInput semantics w.r.t. effect duration updating, meaning that an effect update doesn't reset the length counter. Any thought on this?
@berarma Sorry to ping you this late into a thread but TL;DR: It seems like most projects assume DirectInput semantics w.r.t. effect duration updating, meaning that an effect update doesn't reset the length counter. Any thought on this?
Don't worry. This is an interesting subject that we should talk about now that there's an increasing number of people collaborating on wheel drivers.
I've already had a similar talk where someone patching a driver wants to fix any Wine/Proton/SDL/game bug in the driver, because since they're already modifying the driver, it's easier than having to create a fix for Wine/Proton/SDL.
The problem with this is that we're fragmenting the Linux FFB API and we won't ever have a stable API if we work this way. And the drivers will become increasingly complex, having to deal with incompatible one-game fixes which might break other games.
Trying to follow the Windows API in the Linux drivers has the same problems, I think there has to be a clear separation, and Wine/Proton is the compatibility layer. Any issue with Windows API to Linux API translations should be addressed in those projects.
Since the Linux FFB API documentation is scarce, we must rely on the source code and previous drivers implementations. The most important one being ff-memless because it's probably the most used in practice, and I think the only one in use for wheels until recently. Native games and even Wine/Proton have been tested against it.
In addition, I think the semantics of the Linux API are clear. There's no update operation for effects, only upload and play. When we upload an effect while it's playing, we're not only updating some parameters, we're uploading a new effect that will replace the one that is playing without having to stop and play again. In this regard, I think I didn't follow correctly these semantics in my new-lg4ff driver because they weren't clear to me at the time, but the effect length is updated.
If we don't think about this carefully, we might end up with incompatible and over-complicated drivers. Applications will need to know which driver is in use to send the correct commands, breaking the separation of concerns of what a driver should do (handle the hardware) and what a user space application can expect (devices following the same conventions independently of the driver in use).
I have searched the lkml in hope of some information.
The earliest mention of ff-memless is in the "Input patches for 2.6.18" on 2006-10-02 (lkml) though the commit is dated 2006-07-19. It was developed by Anssi Hannula. I couldn't find any previous discussions about it. Maybe it was input-ff/ff-effects previously? If yes, then it is part of "input: force feedback updates, third time" (lkml) patch set from 2006-05-30, but there are a lot of differences between them.
hid-pidff is part of the "input: force feedback updates, third time" (lkml) patch set made on 2006-05-30 by Anssi Hannula. This was developed very close to ff-memless or in the same patch set. The notable difference is that hid-pidff does not allow updates with the same duration, effectively the duration cannot be doubled. This suggests the Windows/DirectInput mindset. (details in my previous comment)
An interesting find is the proposed ff-memless-next from Michal Malý in many forms between 2013-12 and 2014-05. The initial variants were not merged because of code duplication concerns (RFC, RFC v2, RFC v3, PATCH, PATCH v2) the second variants that are targeted to replace ff-memless not merged either, but I don't know the exact reason (PATCH, PATCH v2, PATCH v3, PATCH v4).
The summary of the patch set states:
All effects are handled in accordance with Microsoft's DirectInput/XInput.
however, the effect update is still using the ff-memless method of extending/restarting the whole effect.
In PATCH v2 from the initial variants (lkml) the effect updating/restarting was discussed, but the only guideline that Anssi can point to was the already known ff wiki article.
There was a discussion on 2014-02-15 with the subject of "Questions about the documentation/specification of Linux ForceFeedback input.h" (lkml) Unfortunately the duration update was not brought up, only the infinite duration. Through the whole discussion, the DirectInput is used as a reference point.
I have searched the lkml in hope of some information.
The earliest mention of
ff-memlessis in the "Input patches for 2.6.18" on 2006-10-02 (lkml) though the commit is dated 2006-07-19. It was developed by Anssi Hannula. I couldn't find any previous discussions about it. Maybe it wasinput-ff/ff-effectspreviously? If yes, then it is part of "input: force feedback updates, third time" (lkml) patch set from 2006-05-30, but there are a lot of differences between them.
hid-pidffis part of the "input: force feedback updates, third time" (lkml) patch set made on 2006-05-30 by Anssi Hannula. This was developed very close toff-memlessor in the same patch set. The notable difference is thathid-pidffdoes not allow updates with the same duration, effectively the duration cannot be doubled. This suggests the Windows/DirectInput mindset. (details in my previous comment)An interesting find is the proposed
ff-memless-nextfrom Michal Malý in many forms between 2013-12 and 2014-05. The initial variants were not merged because of code duplication concerns (RFC, RFC v2, RFC v3, PATCH, PATCH v2) the second variants that are targeted to replaceff-memlessnot merged either, but I don't know the exact reason (PATCH, PATCH v2, PATCH v3, PATCH v4). The summary of the patch set states:All effects are handled in accordance with Microsoft's DirectInput/XInput.
however, the effect update is still using the
ff-memlessmethod of extending/restarting the whole effect. In PATCH v2 from the initial variants (lkml) the effect updating/restarting was discussed, but the only guideline that Anssi can point to was the already known ff wiki article.There was a discussion on 2014-02-15 with the subject of "Questions about the documentation/specification of Linux ForceFeedback input.h" (lkml) Unfortunately the duration update was not brought up, only the infinite duration. Through the whole discussion, the DirectInput is used as a reference point.
I'd take a pragmatic approach of breaking the least software, or even not breaking any if possible. I wouldn't mind much who did what first if it wasn't ever significantly used. ff-memless is used by many FFB devices and changing its API would break them.
In this case, since the drivers which aren't following standard practice where hardly used (they weren't fully working even), like hid-pidff, I think we can easily avoid breaking what was already working. With new drivers like hid-tmff2 is even easier to avoid breakage.
ff-memless-next is out of the question since it wasn't merged and I don't think it will for the same reasons I pointed above. I feel like we're discussing if we should all go our own separate ways changing the API as we please. I hope this is finally not the case because it would certainly make our lives harder, and merging into the kernel would be a no-no.
From the LKML thread at https://lore.kernel.org/all/[email protected]/
As you've seen, the effect model should very closely match the DInput model (as that is what the devices tend to support), mostly just the units are different (our units use the 16-bit range as a whole, i.e. there are no out-of-range values).
Vague language of course, but it kind of seems like the Linux FFB is intended to match DInput, and a programming bug has just spread to become the most common behaviour (due to how many devices use ff-memless). Possibly a case of an implementation becoming the standard, but definitely lacking documentation.
I'm not opposed to making this driver behave as ff-memless, as long as we document it as the expected behaviour under Linux, and preferably send out patches to make sure the biggest users of FFB (SDL, Wine) are aware of this difference between Windows and Linux. As previously stated, I'm not aware of any programs or projects that rely on one behaviour or the other (please feel free to show me counter-examples!) so this is probably not a particularly critical detail to get right, but it'd be nice if everyone was on the same page at least.
From the LKML thread at https://lore.kernel.org/all/[email protected]/\n\nAs you've seen, the effect model should very closely match the DInput\nmodel (as that is what the devices tend to support), mostly just the\nunits are different (our units use the 16-bit range as a whole, i.e.\nthere are no out-of-range values).
He was talking about the effect model, which is pretty close already, while this discussion was about semantics, or so I understand. I just want everyone to understand the important implications of going incompatible with other drivers.
If this change isn't important, then we're just discussing this because we should be compatible with the Windows API. What's the reason when there's already a compatibility layer that takes care of that? MS might change or deprecate this interface for a new one any day. Should we be following their mood? I don't think making our drivers dependant on what MS does with Windows is good. While it might have been a good starting point and an inspiration, following what they do shouldn't be our end goal.
He was talking about the effect model, which is pretty close already, while this discussion was about semantics, or so I understand.
I thought a model defines the semantics, but I guess we must have different definitions?
If this change isn't important, then we're just discussing this because we should be compatible with the Windows API.
Sorry, must've expressed myself poorly. I don't mean that we should automatically follow what Windows does, the confusion here (on my part at least) lies in the fact that Wine and SDL, which I imagine together account for the vast majority of all FFB use, seem to assume that the duration parameter works the same on Linux and Windows. At least to me it's not obvious if the issue is drivers implemeting some (poorly defined) API incorrectly or users using it wrong but I'm fine with just picking one and documenting the choice clearly so other people don't have to worry avout this particular problem ever again, and right now I'm leaning towards extending durations with updates. Does that seem reasonable?
For me, the effect model is the structs, the data. I think this is common terminology.
Extending durations seems reasonable because we aren't breaking compatibility with existing drivers. I hope I'm making it clear it's not just my preference.
If Wine isn't translating correctly or SDL isn't working properly, it will already be an issue for existing drivers that will have to be fixed at some point.
If there never was a case for this use, maybe they are unaware of the issue. But it's no less important to keep consistency between drivers.
Just to make sure everyone is on the same page. I may have described the situation inaccurately earlier, sorry for that. What we are speaking about is not just one parameter of the effect, it's all the parameters that changes with time like delay, attack length/force, fade length/force, ramp start/end level, phase, saturation and coefficient.
If we are going the ff-memless way, then I think it would be good to make sure that at least the larger projects (like Wine/Proton and SDL) and other wheel drivers are going to make changes / accept patches to adapt these changes. There is no point in being correct if no one wants to do it that way. But even before everything, a small scale test can be done to see if we can achieve satisfactory results.
There is no point in being correct if no one wants to do it that way. But even before everything, a small scale test can be done to see if we can achieve satisfactory results.
What do you mean? All ff-memless devices are doing it this way, this is different to "no one wants to do it that way". Projects like Wine, Proton or SDL will do whatever works with the kernel drivers, and right now the only drivers in the kernel which are actually in use are ff-memless. If there are others it would be interesting to know.
If this case isn't currently addressed correctly, the most logic reason could be that it rarely happens, making it even less convenient to make breaking changes in the kernel. As previously said, the most common case is setting the length infinite in the initial and later effect uploads.
You could start a discussion involving Wine, Proton, SDL and kernel developers to change how ff-memless works, but I guess it'll be much more difficult than fixing Wine/Proton/SDL to work correctly with the current state. Wheel/FFB discussions usually get stalled because there's little interest, specially when they involve changing kernel drivers.
Also, to start such a discussion there should be a rationale to break backwards compatibility for many FFB devices. Maybe I've missed some message but I think I haven't read the reasons yet.
What we are speaking about is not just one parameter of the effect, it's all the parameters that changes with time like delay, attack length/force, fade length/force, ramp start/end level, phase, saturation and coefficient.
Probably good to bring those up as well, sure.
Seems like ff-memless & co. pretty much just treat an upload as a restart, including waiting delays and running the envelopes again. From what I can tell from the source code, delays and envelope effects are calculated based on the play_at member, which is set to the upload time + effect delay on an upload: https://elixir.bootlin.com/linux/latest/source/drivers/input/ff-memless.c#L472
I don't think I have any ff-memless devices so I can't really confirm, unfortunately. The T300 definitely doesn't do this on an upload so in our case that would mean that each effect update would have to finish with sending a PLAY packet to fully restart the effect. This also effectively means that emulating DInput is really quite difficult, for example translating a DInput magnitude change on an effect with a delay would mean having to calculate how much time is left in the delay and subtract that from the existing Linux effect. Same and probably more annoying with the envelopes.
The FFB docs say 'Normally, the effect is not stopped and restarted' which I guess should be read as 'Normally, the effect is not stupped, and is restarted' assuming ff-memless has the correct interpretation of it, though I don't entirely understand how a stop + start is different from a restart.
Alternatively, the quote could've been paraphrased from DInput, which states
Normally, if the driver cannot update the parameters of a playing effect, the driver is permitted to stop the effect, update the parameters, and then restart the effect.
This makes more sense to me, restarting the effect every time it is updated is a bit silly if the wheel itself supports partial updates.
right now the only drivers in the kernel which are actually in use are ff-memless. If there are others it would be interesting to know.
There's at least the Logitech HID++, which seems to just dump the effect buffer as a whole to the wheel during an update: https://elixir.bootlin.com/linux/latest/source/drivers/hid/hid-logitech-hidpp.c#L2543
I don't know how the wheel's firmware itself reacts to the delays, but could be cool to find someone with a G923 and ask.
What do you mean? All ff-memless devices are doing it this way, this is different to "no one wants to do it that way". Projects like Wine, Proton or SDL will do whatever works with the kernel drivers, ...
I mean that even if one of Wine, Proton, or SDL is opposed to adapt the ff-memless way of doing updates (I have no information about any of them) it's worse than having the current state. Currently, as far as my research gone, the userspace is mostly (*) uniformly ignoring the ff-memless behavior and expects DirectInput-style updates.
*: The only project I found that clearly aware of ff-memless is "qt-mobility-haptics-ffmemless" code. Maybe there are others that are closed source or not on github.
... right now the only drivers in the kernel which are actually in use are ff-memless. If there are others it would be interesting to know.
There is hid-pidff that is in the kernel that is the USB PID standard implementation. I know that OpenFFBoard is using it, but I can imagine there are more devices because of its generic nature. (I know that I brought this up a few times already, and I know that you reacted to it)
This also effectively means that emulating DInput is really quite difficult, for example translating a DInput magnitude change on an effect with a delay would mean having to calculate how much time is left in the delay and subtract that from the existing Linux effect. Same and probably more annoying with the envelopes.
In case of this driver (hid-tmff2), it would mean that a program (something 3rd party, game, whatever) has to keep track of time and recalculate every effect parameter based on the elapsed time on every update then send it to this driver where it is converted back to DInput-like values (also keeping track of time) to be consumable by the wheel, effectively undoing what every program has to do to be compatible with ff-memless.
There's at least the Logitech HID++, which seems to just dump the effect buffer as a whole to the wheel during an update: https://elixir.bootlin.com/linux/latest/source/drivers/hid/hid-logitech-hidpp.c#L2543
I don't know how the wheel's firmware itself reacts to the delays, but could be cool to find someone with a G923 and ask.
I don't own the wheel but found the documentation directly from Logitech: https://opensource.logitech.com/wiki/force_feedback/Logitech_Force_Feedback_Protocol_V1.6.pdf
In the 4.1.3 x8123.2 downloadEffect section says
The downloadEffect method is used to create and update force effects on the device. The first byte decides if the effect is to be allocated fresh (EFFECT_ID == 0), or if an existing effect should be modified (0 < EFFECT_ID <= SLOT_COUNT). The first byte of the response contains either 0x00 if there was an error, or the EFFECT_ID.
In hid-logitech-hidpp the START and AXIS seems to be always set to 0 and EFFECT_ID is 0 if new effect is uploaded otherwise not, so updates are used, but the doc is not clear about how an update is handled.
Yes, if someone can try this out on real hardware, that would be the best.
So the reason is that it's easier to just let the firmware do whatever it does. Thus you'd really want the API to be open about it and not force any particular behavior. It just happens that this wheel behaves more closely to DInput for this case.
If I didn't convince you then go ahead and take the easy path. I don't think an excuse is needed. It won't have consequences in the short term as far as we know. Given the state of neglect of this area of the kernel, I guess it will become a mess anyway,
So the reason is that it's easier to just let the firmware do whatever it does. Thus you'd really want the API to be open about it and not force any particular behavior. It just happens that this wheel behaves more closely to DInput for this case.
I didn't mean that, I only said how it is going to look like in hid-tmff2 (in case if you are replying to the paragraph that starts with "In case of this driver ...").
However, I think hardware implementations should be considered when an API is made or changed. We don't control the manufacturers that are creating these devices and what they are going to do is what is easier to them undoubtedly the Windows market share is larger than Linux (considering gaming) and the manufacturers are designing their hardware to work with the Windows API.
So the reason is that it's easier to just let the firmware do whatever it does.
Well, that's how the first FFB driver born for I-Force wheels way before ff-memless, this is the first driver that introduced the term "update". I-Force has separate slots for the "core" effect and the "modifiers", changing the "core" requires a restart but the "modifiers" can be changed on the fly. I can't be sure about the exact behavior, but why would there be slots if they are restarting the whole effect? I think it works DInput-like. Also, it was not changed to ff-memless like other devices when ff-memless was introduced, and it's almost untouched since then over here: https://elixir.bootlin.com/linux/v6.9.8/source/drivers/input/joystick/iforce When I-Force got its update capability: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/drivers/input?id=07b8fb25a96707cdd01072fd6298c2b8570801be
After the I-Force got its update capability, the "Dynamic update of an effect" section changed from
his consists in changing some parameters of an effect while it's playing. The driver currently does not support that. You still have the brute-force method, which consists in erasing the effect and uploading the updated version. It actually works pretty well. You don't need to stop-and-start the effect.
to
Proceed as if you wanted to upload a new effect, except that instead of setting the id field to -1, you set it to the wanted effect id. Normally, the effect is not stopped and restarted. However, depending on the type of device, not all paramaters can be dynamically updated. For example, the direction of an effect cannot be updated with iforce devices. In this case, the driver stops the effect, up-load it, and restart it.
Src: https://git.kernel.org/pub/scm/linux/kernel/git/tglx/history.git/commit/Documentation/input/ff.txt?id=3dfd8b7d5c3fa66bccbbb9fa4d917df7f5dca751
Some observations of mine:
The difference between ff-memless and others are only visible when an effect is played with an envelope, delay or when the duration of the effect is important. Otherwise, the difference is invisible, I think.
Most of the devices that are depends on ff-memless are gamepads with some rumble motors, and low-level chips that are lacking the concept of an "update". Gamepads are incapable of playing force feedback effect with precision because they are not designed for that, hence harder to notice something that is already rare.
You asked previously:
right now the only drivers in the kernel which are actually in use are ff-memless. If there are others it would be interesting to know.
It depends on how you define "actually in use" but here is what I found. I only include wheels that are probably capable of more than rumble.
Depends on ff-memless
-
hid-lg4ff, in tree- Logitech wheels
-
hid-emsff, in tree- EMS Trio Linker Plus II
Works like ff-memless
new-lg4ff, out of tree- Logitech wheels
Likely DInput-like
hid-logutech-hidpp, in tree- Newer Logitech wheels
DInput-like
-
iforce, in tree- Immersion I-Force
-
hid-fftec, out of tree- Fanatec
-
hid-tmff2, out of tree- Thrustmaster (excluding T150)
-
hid-pidff, in tree -
hid-simagic-ff, out of tree,hid-pidffcopy- Sigmagic
-
hid-moza-ff, out of tree,hid-pidffcopy- Moza
Based on these, I think we can't speak about some defined behavior on the kernel side either, so if we want something that's working the same for every device, then a change in the kernel is unavoidable.
At this point, I see 3 possible decision for this discussion. We are not under pressure to do something, so I think all the 3 are equally valid.
Leaving everything as-is
- Required changes: None
- Exactly defined kernel and userspace behavior: No
Adapt to ff-memless
- Required changes: Probably most of the already existing applications that are using this API directly. What I found is mostly translation layers like SDL, Wine and OIS.
- Required changes: In and out of tree kernel drivers
- Exactly defined kernel and userspace behavior: Yes, ff-memless
Adapt to DInput
- Required changes: I'm not aware of too many applications, only "qt-mobility-haptics-ffmemless". There are more probably
- Required changes: In and out of tree kernel drivers
- Exactly defined kernel and userspace behavior: Yes, DInput (like)
In both cases, when something is changed, closed-source applications should be considered for their current behavior because they cannot be changed easily. We can think that they are going to adapt, but when?
Judging from a previous attempt to change one line of hid-pidff, we are probably not going to go too far with these kernel changes. I think the "Leaving everything as-is" is the only achievable option, but if any of you know how to make any of the other options happen, I open to them probably both of you know more about kernel stuff than me, I'm only digging in the kernel for the last 7 days.
Right, I think we've let this stew for long enough. I at least haven't come up with anything and agree that probably the best course of action for now is to let things be the way they are. My opinion's been kind of flip-flopping over the course of this discussion, but I believe we uncovered some interesting aspects that I've never considered before and I'm happy it was brought up. It can always be resurrected if someone finds a real usecase that is affected.
@MmAaXx500 Thanks for a sober and really well put together overview of the topic, again great work. Do you think you have everything you need to get this PR over the finish line?