hhd-dev / hhd

Handheld Daemon, a tool for configuring handheld devices.
GNU General Public License v3.0
110 stars 14 forks source link

FAQ: why spoof a PlayStation controller? #4

Closed appsforartists closed 8 months ago

appsforartists commented 9 months ago

As someone new to Linux and Steam, my understanding of Linux gamepad support is:

  1. controller connects via USB/Bluetooth and advertises which buttons it supports
  2. controller is polled for events
    • controller announces which of the buttons it announced are currently down (and pressures for analog sticks/triggers)

As such, I would expect a novel controller (like the Legion Go) to announce "I have ABXY, start, select, mode, capture, D-pad, 2 sticks, 2 digital shoulders, 2 analog shoulders, 4 rear paddle buttons, and a scroll wheel," and then for those buttons to appear in Steam Input.

The button codes are defined in the kernel, and the mappings for the very similar Steam Deck are documented in the kernel.

Why does HHD pretend to be a PS Edge?

antheas commented 9 months ago

Hi Brenton, this is a complicated question to answer. It essentially boils down to the fact that there was this revolutionary controller named xbox 360 and it was so good and it lasted for so long that the kernel was shaped to only have its capabilities.

The kernel has no notion of back buttons, imu (gyro, accelerometer), touchpad, haptic triggers, mute button etc.

Although im happy to find out about BTN_BASE in the steam driver, which I will include in the next update. I did not know that it existed and I will map that to legion R on the evdev emulation version.

As you see, steam hid uses BTN_TRIGGER_HAPPY* for the back paddles. 1, 2, 3, 4 are not canonical names for the paddles, but placeholders for extra buttons.

For imu (gyro/accel) support, the drivers nintendo, playstation, create an evdev "Motion Controller" device that applications are meant to bind to the original device in userspace. Right now, this device is only supported by the emulator dolphin and it is expected that the next SDL version will include it so that it will work with Yuzu.

But both controllers use different axis for different things, so you need per controller mappings.

Essentially, it is a big mess and it is such a big mess nobody uses the kernel to bind advanced controller features for the DS5, the steam deck, or any advanced controller.

Everyone reads the raw output of the controller using hidraw. Which is what brings us to why use the DS Edge controller. The DS Edge controller is a controller that is supported by the kernel fully (driver from sony; the touchpad works; motion devices are exposed; leds work), by steam fully (steam can write leds and remap the back buttons through hidraw), and by SDL fully (yuzu and linux native games can utilize the controller fully through hidraw as well).

If you remap the buttons to HAPPY*, steam will choose (even though it can see them) to not recognize them and to not let you use them for gamepad layouts. It will also not support gyro, and steam is one of the main ways gyro is used right now. Essentially: no advanced features.

The current version of HHD actually supports doing evdev emulation and you can test it out. As long as you dont need back buttons in steam (they work outside of it) and can wait a bit for yuzu to catch up to reading motion devices, you can use the evdev controller fully. The motion controller is experimental.

So this answers why DS5, but not why userspace.

antheas commented 9 months ago

However, this is just part of the story. Because if this was the only issue, this is something that can be solved in the kernel level as a driver that converts the hidraw of the legion go to the one of the hidraw of DS Edge. However, HHD as you know is userspace.

The reason for this is that in order for HHD to show a pretty ds edge controller, it actually has to fuse together 6 different devices.

Legion go controllers actually appear as a usb hub, and each device is a separate usb device. To fuse this together into one driver we would have to actually create a driver for the whole hub and I dread to think what that would involve.

Also, the accel and gyro come from a different subsystem (lenovo does not give access to controller imus; just very partially) , so it is not possible to include them in the above driver. We use the display gyro to fix that, which uses a pcie co-processor and a meh kernel driver.

Therefore, the only way to do a unified device that has all the capabilities of the legion go is to to it in userspace. For the time being.

The dse controller maps to the legion go 1-1, exactly. The only difference is that the legion go has an extra button (m2). But not even that is a problem, because everyone expects legion R to be QAM and steam only supports that on the steam deck. Other controllers have to use Mode + A, so legion R actually becomes a macro under hhd. So the m2 button becomes the mute button instead of legion R.

