Driving Simulator for Vehicle Research

As you know Mika I did some testing with the SC1 on Linux, since we already talked about that some time ago. I recently did some more tests and some also include things you mentioned here, so let me give you a few more hints:

  1. One problem – as you mentioned – can be that there are two interfaces for joysticks in the kernel, the jsdev and the evdev one. Actually it can be problematic in games if both are present, since joysticks may be detected twice (depending on how the software is programmed, I guess). Everything I tested so far (couple of sims) does work without the jsdev interface, so I’d just suggest to block it completely, which is done via an entry in a file like /etc/modprobe.d/blacklist-joydev.conf (actually the name does not matter here, just has to end with .conf):
    blacklist joydev

  2. There is test software for evdev, e.g. evtest-qt:
    https://gitlab.com/evtest-qt/evtest-qt
    It’s also a very good idea to install linuxconsole, since that contains some other tools useful for the evdev interface (i.e. evdev-joystick, which is required in the next step)

  3. For some reason Linux – by default – applies some really idiotic deadzone, which is most likely what you mean with “was not able to calibrate the joystick properly”. This can be checked with evdev-joystick. First, you need to get the path for the device, e.g. by:
    evdev-joystick --l
    which lists all evdev joystick devices. There you’ll notice that deadzone and fuzz are both non-0, which is the cause of the problem here. Hence set these to zero:
    evdev-joystick --evdev /some/path/output/by/the/previous/command --axis 0 --deadzone 0

Now this won’t be permanent, meaning that it’ll reset when rebooting. To get this as a permanent setting, most people would suggest to set up a shell script run by udev, most likely because they just don’t know any better.
Actually the better idea is to enter this in the hwdb provided by udev (or rather systemd which contains udev these days). btw. as a manufacturer you could possibly submit a patch for systemd to include appropriate data for your device to get rid of this necessity for all GD-based wheels, if you feel it is a correct setting. Anyway, the entry here would go into a file in /etc/udev/hwdb.d, e.g.
/etc/udev/hwdb.d/61-evdev-local.hwdb:

    evdev:name:*Simucube*:*
    EVDEV_ABS_00=:::0:0

*Simucube* matches the name for the device as reported by evdev during driver loading (see dmesg output). I think it might also be possible to match the device IDs, but I haven’t been able to get that working so far and unfortunately I couldn’t find really helpful docs on hwdb telling me how to do it correctly.
The entry after the = means MIN:MAX:STEP:FUZZ:DEADZONE where you can leave out the settings that you do not want to adjust (meaning I set FUZZ and DEADZONE here).
ABS_00 is the absolute axis 0, axis 1 would be ABS_01 etc. Since I don’t use the additional connectors, for me it’s sufficient to only set axis 0, but for others (e.g. if connecting pedals) setting fuzz and deadzone for additional axis (1 or above) may be necessary as well.
Using this it is also possible to set min and max, in case you want to calibrate pedals for a specific range (like I do for my Heusinkveld pedals where the physical range does not match the full range of the controller).

While it will most likely update on its own at some point, it’s a good idea to update the hwdb manually to ensure the entry is included:

systemd-hwdb update

After that at least my Simucube behaves like it should. Well, apart from the next one.

  1. FFB does not work (yet). The patch for that isn’t really massive, there is only one field you need to patch out and that is 0xa7 (PID_START_DELAY):
diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c                                                                                                                                                                                                   
index fddac7c72f64..8684808c5967 100644                                                                                                                                                                                                                                        
--- a/drivers/hid/usbhid/hid-pidff.c                                                                                                                                                                                                                                           
+++ b/drivers/hid/usbhid/hid-pidff.c                                                                                                                                                                                                                                           
@@ -57,9 +57,8 @@ the only field in that report */                                                                                                                                                                                                                             
#define PID_TRIGGER_BUTTON     3
#define PID_TRIGGER_REPEAT_INT 4
#define PID_DIRECTION_ENABLE   5
-#define PID_START_DELAY                6                                                                                                                                                                                                                                      
static const u8 pidff_set_effect[] = {
-       0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7                                                                                                                                                                                                                               
+       0x22, 0x50, 0x52, 0x53, 0x54, 0x56                                                                                                                                                                                                                                     
};

