Would like to get disabling force feedback auto-centering working so that trimming will work properly with force feedback devices in flight simulators.
Currently the call to disable it is a stub
static HRESULT dinput_device_set_property( IDirectInputDevice8W *iface, const GUID *guid, const DIPROPHEADER *header ) { ... case (DWORD_PTR)DIPROP_AUTOCENTER: { const DIPROPDWORD *value = (const DIPROPDWORD *)header; if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED; if ( header->dwHow != DIPH_DEVICE) return DIERR_INVALIDPARAM; impl->vtbl->set_property( iface, FIXME( "DIPROP_AUTOCENTER stub!\n" ); impl->autocenter = value->dwData; return DI_OK; } ... }
After having looked through the wine, SDL, and kernel code and read up on the force feedback HID physical interface device (PID) usage table reports, I am guessing this was left as a stub because it doesn't fit in with the current design of dipnut communicating with the backend via HID reports. The issue being that auto centering, unlike everything else, isn't part of the PID specification, so there isn't a predefined report that can be used for this (see table 0x0f)
https://www.usb.org/document-library/hid-usage-tables-14
Although I am no wine or Windows code expert, I am guess this leaves the following options
- leave it disabled in the backend and emulate it via a frontend spring effect - abuse an existing HID report to communicate it - add a WINE specific HID report to communicate it - add a WINE specific IOCTL to communicate it
For the first option, I mean have the winebus backends disable it via the kernel or an SDL call and then have the dinput layer enable simulate it by adding a default spring effect. This then lets the dinput frontend disable it again by removing or disabling its spring effect. For the second one I mean something like reserve an effect ID for specifying auto centering.
Both of these are inspired by the fact that from the kernel code, it seems auto centering for HID devices is an undocumented use of the first effect ID for a spring effect
https://github.com/torvalds/linux/blob/610a9b8f/drivers/hid/usbhid/hid-pidff... https://github.com/torvalds/linux/blob/610a9b8f/drivers/hid/usbhid/hid-pidff...
Would be interesting in knowing what sort of implementation people think would be best and acceptable for inclusion into wine?
Thanks! Tyson
On 1/1/24 17:38, Tyson Whitehead wrote:
Would like to get disabling force feedback auto-centering working so that trimming will work properly with force feedback devices in flight simulators.
Currently the call to disable it is a stub
static HRESULT dinput_device_set_property( IDirectInputDevice8W *iface, const GUID *guid, const DIPROPHEADER *header ) { ... case (DWORD_PTR)DIPROP_AUTOCENTER: { const DIPROPDWORD *value = (const DIPROPDWORD *)header; if (!(impl->caps.dwFlags & DIDC_FORCEFEEDBACK)) return DIERR_UNSUPPORTED; if ( header->dwHow != DIPH_DEVICE) return DIERR_INVALIDPARAM; impl->vtbl->set_property( iface, FIXME( "DIPROP_AUTOCENTER stub!\n" ); impl->autocenter = value->dwData; return DI_OK; } ... }
After having looked through the wine, SDL, and kernel code and read up on the force feedback HID physical interface device (PID) usage table reports, I am guessing this was left as a stub because it doesn't fit in with the current design of dipnut communicating with the backend via HID reports. The issue being that auto centering, unlike everything else, isn't part of the PID specification, so there isn't a predefined report that can be used for this (see table 0x0f)
https://www.usb.org/document-library/hid-usage-tables-14
Although I am no wine or Windows code expert, I am guess this leaves the following options
- leave it disabled in the backend and emulate it via a frontend spring effect
- abuse an existing HID report to communicate it
- add a WINE specific HID report to communicate it
- add a WINE specific IOCTL to communicate it
For the first option, I mean have the winebus backends disable it via the kernel or an SDL call and then have the dinput layer enable simulate it by adding a default spring effect. This then lets the dinput frontend disable it again by removing or disabling its spring effect. For the second one I mean something like reserve an effect ID for specifying auto centering.
It would be nice to figure how Windows does it, and do the same. The idea is that we should be able to communicate to HID PID devices directly for when the hidraw is used.
There are tests in dinput/tests that emulate a virtual HID PID device with various custom HID device descriptors. It can be a bit tedious and difficult to figure, but if you can find an actual HID PID device which has dinput auto-center support on Windows, using its descriptor could be a good starting point to figure the required HID features. Some people have also been successfully using Wireshark to monitor HID reports over USB with HID PID devices to figure the expected report sequences.
Also, maybe Windows also supports some kind of fallback (or maybe it just works like that), as long as spring effects are available, in which case we should do the same.
Both of these are inspired by the fact that from the kernel code, it seems auto centering for HID devices is an undocumented use of the first effect ID for a spring effect
https://github.com/torvalds/linux/blob/610a9b8f/drivers/hid/usbhid/hid-pidff... https://github.com/torvalds/linux/blob/610a9b8f/drivers/hid/usbhid/hid-pidff...
Maybe doing something similar in dinput is the way to go if that's truly how it works, although that is only going to work with device-managed effect ids (which I don't think we do with SDL / evdev backends). The spring effect fallback is probably a good solution (even a primary solution if figuring how native works proves too difficult).
Cheers,
There are tests in dinput/tests that emulate a virtual HID PID device with various custom HID device descriptors. It can be a bit tedious and difficult to figure, but if you can find an actual HID PID device which has dinput auto-center support on Windows, using its descriptor could be a good starting point to figure the required HID features. Some people have also been successfully using Wireshark to monitor HID reports over USB with HID PID devices to figure the expected report sequences.
I bought an old MS Sidewinder FF2 off of EBay. It is a USB HID PID with device managed effect ids. I tried it on an old Windows laptop with USBPcap installed. It has auto-centering when plugged in. When you select it in MS's old fedit.exe program, it says it disables it, and it does. It also lets you run any of the effects. I captured sessions with USBPcap, but I think stuff must be being dropped, as what I get makes no sense
- unplugged device, stick move freely - plug in device, usb initializes, no HID items, power light comes on and auto-centering (stick no longer moves freely) - select it in fedit.exe, usb hid pid 0x7f feature get report (parameter block pools report), auto-centering goes off (stick moves freely) (linux does this exact same request, but auto-centering remains in effect with it) - add sine effect and play it, usb hid pid 0xab (create new effect parameter block) feature set report, usb hid pid 0x98 (effect parameter block load report) feature get report (reports id 1, while linux gives id 2, which seems to agree with auto-centering being off), effect plays on stick, but no command ever sent to disable auto-centering, set the effect type-specific parameter block, or actually start the effect playback that I can see - close program, stick resumes auto-centering, with no further hid reports - repeating gives the same packets
Here is a copy of the capture file from the above sessions if you want to see. I waited at least 10s between each action to clearly separate them. That gives you the USB HID reports for the Sidewinder FF2 too at time 38.536156
https://staff.sharcnet.ca/tyson/hidff3.cap
Maybe doing something similar in dinput is the way to go if that's truly how it works, although that is only going to work with device-managed effect ids (which I don't think we do with SDL / evdev backends).
That seems like the thing to do to me too.
Thanks! Tyson
.... I captured sessions with USBPcap, but I think stuff must be being dropped, as what I get makes no sense
Turns out it is all in there. I hadn't realized the URB_INTERUPT packets contained reports too. As can be seen in the captured data file
https://staff.sharcnet.ca/tyson/hidff3.cap
it is brutally simple. When fedit.exe starts, you see
7f - Parameter Block Pool Report 9A - DC Reset 99 - DC Stop All Effects 7e - Device Gain = 255
and it is the "stops all effects" that shuts down the autocenter. When it exits, you just see
9A - DC Reset
and it is this "reset" that restores the autocenter.
On 1/4/24 07:14, Tyson Whitehead wrote:
.... I captured sessions with USBPcap, but I think stuff must be being dropped, as what I get makes no sense
Turns out it is all in there. I hadn't realized the URB_INTERUPT packets contained reports too. As can be seen in the captured data file
https://staff.sharcnet.ca/tyson/hidff3.cap
it is brutally simple. When fedit.exe starts, you see
7f - Parameter Block Pool Report 9A - DC Reset 99 - DC Stop All Effects 7e - Device Gain = 255
and it is the "stops all effects" that shuts down the autocenter. When it exits, you just see
9A - DC Reset
and it is this "reset" that restores the autocenter.
So looks like the device itself has it builtin? Would probably be worth checking happens if you change auto-center ON and OFF through DInput.
I wrote a small test program to see what happens on the bus.
Discovered you can only set the autocenter property on or off while the device is not acquired. This is the steps the program does and what I see on the bus
// DirectInput8Create(...) // EnumDevices(...) // CreateDevice(...) // SetCooperativeLevel(...,DISCL_EXCLUSIVE | DISCL_BACKGROUND) // SetDataFormat(...) // SetProperty(DIPROP_AUTOCENTER_OFF) GET_REPORT 03 46 05 07 01: 03: PID Pool Report 46 05: RAM Pool Size: 0x546 (0-65535) 07: Simultaneous Effect Max: 7 (0-255) 01: Device Managed Pool: 1 (0-1) Shared Parameter Blocks: 0 (0-1) Usused: 000000b // Acquire() INTERRUPT OUT 0c 04: DC Device Reset INTERRUPT OUT 0c 03: DC Stop All Effects INTERRUPT OUT 0d ff: Device Gain: 255 // Unacquire() INTERRUPT OUT 0c 04: DC Device Reset // SetProperty(DIPROP_AUTOCENTER_ON) // Acquire() INTERRUPT OUT 0c 04: DC Device Reset INTERRUPT OUT 0d ff: Device Gain: 255 // Unacquire() INTERRUPT OUT 0c 04: DC Device Reset // SetProperty(DIPROP_AUTOCENTER_OFF) // Acquire() INTERRUPT OUT 0c 04: DC Device Reset INTERRUPT OUT 0c 03: DC Stop All Effects INTERRUPT OUT 0d ff: Device Gain: 255 // Unacquire() INTERRUPT OUT 0c 04: DC Device Reset // Release(...) // Release(...)
Under XP the acquire and unaquire DC device resets are followed by a
INTERRUPT OUT 01 01 00 00 00 00 00 01 00 ff 00 84 00 ff 00 00: Effect Block Index 1 01: Set Effect Report 01: Effect Block Index: 1 (1-40) 00: Effect Type: 0 (1-12) (i.e., NULL) 00 00: Duration: 0 (0-32767) 00 00: Trigger Repeat Interval: 0 (0-32767) 01 00: Sample Period: 1 (0-32767) ff: Gain: 255 (0-255) 00: Trigger Buttons: 0 (1-8) (i.e., NULL) 84: Axes Enable: X: 0 (0-1) Y: 0 (0-1) Direction Enable: 1 (0-1) Unused: 1000b 00 ff: Direction: Instance1: 0 Instance2: 255 00 00: Start Delay: 0 (0-32767)
This is not done under Windows 7.
So, what I see is
- the reset state has a spring effect running in effect slot 1 - a reset command is issued after acquiring or unacquiring - a stop all effects command is also issued after the acquirinig reset command if autocenter is disabled - a set gain command is issued acquiring - trying to change autocenter while the device is acquired returns DIERR_ACQUIRED (this is not documented)
Cheers! Tyson
On 1/8/24 06:16, Tyson Whitehead wrote:
I wrote a small test program to see what happens on the bus.
Discovered you can only set the autocenter property on or off while the device is not acquired. This is the steps the program does and what I see on the bus
// DirectInput8Create(...) // EnumDevices(...) // CreateDevice(...) // SetCooperativeLevel(...,DISCL_EXCLUSIVE | DISCL_BACKGROUND) // SetDataFormat(...) // SetProperty(DIPROP_AUTOCENTER_OFF) GET_REPORT 03 46 05 07 01: 03: PID Pool Report 46 05: RAM Pool Size: 0x546 (0-65535) 07: Simultaneous Effect Max: 7 (0-255) 01: Device Managed Pool: 1 (0-1) Shared Parameter Blocks: 0 (0-1) Usused: 000000b // Acquire() INTERRUPT OUT 0c 04: DC Device Reset INTERRUPT OUT 0c 03: DC Stop All Effects INTERRUPT OUT 0d ff: Device Gain: 255 // Unacquire() INTERRUPT OUT 0c 04: DC Device Reset // SetProperty(DIPROP_AUTOCENTER_ON) // Acquire() INTERRUPT OUT 0c 04: DC Device Reset INTERRUPT OUT 0d ff: Device Gain: 255 // Unacquire() INTERRUPT OUT 0c 04: DC Device Reset // SetProperty(DIPROP_AUTOCENTER_OFF) // Acquire() INTERRUPT OUT 0c 04: DC Device Reset INTERRUPT OUT 0c 03: DC Stop All Effects INTERRUPT OUT 0d ff: Device Gain: 255 // Unacquire() INTERRUPT OUT 0c 04: DC Device Reset // Release(...) // Release(...)
Under XP the acquire and unaquire DC device resets are followed by a
INTERRUPT OUT 01 01 00 00 00 00 00 01 00 ff 00 84 00 ff 00 00: Effect Block Index 1 01: Set Effect Report 01: Effect Block Index: 1 (1-40) 00: Effect Type: 0 (1-12) (i.e., NULL) 00 00: Duration: 0 (0-32767) 00 00: Trigger Repeat Interval: 0 (0-32767) 01 00: Sample Period: 1 (0-32767) ff: Gain: 255 (0-255) 00: Trigger Buttons: 0 (1-8) (i.e., NULL) 84: Axes Enable: X: 0 (0-1) Y: 0 (0-1) Direction Enable: 1 (0-1) Unused: 1000b 00 ff: Direction: Instance1: 0 Instance2: 255 00 00: Start Delay: 0 (0-32767)
This is not done under Windows 7.
So, what I see is
- the reset state has a spring effect running in effect slot 1
- a reset command is issued after acquiring or unacquiring
- a stop all effects command is also issued after the acquirinig reset
command if autocenter is disabled
- a set gain command is issued acquiring
- trying to change autocenter while the device is acquired returns
DIERR_ACQUIRED (this is not documented)
Cheers! Tyson
Interesting, thanks for that investigation. I think we can do the same in dinput, and send DISFFC_STOPALL when auto-centering is off.
Then, in winebus.sys, and depending on the backend -in evdev, but maybe also SDL unless it has another better way for doing it- we could implement PID_USAGE_DC_DEVICE_RESET in a way that it adds an implicit spring effect when supported.
On Mon, Jan 8, 2024 at 2:58 AM Rémi Bernon rbernon@codeweavers.com wrote:
Interesting, thanks for that investigation. I think we can do the same in dinput, and send DISFFC_STOPALL when auto-centering is off.
Then, in winebus.sys, and depending on the backend -in evdev, but maybe also SDL unless it has another better way for doing it- we could implement PID_USAGE_DC_DEVICE_RESET in a way that it adds an implicit spring effect when supported.
In retrospect, it makes sense that the reset state has a spring effect playing.
Without a default spring effect playing on startup/reset, it wouldn't be usable as a regular HID joystick on systems that don't have PID support.
Are there actually devices that don't reset to a spring effect being played then?
Put together an implementation. Works good on my hardware. Here were the required changes
dinput - always DIERR_ACQUIRED if trying to set autocenter and acquired - send a stop to all effects on acquire if autocenter is off
bus{sdl,udev} - enable autocenter on reset - disable autocenter on stop all effects
https://gitlab.winehq.org/wine/wine/-/merge_requests/4830
The prior code didn't DIERR_ACQUIRED if it was in exclusive mode, but Windows does. This behaviour is what makes it easy to implement as it means you only have to deal with autocenter during an acquire where you can just do a stop all effects as only autocenter is running.