The dse controller does have a big issue though, and that is the glyphs. Ideally, we would emulate an SD OLED controller, but im waiting on some reverse engineering to do that.

Edit: in case youre wondering, both sd and dse have built in imus, and send a unified report that contains all buttons, axis, and imu data in one. Which makes it very easy to parse from an app perspective. Even without using the kernel.

antheas commented 9 months ago

Does this answer your question?

Also, we're starting to work on the ui for hhd. You seem to have experience with UI/graphic design perhaps. If you want to help ping me on discord. I will answer tomorrow.

appsforartists commented 9 months ago

This guy has done a bunch of reverse engineering on the Steam Deck. He's on Matrix - I can put you in touch if it's helpful.

Thanks for the thorough response! Sounds like the crux of it is that the firmware presents the controller as a USB hub of a bunch of individual USB devices to be reassembled into a controller; Lenovo does that on Windows. Someone needs to do it on Linux. The DS Edge was the easiest thing to reassemble it into that would completely work in the SteamOS UI. Work is in progress to reassemble it into a generic controller (evdev), but Steam doesn't expose the rear paddles in the UI if you use that mode. Work is planned to reassemble it into a Steam Deck (and eventually Joy Cons, Xbox Elite, etc.).

Is that a fair summary? If so, maybe we should add it to the README with a link to this issue for the full explanation.

antheas commented 9 months ago

Good idea about adding to FAQ. I will reference the daemon you sent. It would be good to get in touch with that developer. I did not know that project existed.

The scopes of hhd and opensd are different. I originally made hhd run as a user daemon, which would seem to be more privacy friendly. However, I received pushback on the additional udev rules that were required. So it is easier from a community perspective to run as a system service.

It also allows hhd to expand to being more than a remapping daemon and to do advanced features like tdp control and controller hiding.

With projects like this, the UI is a killer feature, which is why current development centers on getting an http api working and a PWA under hhd.dev, a domain that I bought yesterday.

About your explanation, see comment below.

antheas commented 9 months ago

Thanks for the thorough response! Sounds like the crux of it is that the firmware presents the controller as a USB hub of a bunch of individual USB devices to be reassembled into a controller; Lenovo does that on Windows. Someone needs to do it on Linux. The DS Edge was the easiest thing to reassemble it into that would completely work in the SteamOS UI. Work is in progress to reassemble it into a generic controller (evdev), but Steam doesn't expose the rear paddles in the UI if you use that mode. Work is planned to reassemble it into a Steam Deck (and eventually Joy Cons, Xbox Elite, etc.).

Lenovo does not do that in windows. In fact, the legion controllers with your xpad patch have feature parity with the windows version. And work just as poorly if legion space is not installed.

The following project does the same work of hhd in windows and I have been in touch with its developers. https://github.com/Valkirie/HandheldCompanion

How handhelds work under windows

Essentially, all windows handhelds expose their controllers as multiple devices: a vendor device, a shortcuts device, and a controller device.

The vendor device is used to configure the controllers from the manufacturer software and can modify what the controller, shortcut devices output.

The shortcuts device outputs shortcuts windows users find useful (ctrl+alt+delete, print screen etc).

The controller device typically uses a protocol named xinput, which only allows for the buttons found in an xbox 360 controller. In the initial firmware versions of the go, the controllers did not output the xbox button, but lenovo responded to complaints and added a shortcut for it (Legion L + RB ?).

HandyGCCS

There exists a project right now that, for devices from GPD and Ayeneo, can fuse these devices together under a new common device. See below. https://github.com/ShadowBlip/HandyGCCS

However, this project outputs a uinput controller (which hhd can do now as well), which has the limitations I described above. No back buttons (in steam) and no gyro.

In addition, it only supports input from evdev (hhd also does hid). For devices like the Legion Go, that gatekeep certain buttons to manufacturer software, if there is no kernel driver outputting events, it can not use it. If you see under the legion go section, HandyGCCS only supports rebinding Legion L + B. The back or legion buttons themselves are not available.