#define PID_ATTACK_LEVEL       1
@@ -311,7 +310,6 @@ static void pidff_set_effect_report(struct pidff_device *pidff,                                                                                                                                                                                            
pidff->effect_direction->value[0] =
pidff_rescale(effect->direction, 0xffff,
pidff->effect_direction);
-       pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay;                                                                                                                                                                                                    

hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT],
HID_REQ_SET_REPORT);
@@ -326,8 +324,7 @@ static int pidff_needs_set_effect(struct ff_effect *effect,                                                                                                                                                                                                
return effect->replay.length != old->replay.length ||
effect->trigger.interval != old->trigger.interval ||
effect->trigger.button != old->trigger.button ||
-              effect->direction != old->direction ||                                                                                                                                                                                                                          
-              effect->replay.delay != old->replay.delay;                                                                                                                                                                                                                      
+              effect->direction != old->direction;                                                                                                                                                                                                                            
}

/*
@@ -736,7 +733,6 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)                                                                                                                                                                                    
pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0;
pidff_set(&pidff->set_effect[PID_GAIN], magnitude);
pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
-       pidff->set_effect[PID_START_DELAY].value[0] = 0;                                                                                                                                                                                                                       

hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT],
HID_REQ_SET_REPORT);

After that the FFB will work. Didn’t test it with a lot of games so far, since I use Linux mainly for flying rather than driving. But wanted to test out ATS and ETS2 in the near future, since for those there exists an FFB plugin for Linux, which is supposed to have much improved FFB compared to the shitty built-in one.
Also my motivation thus far was low because Assetto Corsa via Steam/proton was a big pain, but this seems to get better slowly, so maybe I’ll have a look at that as well. (AC still is the one racing sim I use the most.)

2 Likes

I should also add that there is FFBChecker to test the ffb and it works with evdev, once you’ve patched and recompiled the Linux kernel:

That way I have verified that the SC1 with the patch above applied does work.

Thanks for these links, much appreciated.

We might consider doing a completely unsupported test release of the sc1 software for Linux, but lets see what happens…

The missing control software would be the smallest issue for me. For that I could just use a VM to set up the wheel like I want it to have, but usually I don’t even need that, since over 90% of the time I just drive with my default profile (which is automatically loaded upon device startup).

I think the major obstacle to get out of the way is that with the kernel not recognizing the ffb report layout, since most people will not be willing to recompile the kernel just to get the wheel working.
Either you can do something for that from your side (didn’t seem like it as far as I understood), or we have to setup an exception for the Simucube in the Linux kernel (which is out of my league of programming unfortunately) or we have to convince the Linux kernel people to accept the patch above (argument for that would be “That’s what Windows seems to do anyway.”.

With ACC installed/run via Steam/proton I was able to verify that the SC and the FFB works just fine (same as on Windows) when running a game via wine/proton and with the above patch applied to the kernel.

Actually ACC runs pretty fine with more or less identical performance. I might actually uninstall this one on Windows now once the SC runs on Linux without patching.

Mika, do you have a link to some doc regarding the behaviour of Windows regarding the 0xa7 field? If so, maybe providing that together with the patch could convince the Linux people to accept some change like that?

We haven’t come up with any documentation on the capabilities and limitations of the Microsoft’s driver, its all by experimentation.

I finally figured out how to match vendor and product ids. File would look like this:

    evdev:input:b0003v16D0p0D5A*
    EVDEV_ABS_00=:::0:0

(no colon before the last wildcard).

1 Like

Hello, can you inform me how is the situation right now with SC on linux?

It doesn’t seem very good.
I think to achieve this would be relatively not so big of an effort, right now seems the base already holds the driver && settings, and the driver paddock windows apps does some kind of authentication only, if we could skip that authentication with some argument I think we would be closer to see what happens next.

Linux detects it when plugged in currently(dmesg), but dunno if it works and how good if it could connect if that ‘authentication’ could be by passed.

Situation has not changed - there are some fields in the usb interface descriptor that Linux expects there to be for the device to be detected correctly, and when enabled, Windows says that the device cannot be used.

There is no ‘autentication’ mechanism at all, however the API to command the device and its settings is not public.

Wine 7, which was released in the beginning of the year, added support for HID PID devices. This allows Wine to control such devices directly, bypassing the Linux FFB driver (along with all other params as deadzone, fuzz, etc.) completely. So for the FFB to work, it needs the hidraw backend in winebus.sys enabled for the used wineprefix, as well as hidraw user access granted for SC2 on the Linux side.

All sims (rF2, AMS2, ACC, AC, R3E, PC2, BeamNG, etc.) work properly, as well as all kinds of controlling apps (TrueDrive, SimHub, etc.). I’ve been using my SC2 this way for more than a year without any problems. Subjectively the FFB between Windows and Wine feels the same (or very similar).
Objectively I did some tests incl. FFB rate measuring, comparison of HID reports (whether the values are similar and in range, the sequence of reports, etc.), and didn’t find any issues.

As I had some experience with the HID PID specification, and really like my SC2 :+1:, so I pushed some patches to upstream Wine to help them in the implementation, and to iron out some bugs. Tested everything on the SC2, as they didn’t have any other PID FFB devices, so we may say that the PID implementation in Wine is developed with SC2 in mind :smile:.

The hidraw backend in your wineprefix can be enabled through the registry value “Enable SDL” in [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\winebus], which must be added with right click->New->DWORD value, and set to “0” (turn off SDL, so devices can be enumerated by the hidraw backend).
To start regedit:

WINEPREFIX=“path to your wineprefix” wine regedit

The hidraw user access for SC in Linux can be enabled by placing a file “72-simucube.rules” in “/etc/udev/rules.d/” with the following content:

# Simucube 1; USB
KERNEL==“hidraw*”, ATTRS{idVendor}==“16d0”, ATTRS{idProduct}==“0d5a”, MODE=“0660”, TAG+=“uaccess”

# Simucube 2 Sport; USB
KERNEL==“hidraw*”, ATTRS{idVendor}==“16d0”, ATTRS{idProduct}==“0d61”, MODE=“0660”, TAG+=“uaccess”

# Simucube 2 Pro; USB
KERNEL==“hidraw*”, ATTRS{idVendor}==“16d0”, ATTRS{idProduct}==“0d60”, MODE=“0660”, TAG+=“uaccess”

# Simucube 2 Ultimate; USB
KERNEL==“hidraw*”, ATTRS{idVendor}==“16d0”, ATTRS{idProduct}==“0d5f”, MODE=“0660”, TAG+=“uaccess”

For Proton 7.0/SteamPlay the things are a little bit more complicated, since they enable selectively the hidraw backend only for some devices, which is hardcoded in the source. I plan to push a patch, which would add the SC devices and some others as Fanatec CS pedals (which would allow apps that use Fanatec SDK to control the pedal buzz function, as R3E, etc.).

On the Linux side for native apps, already @Berniyh posted what is needed. I have some patches that make those missing “Usages” optional. Have been thinking to submit them upstream, but the Linux maintainers are very strict, so it’ll need a lot of time to explain the need of such changes. I’ve fixed also some bugs, which would be easier to push upstream.
BTW, there is no need to compile the whole kernel for this. The code (hid-pidff.c) is in the “usbhid” driver, which can be installed as a dkms driver, and make the process fully automatic. It would allow using the kernels that come with your distribution, etc.
For the hwdb changes (deadzone, fuzz, etc.) that @Berniyh posted, as well as hidraw access, i guess those can be pushed relatively easy to systemd upstream. So they would arrive automatically on the users’ systems, when the various distributions install the next version of systemd.

I noted, when helping the PID development in Wine, that there are some issues with the HID report descriptor, which might be preventing the hid parser in Windows to properly parse those report descriptions. Here is an excerpt from the “Set Effect Report” part:

Usage (21h),
Collection (Logical),
Report ID (1),
Usage (22h),
Logical Minimum (1),
Logical Maximum (40),
Physical Minimum (1),
Physical Maximum (40),
Report Size (8),
Report Count (1),
Output (Variable),
.
.
.
Usage (57h),
Collection (Logical),
Usage (000A0001h),
Usage (000A0002h),
Unit (Degrees),
Unit Exponent (-2),
Logical Minimum (0),
Logical Maximum (180),
Physical Minimum (0),
Physical Maximum (36000),
Unit,
Report Size (8),
Report Count (1),
Output (Variable),
Unit Exponent (0),
Unit,
End Collection,
Usage Page (PID),
Unit (Seconds),
Unit Exponent (-3),
Logical Minimum (0),
Logical Maximum (32767),
Physical Minimum (0),
Physical Maximum (32767),
Report Size (16),
Report Count (1),
Unit,
Unit Exponent (0),
End Collection,

(Sorry for the not properly indented text, but I couldn’t find a way to add spaces or tabs, it always cuts them to one space :slight_smile: )

In the collection “Direction” (usage 57h) there are declared two indexed usages 000A000(1|2)h, which aren’t reflected by the same number report counts with output (var). Till the end of the “Direction” collection there is one report count with output, and till the end of the collection “Set Effect Report”, there is another one without output (I believe this is from your tests with the “Start Delay” usage). So the Windows parser probably gets confused by the lacking report count and tries to add the next report count from the supposedly added “Start Delay” usage, just to find out that the unit is in seconds, while it expects degrees for “000A0002h”, etc.
IMO the solution here is to set the report count inside the collection “Direction” (57h) to 2, which will properly reflect the number of the declared usages in the collection. This will free the Windows parser to parse the next “Start Delay” (0xA7) usage when added.
The Wine test suite, which they run on Windows machines to confirm validity of their work, includes a virtual HID PID driver, which declares “Start Delay”, as well as the other missing usages, without any problems for Windows accepting them and interacting with the driver.

The other missing usages are from the “Set Condition Report” (0x5F) - “Negative Coeficient” (0x62) and “Dead Band” (0x65). The Linux driver currently just disables the condition reports entirely, if it doesn’t find those usages. The “Negative Coeficient” (0x62) can be added after “Positive Coefficient” (0x61) as a second usage with just the same parameters, and only the report count increased to 2. The “Dead Band” can be added at the end.

@Mika, can I contact you through PM? Don’t want to make this post any longer :smile:. I have a specific question about one of the HID reports, and also have found another issue with one of the games, which I believe needs fixing on the Fw side.

2 Likes

Thanks for the informative post! Of course you can PM. I will only be able to react and do tests at our end the week after next week.

Wow, thanks for the detailed investigation and report. :slight_smile:

I remember trying to access the SC1 and also other devices using the new hidraw driver, but I never got this to work and ultimately gave up because the documentation on it was so old and unhelpful.
Guess I should try again.

One question though: shouldn’t hidraw be the default for the newer Wine versions?

The default is still SDL, although all the backends have been completely reworked around HID. Both SDL and evdev build virtual HID report descriptors internally, so they look like native HID devices to the rest of the (pseudo) driver chain - hidclass.sys - and Wine/Windows.
The best thing is the Wine devs choose PID for the FFB implementation. So if the device has FFB axes, those backends (SDL and evdev) expose also PID reports at their virtual HID descriptors. Hidraw just exposes what it sees natively - the whole HID report descriptor, and all input, output and feature reports just going through to/from the Linux hidraw interface.
Then the real HID PID implementation is in dinput.

So it still needs “Enable SDL” disabled, to allow hidraw to enumerate the devices. There is one catch though - if the devices don’t have hidraw user access granted, then the winebus.sys will fallback to the evdev backend, as SDL is already disabled. Evdev is the native Linux interface for such devices, so it should work fine.
I’d recommend granting hidraw user access for all your hid devices - pedals, button boxes, joysticks - and using the hidraw backend, which would allow most of the controlling software for those devices to function properly.

Proton 7.x had hidraw as default backend, but recently they choose to enable it selectively for certain devices.

1 Like

@Mika
For more clarity I’m attaching the HID report descriptor of SC2 Sport, in a text file properly indented, where the issue is immediately clear:
SC2_descr.txt (53.9 KB)

The issue is on the row 206 (in the “Set Effect Report” (21h)):
“Report Count” should be set to 2 for the two declared Usages in the collection “Direction” (57h), since the output is variable.
This would allow adding “Start Delay” at the end of the “Set Effect Report”, and Windows properly parsing and accepting the report descriptor, IMO.

For the “Set Condition Report” (5Fh):
The missing “Negative Coefficient” (0x62) Usage can be added after row 292, and then on row 293 “Report Count” changed to 2.
The missing “Dead Band” (0x65) Usage can be added at the end of the report description.

IMO those two missing fields in the condition report could be the cause of issues that some users have with the DirectInput Condition effects. Both can make things feel different if the developer used them.

I mentioned TrueDrive in my previous post, so would like to confirm that it works flawlessly on Wine with the hidraw backend enabled. Both paddock and offline. Enabling/disabling high torque, setting/changing options, working with profiles (creating, editing, deleting), etc., all works without issues. Even the wheel rotation graphic has a slight lag, exactly as Windows :slight_smile:.
The only feature I haven’t tested is flashing the firmware, since it’s a potentially dangerous move. So everyone, please flash your firmware on a Windows machine/VM, where the things are tested and it is safe to do.
Of course there are some other caveats - for example you can’t run it as easy on boot - that option won’t work the same way on Linux, etc.

Hm, I can’t get it working. Do you maybe know a simple tool to test FFB on Windows?
I tested with AC and somehow didn’t work. Since I made the changes using protontricks I’m very sure that I changed the appropriate prefix. I’m also sure that I changed/checked the correct hidraw device.
Also, is it really “Enable SDL”? Asking, because the registry entries usually don’t have a space.
Nevertheless, I tested with both settings and it just doesn’t work.
In addition, I played with the “Start” setting in the winebus registry group, as suggested here
https://wiki.winehq.org/Hid
to be sure, but that also didn’t help. (original setting was 3, changed it to 2)

@Berniyh
It won’t work on proton, as they have made it selectively allow devices with certain VID/PIDs for the hidraw backend. So the “Enable SDL” winebus.sys value doesn’t work there. I’m planning to push a patch which would enable Simucube, as well as other devices that would benefit from hidraw access. Hidraw was initially the default backend for proton, but few months ago they changed it to the way it works now.
It uses SDL for all other devices, so it needs the Linux PID FFB driver to work properly (which must be patched to recognize the Simucube as a PID device :slight_smile:).

For testing the FFB on Wine/Proton, just use WINEPREFIX=“the path to the prefix” “path to the wine binary” control. Then open the “Game Controllers” item and choose “Test Force Feedback” tab.

The “Enable SDL” value works on Wine/Wine Staging, though. I’m using Wine Staging with Steam (for Windows) installed in a WINEPREFIX. Can be done easily with Winetricks. Using it this way, since it’s easier for development (pushing patches to upstream, experimenting things, etc.). rF2 also had much higher FPS in Wine Staging with Steam (Windows), than on Proton with Steam (Linux). I believe they fixed the issue some time ago, although cannot be sure (haven’t played it on proton recently).

So the technology is there, with the last touches on the Wine PID implementation made with SC2 in mind :smile:. We just need to put the final pieces together to make it work out of the box.

For example:
If/When GD fixes the HID report descriptor and adds the missing usages, the Linux PID driver will recognize the Simucube as it is (without any changes needed). This way the FFB on Simucube would work out of the box for Proton (SDL backend) and any native Linux app.

If/When Proton devs accept the patch I’m planning to push for the hidraw access of Simucube, Proton would use the HID PID implementation in Wine, directly controlling the device through the hidraw Linux interface, etc., etc. :slight_smile:

I hope not to screw up but I think that to change the HID of the SC2 it can be achieved by activating the appropriate axes on TD general tab, so that Windows recognizes it as a steering wheel, not a generic device with only one axis as It comes from factory.

This is false. Those axis are always included in the HID report, but if unconfigured, they always report 0.

Changing the HID descriptor is something I will have a look at next week when back at the office.

Affecting the game controller / steering wheel aspect of device detection is not possible without writing a fully custom USB device driver.

1 Like

I’d be very interested in this patch! I took a peek at the proton source to see if I could figure it out, but I can’t see where they’re selectively permitting the hidraw backend, ie where I’d need to make a change.

That said, I also can’t get the TrueDrive paddock to run under Proton 7 either; I don’t know if that’s related, since it’ll see the wheelbase but via SDL and maybe get confused, but it just hangs when I load it up. I can get it to see the wheelbase under Proton 6, but naturally get no FFB under there for the other reasons you already mentioned. Happy to be a guinea pig for trying things out!