How do back buttons/gyro work in Windows?

Legion go comes with advanced controllers that have back paddles and Legion L, R buttons. Handling back buttons as you've seen above is a mess. Legion space sends commands to the controllers that instruct them to remap the back buttons into the controller output, in a limited set of actions. Lenovo reps have said this will change with a firmware update coming mid-Jan onwards.

I have decoded all legion space commands below. I plan to add them to hhd, but since everyone seems happy software remapper I do not know if it will be useful (we can not remap legion buttons with those). https://github.com/hhd-dev/hwinfo/tree/master/devices/legion_go/peripherals

Keep in mind that in windows with various anti-cheat software and thousands of different configurations, doing the remapping in hardware and outputting an xbox device is essential to both avoid anti-cheat bans and to ensure software works properly.

Dualsense controllers, joycons and the steam deck have really poor support in windows, because everyone expects an xbox controller. See below. https://www.pcgamingwiki.com/wiki/Controller:DualSense

Lenovo hopes to build a side revenue stream through legion space, so Legion L + R are legion space only. It is essentially a bixby button v2. They also do it for ease of use and compatibility reasons (steam + apps cant listen to them and launch alongside space). But if you want to use them as QAM or anything else, you need to wrap the xinput controller and splice in information from the vendor device.

This is very non-trivial to do in a linux kernel driver, as the vendor device and xinput device are completely different usb devices with different protocols. Also, because of non-standardization, the output device would be poorly supported and users will find issues with the driver.

For example, the upcoming ROG Ally kernel driver remaps the back buttons to F14 and F15. If I were a linux maintainer I would reject this patch as I frequently use F13 and F14 for my shortcuts in my devices and I would expect this would break the workflows of downstream users.

Reference the fact that hhd has an extensive configuration file that users still find issues with. Some users detest the fact hhd switches the glyphs to playstation ones and refuse to use that for this reason. However, DSE is the only controller with broad support for all its features.

Emulating the SD Controller would fix the glyphs but has much poorer support (SDL does not support rumble for example). Since downstream apps expect the user to wrap it with steam input.

Also, the touchpad + leds work beautifully for games designed for the playstation, such as Spiderman Remastered, which im considering buying in the winter steam sale.

antheas commented 9 months ago

I will also add that there is a performance overhead with using python for emulation of around 2-3x. Especially when reading hidraw directly. Still, hhd uses around 2.5% of a single CPU core with all features enabled in idle and around 4% when using controller axis. Which is very acceptable. You can halve it to 1.5% without gyro, reading the hidraw device + polling the imu to work are expensive.

I did not make the choice lightly. The python ecosystem is very mature and python makes community contributions and packaging a lot easier. Especially for sections that are not performance sensitive (e.g., 90% of the hhd source code).

Also, the controller hotpath can be rewritten in C in the future, once the performance requirements are stable, and packaged as part of the hhd package. Python has excellent inter-interoperability with C.

We can probably reduce it to a bit lower than 2% if around 1000 lines of hhd are rewritten in C. But i plan to rewrite most of the API soon to allow for macros and generalize it for other devices. So it does not make sense for the time being.

appsforartists commented 9 months ago

Super impressed with your reverse engineering work - thanks for the thorough reply!

the touchpad + leds work beautifully for games designed for the playstation, such as Spiderman Remastered

This wouldn't have occurred to me - I'll have to give it a try when I get to Spiderman.

The python ecosystem is very mature and python makes community contributions and packaging a lot easier.

As someone who speaks Python and not C, I approve of this choice. It definitely makes the codebase more approachable than most in this space.

(Leaving this issue open because I still think we should include this in the FAQ.)

antheas commented 9 months ago

Turns out BTN_BASE has an offset that is lower than the normal BTN_* values.

So it becomes the first button of the controller.

It is a terrible idea to use because it will pretty much break every single app that doesnt have a profile for the HHD controller. Sucks for the steam deck that uses it I guess. But it is another example of kernel issues.