-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Add a hwdb listing game controllers, use it to apply uaccess #22860
base: main
Are you sure you want to change the base?
Conversation
ID_INPUT_JOYSTICK refers to "joysticks", which for historical reasons is the name given by the Linux kernel and udev to most game-oriented input devices, including flight sticks, gamepads, steering wheels and so on (but not accelerometers or gyroscopes that have axes but no buttons, even if they are part of a gamepad with motion controls such as a PS3/PS4/PS5 controller). My intention is for ID_GAME_CONTROLLER to be a generalization of this, applying to anything that a gamer would think of as a dedicated gaming controller, for example: * all "joysticks", including gamepads, steering wheels and so on * the accelerometer that provides motion controls in a PS3/PS4/PS5 controller, which is part of the same HID device but exposed as a separate evdev device node by the Linux kernel (see systemd#17691) * the EDTracker, which is an Arduino-based accelerometer designed to be used for head tracking by Elite:Dangerous players (see ValveSoftware/steam-for-linux#8443) However, ID_GAME_CONTROLLER should not include accelerometers used to detect the orientation of a laptop/tablet, because those are not really a gaming device (admittedly people have sometimes hooked up older Thinkpads' HDAPS accelerometers to control games by tilting the laptop, but that seems like something that's reasonable to have to configure for yourself). I've named this without the INPUT_ prefix with the intention that it can be applied to both evdev devices and hidraw devices (systemd#22681). Signed-off-by: Simon McVittie <smcv@collabora.com>
The initial hwdb is taken from steam-devices, which is mostly a list of gamepads suitable for use with Steam's Steam Input input-remapping features, but it can be expanded later to include non-gamepads. Many of these controllers can be connected either via Bluetooth or with a USB cable, with the same vendor/product ID either way; for those, make the bus part of the modalias match both. A few devices have a different product ID when connected via USB or via Bluetooth, and some devices have an optional wireless dongle for a non-standard (non-Bluetooth) wireless protocol, which shows up as a distinct USB product ID; list those dongles separately. Being a game controller is really a fact about the physical device rather than a fact about a specific kernel interface (evdev and/or raw HID), so match against the HID parent device's modalias. This lets us avoid the differences in syntax between USB and Bluetooth devices, and between evdev and raw HID "views" of the same device. The steam-devices udev rules match all HID devices under Valve's vendor ID, regardless of product ID, but for this commit I've only included devices that correspond to the publically available (and now discontinued) Steam Controller, with commented-out entries for other Steam Controller versions that are listed in SDL source code. Additional devices can be added or uncommented later if desired. Detection of "joysticks" via input_id's heuristics (based on counting buttons and axes) continues to be necessary for non-HID game controllers, in particular Xbox gamepads via the xpad driver and legacy MIDI-port joysticks. Signed-off-by: Simon McVittie <smcv@collabora.com>
The subset of these that had ID_INPUT_JOYSTICK were already user-accessible, but now that we are marking HID game controllers as such, we can extend that to things like the motion controls integrated into the Playstation 3, 4 and 5 controllers. Resolves: systemd#17691 Signed-off-by: Simon McVittie <smcv@collabora.com>
The usual way to communicate with game controllers on Linux is the semi-high-level evdev interface, but games and game-related software sometimes benefit from lower-level access to the raw HID device nodes. This is particularly desirable for Wine, because the lower-level Windows APIs for game controllers are analogous to Linux hidraw device nodes, and Windows games make active use of this. If raw HID access is available for a HID controller like a Playstation gamepad, then Wine can pass it through as-is, and games that work with a particular controller on Windows will generally work on Linux too; if only the semi-high-level evdev interface is available, then Wine has to fake up a HID device the information provided by evdev, which involves some information loss and can result in breaking the game's assumptions (for example putting joystick axes in the "wrong" order could break a game that "knows" that axes 2 and 3 are the right stick). Raw HID access can also provide controller-specific functions, like control over the lightbar on a Playstation gamepad, and similar device-specific functionality of Steam Controller gamepads. Some of these are also exposed via Linux-specific kernel APIs, but games and their middleware libraries that want to manipulate this sort of thing will generally need to know how to do it via raw HID anyway (otherwise they wouldn't work on Windows and macOS), so it's natural to want to reuse the same tested code on Linux. It is important that we do not expose raw HID access to *all* HID devices, because that would be enough to log keyboard inputs. Resolves: systemd#22681 Signed-off-by: Simon McVittie <smcv@collabora.com>
This is a prototype, and so far I've only tested it by installing rules/hwdb files manually, not by doing a complete systemd build. Does it look like I'm going in the right direction? |
Suggestions welcome for a better name that encompasses joysticks, gamepads, steering wheels, accelerometers, gyroscopes, Guitar Hero guitars and so on... |
I haven't (yet) attempted to parse the HID descriptors like |
An open question: |
@@ -63,7 +63,8 @@ ENV{ID_PDA}=="?*", TAG+="uaccess" | |||
# Programmable remote control | |||
ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess" | |||
|
|||
# joysticks | |||
# Gaming devices | |||
SUBSYSTEM=="input", ENV{ID_GAME_CONTROLLER}=="?*", TAG+="uaccess" | |||
SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given your first commit implied ID_GAME_CONTROLLER if ID_INPUT_JOYSTICK is set, you can drop the second line, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SUBSYSTEM=="hidraw", ENV{ID_GAME_CONTROLLER}=="?*", TAG+="uaccess"
SUBSYSTEM=="input|hidraw"
@@ -65,6 +65,7 @@ ENV{ID_REMOTE_CONTROL}=="1", TAG+="uaccess" | |||
|
|||
# Gaming devices | |||
SUBSYSTEM=="input", ENV{ID_GAME_CONTROLLER}=="?*", TAG+="uaccess" | |||
SUBSYSTEM=="hidraw", ENV{ID_GAME_CONTROLLER}=="?*", TAG+="uaccess" | |||
SUBSYSTEM=="input", ENV{ID_INPUT_JOYSTICK}=="?*", TAG+="uaccess" | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we really open up hidraw for all joysticks? shouldn't it just be the gaming devices that do more than regular joysticks?
i.e. opening hidraw should probably be restricted: if there's a proper input driver around handling these devices, we should push apps to use that, no? only if no full input driver exists we should open up hidraw, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see the (4th) commit message. My understanding is that the problem is that the "proper" input driver is evdev, which involves some information loss: it's a semi-high-level interface that normalizes devices according to a Linux-specific abstraction layer, but that normalization removes information that is sometimes relevant. The difference is a bit like the difference between scancodes and keysyms, or the difference between raw and cooked terminals.
If the form of input you want is "the user is holding left on the d-pad while pressing the A button and pulling the right trigger 75% of the way down", then that's fine. However, if you're Wine passing through a HID device to a Windows game (which expects an interface that is basically equivalent to Linux hidraw devices), then the form of input you really want is more like "the user is pressing the 2nd and 7th buttons, while the 5th analog axis is at 75%", so that the order of the axes matches up with what's listed in the HID descriptor that was also passed through, and is identical to what the same device would do in Windows.
Wine is able to take an evdev device representing a gamepad and fake a semi-plausible HID device from it (basically by assuming it looks like an Xbox gamepad), but I don't think it's able to do that for more general game controllers like flight sticks, steering wheels and gyroscopes, because too much information has been lost in the translation from HID to evdev.
I think SDL and Steam Input have other reasons to prefer raw HID, but I'm not 100% clear on what those reasons are. @slouken might able to say more about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think SDL and Steam Input have other reasons to prefer raw HID, but I'm not 100% clear on what those reasons are. @slouken might able to say more about this?
Steam and SDL use the raw HID device for PS4, PS5, Nintendo Switch, and Steam Controllers for direct report access to support advanced features in a consistent way across all platforms and kernel versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
devices that do more than regular joysticks
That seems like it assumes that a "regular joystick" is something we can easily describe, which I don't think is really the case any more. Are you thinking of the gameport (MIDI-port) joysticks that you might have used 25 years ago, with 2 axes and some numbered buttons? Those are not actually very common or interesting any more.
The baseline level of controller complexity is an Xbox gamepad (6 axes and 15 buttons, if I'm counting correctly), and it goes up from there: in particular, recent Playstation gamepads show up in the evdev interface as three separate evdev devices (one for the functionality it shares with an Xbox gamepad, one for 6 axes of motion controls, and one for a laptop-style clickable trackpad), and it's up to user-space to figure out which ones belong to the same physical device and put them back together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for direct report access to support advanced features in a consistent way across all platforms and kernel versions
What are those advanced features and why would a solution based on evdev not be consistent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are those advanced features
@slouken would likely have a better answer to this part than I do.
why would a solution based on evdev not be consistent?
For SDL, this is mainly a trade-off about where to put the hardware abstraction layer and device-specific knowledge.
One of the goals of libraries like SDL is to present a cross-platform API to games and game-like programs. If it can implement that API by doing literally the same HID operations on Windows, macOS and Linux, then the behaviour of the hardware (including subtleties that games probably shouldn't rely on but almost certainly do, like the order in which axes and buttons are enumerated) is going to end up identical, and a bug-fix for one OS will immediately benefit the others too.
If SDL needs to do HID requests on Windows and macOS, but evdev for input / sysfs for LEDs / etc. on Linux (which ends up being translated into HID requests by the kernel, in a way that can differ between kernel releases), then the same version of SDL will have one set of behaviours on Windows and macOS, plus one or more different sets of behaviours on Linux (possibly kernel-dependent). Faced with that, game developers are very likely to test on Windows, see that their game works fine and call that good enough, even if it doesn't actually work as intended on commonly-used versions of Linux.
Having the hardware-specific knowledge in SDL (or, more generally, in user-space) means you can gain support for new game controllers by upgrading SDL, but not by upgrading the kernel; conversely, having the hardware-specific knowledge in the kernel means you can gain support for new game controllers by upgrading the kernel, but not by upgrading user-space. Mechanisms like the Steam Runtime and Flatpak make it quite straightforward to use an updated SDL, even on LTS distributions that continue to use an old kernel.
As I think I mentioned previously, one of several APIs for game controllers in Windows is basically HID ("dinput", emulated by Windows for Xbox controllers, or direct USB/Bluetooth HID for most others), so any game that wants to run on Windows needs some way to deal with HID anyway. In practice, basically all game developers want their game to be able to run on Windows, so we might as well take advantage of that.
The API that's available on Windows is also very relevant for Wine, which needs to be able to fake that API in terms of the information it can get from Linux. As I mentioned previously, when the Linux kernel converts lower-level HID into higher-level evdev, some information is lost (the application sees for example BTN_SOUTH
and BTN_EAST
, but doesn't know what order those appeared in for the lower-level HID interface), which makes it difficult for Wine to convert evdev back into HID in a way that will provide the same game-visible behaviour as Windows' "dinput" interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
chiming in here: evdev is imo woefully unsuitable for joysticks, full stop. as said above it's a lossy conversion from HID, it splits a single physical device into separate event nodes (that the evdev client then has to re-associate via guesswork) and it's linux only. The standardisation of events makes life easier for clients but that doesn't really work for joysticks which tend to push the limits of evdev (and adding new EV_ABS
axes is virtually impossible without a major rework in the kernel and a new API).
The only advantage left is that it has EVIOCREVOKE
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding new EV_ABS axes is virtually impossible without a major rework in the kernel and a new API
That nicely explains why the PS3/PS4/PS5 controller is exposed as two or three separate evdev devices (6-axis joysticks/triggers, 6-axis accelerometer/gyro, and 2-axis trackpad on PS4/PS5) rather than piling everything into one device: the left stick, the trackpad and the accelerometer all want to be ABS_X
/ABS_Y
, the right stick and the gyro both want to be ABS_RX
/ABS_RY
, and the triggers, gyro and accelerometer all want ABS_Z
/ABS_RZ
.
standardisation of events makes life easier for clients
Yes and no: some of the event maps are very explicit and obvious (for example I like the way the face buttons have been standardized as BTN_NORTH
, etc. so that it's obvious that the upward-pointing face button should always be BTN_NORTH
even though it's labelled Y on Xbox, triangle on Playstation and X on Nintendo), but the conventional mapping for the right stick on a gamepad is somewhat wrong (it repurposes ABS_RX
and ABS_RY
, which in theory are meant to measure rotation), and the conventional mapping for analogue triggers is just odd (a z-axis associated with the stick on the same side? really?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very much a case of hammers in desperate search for nails :) The inertia of getting changes in also means that some axes are abused because it's easier to just know that ABS_FOO
is really "bar" then getting ABS_BAR
added. This was true even before the multitouch support which mostly used up the remaining namespace - see also the wacom drivers which had some creative use of the abs space.
The separation into multiple event nodes is also a factor of the kernel hid drivers splitting per HID application collection - which mostly makes sense and absolves us from having to deal with e.g. a pen and multitouch on the same event node, without a good way of splitting the event streams correctly.
Joysticks, as usual, just tend to fall outside the easy-to-deal-with boundaries.
For weirder or more defective devices where a vendor/product pair is not enough (like ValveSoftware/steam-for-linux#8443, which is a generic Arduino microcontroller programmed to be an input device but has a human-readable name that indicates its function, or ValveSoftware/steam-devices#9, which is a device that reports its vendor and product IDs as all-zeroes but has a correct name), we might need to do a second lookup based on the USB or Bluetooth device ancestor instead of the HID ancestor, in the hope that it reports more useful information. Would it be reasonable to do two hwdb lookups per evdev or hidraw device? I don't really have an intuition for how "expensive" these lookups are. I'd like to continue to use the HID ancestor's modalias string as the primary way to identify game controllers by vendor/product, because for the increasingly common case of a hybrid USB/Bluetooth controller where the vendor and product IDs are the same either way, that lets us match against USB and Bluetooth simultaneously. In particular, newer Playstation and Nintendo controllers work that way. |
they are cheap. I mean, it's not that you are going to connect 10K ps4 gamepads to a single system... So it's just a small number, and then it's negligible if you do 1, 2, or 5 lookups per device. See how |
The documentation for this needs to be absolutely clear that we don't need to receive any hwdb changes if devices are already detected and tagged as ID_INPUT_JOYSTICK by the input-id udev helper.
I don't understand what this does. For each addition, it would be good if you could explain why the property addition is needed, as there are more automated ways to add tags for, say, accelerometers or touchpads that are siblings of a ID_INPUT_JOYSTICK device on USB/Bluetooth device for PlayStation controllers.
There aren't any "MIDI-port joysticks". There's MIDI devices connecting to a gameport.
This is fine.
This isn't good at all. There's no equivalent of This also means that every single device that offers a firmware update interface on top of a HID API will be at risk there. Which means that if you want those devices mentioned by Sam (#22860 (comment)) to have specific handling, then it might be best for them to have special handling, rather than try to make this more generic than it should be. |
This is going in the opposite direction that I want to see. There is nothing special about game controllers which would require them to be user accessible and not mediated by a compositor. We should get inputfd going instead of just making things worse in the long run where we might want to be able to control a compositor with a game pad or support more advanced input methods such as what OpenXR provides. |
# The lookup keys are composed in: | ||
# 60-input-id.rules | ||
# | ||
# Note: The format of the "hid:" prefix match key is a contract between |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a mostly orthogonal to this PR, but I guess we can also discuss this here:
this boilerplate is used in all our files, since forever. But I think it makes no sense. If it were true, users would never be able to make meaningful local hwdb entries. Is there any reason to keep this? I'd prefer not to add it new files as a start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
→ #22990
In general, I think we shouldn't overthink this. If there's some corner case where we give the user access to the accellerometer that is not a gaming device would not be the end of the world. So I'd rather go for simpler rules that cover all the devices that we want covered, instead of trying to be super precise. |
I'm getting mixed messages here: some reviewers are saying that overly-broad access is Very Bad and must be prevented by being absolutely specific about each device (e.g. by vendor and product IDs), but other reviewers are saying that matching via heuristics (for example "accelerometer belonging to the same HID ancestor as a gamepad") would be preferable. I think the answer is perhaps that access to raw HID should be restrictive, but access to evdev via heuristics is OK; and some of the reviewer comments are only looking at one of those two interfaces, and disregarding the other one. As a reminder of the high-level approach, there are three related things happening in this MR:
If reviewers' consensus is that #22681 is rejected / wontfix, then it would make sense to solve #17691 in a way that only works for #17691, instead of this PR; but if #22681 is a valid feature request, then it seems to me that it would make sense to use it as part of the solution to #17691 too. Steam currently implements the equivalent of #22681 (and more) by installing its own udev rules, and considering the absence of those udev rules to be unsupported. My aim in contributing this to systemd is to reduce the need for Steam to install its own udev rules, and perhaps eventually stop installing them at all, but that's not going to happen while the Steam maintainers (who do not include me) still perceive them to be necessary.
I don't see how to do that via declarative hwdb and/or udev rules due to their restriction to looking at the device itself and a single ancestor, so this would have to be imperative C code in This could resolve #17691, but would not do anything to help #22681, unless we ran imperative C code similar to
I'm aware, and that's why I was proposing that this be limited to specific devices for which the user expectation is "it's a game controller, I can control games with it" rather than "it's how I type my password". I agree that the ability to write a keylogger that persists across fast-user-switching is not OK, but it's much less clear to me that a gamepadlogger would be considered particularly bad.
That's sufficient for evdev, but not sufficient for hidraw; so that's only true if we're not doing #22681 at all, or if we implement #22681 by imperative C code that walks up the tree from the
This is a reason to be relatively cautious about which devices we classify as known game controllers, but I don't think it's a reason not to allow HID access to known game controllers. Users generally have physical access to game controllers (the controller isn't a whole lot of use otherwise!), and if that's the case, then then they can change its firmware equally well by temporarily connecting it to a different device that is under their control - perhaps a smartphone, or a Windows PC, or a games console.
We have something like 20 years of prior art for keyboards and mice being mediated by the windowing system (Wayland compositor or, historically, X11 server) but joysticks and gamepads being accessed more directly by the application, and this is also how it works on other platforms (at least Windows, and I think also macOS but I'm less sure there). Changing every game engine (including the unmaintained ones) to use inputfd seems ambitious. |
I can use a game controllers to enter my credit card details into steam for example and every process could snoop that. Any random process being able to brick any device I plug in is also horrible. The fact that the user has physical access is completely meaningless. We want to restrict access to applications not to the user. If you find a way to expose HID devices with only the I/O features (e.g. no firmware flashing, no persistent configuration, ...) and access to them can be revoked that would be a different story. I would still be against just making them user accessible and instead mediate access via inputfd but at least that would work. And to be honest, getting the kernel to create such a restricted HID device doesn't sound impossible. |
@bentiss is that something HID-BPF could be used for? |
It depends on what you want it to do. However...
This seems like a good target for HID-BPF. One of the use case I have is to implement a firewall for HID devices.
With the BPF target in sight, I would rather implement that as a BPF program than a kernel driver feature. For the simple reason that with BPF will be fully controlled from userspace. So if we need to disable the HID firewall on that device while fwupd is doing an update, we can easily do so. Without talking about BPF (and I confess without having read the entire thread), my personal opinion is that we will eventually end up in a situation where we allow regular applications to access joysticks/gamepads. I am sad that we put so much effort in the kernel to support those device and see that SDL, steam and others are just using plain hidraw, because it's portable. So even if we don't like it, there will be a time where users will just put rules in udev to allow their games to work, without using inputfd. So if we can get this in systemd and also work on the firewall from above (being BPF or regular kernel work), that would still be a huge step forward. |
Doing session switching might be hard if HID stuff can be stateful? i have no clue about input devices, but can't you upload bits into the controller and then use it? i.e. motion feedback effects or so? but fi you do that, the session switching doesn't work. |
So, I wonder if the whole approach with uaccess for these types of devices is wrong. Instead maybe we should add a bus API call to acquire these devices, and then bind polkit permission checking to it, and maintain a clear ownership concept, so that one user owns a device in full until it gives it up, without session switching. This would then mean processing by the regular input layer would have to be turned off fully for these devices until the devices are given up (dunno, maybe hidraw already works like that?) This would then mean that games would have to issue one dbus call, that hands them back the open fd, and this would potentially involve polkit auth. Or in other words, I am much more open to give unpriv clients elevated access to specific hw if this is done through an explicit request to acquire access instead of will-nilly as a side-effect though ACLs. |
For Steam we do need multiple processes accessing the device simultaneously. This allows Steam and games to interpret the controller at the same time, and works on Windows and macOS, for example. |
how does that work given they will step on each other's toes then? |
(but note that i'd actually be fine to allow opening the device multiple times — i want ownership per user, that's all. how often the same user opens the device in parallel doesn't really matter. What matters is that if user "lennart" owns it, user "sam" can't have it all regardless how often they switch it, until user "lennart" closes the last fd he has to the device) |
There is two sorts of state here. The long-lived, configuration state, for example RGB backlight control on a keyboard, DPI settings on a mouse, etc. Then there is short-lived state like force feedback in a racing wheel or rumble in a game controller.
Long-lived state isn't an immediate problem like that but it would be great if we could filter out anything that's modifying long-lived state for some FDs so we could just pass them through to any client. Access to FDs without this filter could then be a privileged operation mediated by x-d-p. |
Without knowing how to reset every single device to a neutral state, is this something that can be fixed? And wouldn't you get the same problem if you game (that holds the /dev/hidraw fd) crashes after uploading force feedback? |
I don't think so and I also don't think this should be a blocker. It would be nice if systemd could attach a BPF program to all devices where we know how to reset the short-lived state though which would be called on HIDIOCREVOKE.
Yes, absolutely. I would not expect that we will pass any HID device through to applications though, only the ones we know are safe to do (i.e. they reset their short-lived state on HIDIOCREVOKE and long-lived state can't be modified) whereas I would expect all devices to be accessible from logind. |
hmm, if these are usb devices we could power them down and back up on session switches, no? i think we can trigger that via sysfs somehow? would that work? there might be the chance they reset their state if told to power down? does that make any sense? too ugly? |
maybe the "unbind" stuff would already suffice? i.e. unbind the usbhid stuff from the device temporarily? |
For the archives, #23140 is the draft MR to add |
btw, where are we with this? |
I'm still at "I'm getting mixed messages about what the systemd team wants", which means I don't know how to proceed towards something that could be accepted. There are a few things that could be useful directions for this, if they'd be accepted. One is to solve #17691:
Another is to head towards solving #22681:
|
Another thing that has come up while discussing this topic is that we have an We could benefit from the kernel gaining an With hindsight, I think this is a design flaw of the way evdev devices work: a lot of axes get reused for multiple purposes (like ABS_X being touchpad/touchscreen horizontal touch position, gamepad left stick horizontal angle, flight stick horizontal angle, steering wheel rotation, accelerometer horizontal acceleration and so on), and the kernel-side input drivers are aware of which one they are reporting, but don't have any unambiguous way to tell user-space which sort of device this is. In USB/Bluetooth HID, as far as I'm aware, each of those multiple purposes is labelled differently.
For #22681, as noted in #22860 (comment) we might well need a hwdb as an override even if most controllers can be auto-detected by parsing the HID descriptor, because some gaming input devices are just weird.
If the route that includes this is likely to be accepted, I'll need some time to learn how to parse HID descriptors, and collect sample data that I can test against. If it's not going to be accepted whatever I do, then obviously I'd prefer not to invest that time. |
it appears that adding ID_GAME_CONTROLLER definitely makes sense (and should be uncontrovesial?), independently of how we intend to use it in the end, i.e. by opening it up via uaccess, via logind, entirely or whatever. I guess it would be beneficial that 3rd party projects that open up these devices via udev rules would at least be able to do so via a generic prop we provide. (and no i don't thinkg ID_HIDRAW_GAME_CONTROLLER makes sense. rules can easily check the device type themselves, there's no need to replicate this in the prop name) |
the other question is how to give users/apps access to these devices. It appears to me in the long run the goal should be to have logind manage them though, like other input devices. For that we could open up the evdev stuff (which should already work, no?), and it should be fairly robust, because of the revoke logic. That should already just work, or at least be reasonably low-hanging fruit. We could sooner or later also open up hidraw devices like that, but i guess that's only safe if we provide a "firewall" of kinds via bpf on those. Moreover, we need a revoke concept, and a way to reset the short-lived state of those devices. At least in the beginning we could simply powerdown the devices and up again via those sysfs interfaces when we switch devices, no? in the longer run maybe a better api materializes? (Note that we currently open up fido2 hidraw devices via uaccess already, as we do for certain A/V production devices. Now I wonder if that was a good idea, and if we should have gone the same way there...) |
this is what we do for FIDO devices. If this can be done reliably I would like to see this approach taken, by adding this to input_id. hwdb is great and all, but explicit detection is always better if there's a generic way to do so. |
so, to summarize:
if unbind/bind works for devices that are in use, then item 4 might not be necessary, as we could then revoke the device that way... Does that make any sense? what's the status on the kernel side for hidraw device revoke and hidraw bpf? |
The other day I was asked on the LKML what could be done for hidraw devices. It turns out that with HID-BPF (and a couple of kernel patches that I have yet to send) we can:
This should allow for some "safer" use of hidraw but still doesn't help with the revoke FWIW.
Gvinig FIDO2 hidraw uaccess is fine in 90% of the cases. However a bad actor can open the hidraw node to sniff the non FIDO2 input keys emitted by the key when you press on the button. So long term, not relying on uaccess for those will be best
HID-BPF made it in kernel v6.3 It has a couple of restrictions that prevent the firewall use case:
@whot started working on the revoke hidraw part, and it went nowhere because we had no plan on the userspace side. If you are generally happy with HID-BPF and its use cases, I think I should be able to push forward with the missing bits, bits that are not just about fixing a device. |
i am a fan of bpf and we use it all over the place in systemd already. libbpf would not even be a new dep for us hence, and we could reuse some glue code we already have in place. so from my side hid bpf filtering sounds great. |
Which other input devices? There are two completely different models for input devices right now:
If it's acceptable for my PS4 controller to report button presses and joystick movements to any process running as a uid with access granted via uaccess, then presumably it ought to also be acceptable for it to report motion events (accelerometer and gyro) to those same processes, which was my motivation for using uaccess to solve #17691 and similar. Some game engines use a middleware library like SDL which can (at least potentially) be adapted to OS-level changes and upgraded over time, although quite a lot of games bundle or statically link an older SDL, and the developers of these libraries are likely to be unimpressed if a straightforward way to enable these devices gets blocked by waiting for a hypothetical future approach that never arrives. In particular, if the future approach is "plumb something through the Wayland compositor", then that's never going to work for Xorg users. Other game engines (for example Godot) implement evdev access directly, so upgrading libraries like SDL will never fix them: either direct evdev access works, or those game engines don't work. |
OK, so adding ID_GAME_CONTROLLER as a declarative property on evdev and hidraw devices seems like it would be a useful step towards making this better. I can try to do that. The design I have in mind for that is that if a single physical device exposes multiple device nodes, I would expect all of them to be ID_GAME_CONTROLLER, even the ones that are not "joysticks". So plugging in a PS4 controller (which is a complex device with two sticks, 15+ buttons, motion controls and a trackpad) should produce these device nodes:
So ID_GAME_CONTROLLER means "this is part of a game controller", and every JOYSTICK is by definition a GAME_CONTROLLER, but not every GAME_CONTROLLER is a JOYSTICK. Valid? |
Thumbs up for me for this (well, the whole outline from #22860 (comment)) |
I was suggesting the way how mice/kbd are handled. I am aware though that this would break existing behaviour of course. |
Just to mention this expicitly: It's fine to set ID_GAME_CONTROLLER relatively agressively, i.e. on all subdevices of a relevant USB device. We expect that people filter by device type and this property anyway. |
yeah |
I think we all agree that removing uaccess on evdev joysticks/game controllers would be a bad idea because it would break backwards compatibility but I also think that we all agree that ideally they should be handled like keyboards and mice currently are. At least for hidraw devices there is no backwards compatibility to be concerned about so plumbing them through In the longer term I also think that we might be able to remove the evdev joysticks/game controllers |
So yeah, breaking compat with taking away uaccess is of course problematic. Now, I don't think "emulating" direct joystick access via seccomp-notify or so from logind makes much sense – but I do think it might make a lot more sense to add something like that to execution sandboxes such as flatpak, which is probably where games and apps are more likely ending up in anyway. i.e. if flatpak would use seccomp-notify to hook into any attempts to open joystick devices, and then redirect that to logind instead, and pass back the "managed" fd that logind controls, then things would be pretty good. |
Flatpak already does not mount There is currently no support for a narrower-scoped Games and game-adjacent software that want to access input devices at a lower level use This is something that games and game middleware libraries already do, even though it requires non-default permissions configuration. Games cannot use a Wayland or D-Bus-based input device portal until it exists and is widespread. |
input-id: Make ID_INPUT_JOYSTICK imply ID_GAME_CONTROLLER
ID_INPUT_JOYSTICK refers to "joysticks", which for historical reasons is
the name given by the Linux kernel and udev to most game-oriented input
devices, including flight sticks, gamepads, steering wheels and so on
(but not accelerometers or gyroscopes that have axes but no buttons,
even if they are part of a gamepad with motion controls such as a
PS3/PS4/PS5 controller).
My intention is for ID_GAME_CONTROLLER to be a generalization of this,
applying to anything that a gamer would think of as a dedicated gaming
controller, for example:
controller, which is part of the same HID device but exposed as a
separate evdev device node by the Linux kernel (see Dualshock/Sixaxis gyroscope input device is not accessible to the user #17691)
be used for head tracking by Elite:Dangerous players (see
EDTracker USB analog joystick is not detected (as a controller) ValveSoftware/steam-for-linux#8443)
However, ID_GAME_CONTROLLER should not include accelerometers used to
detect the orientation of a laptop/tablet, because those are not really a
gaming device (admittedly people have sometimes hooked up older Thinkpads'
HDAPS accelerometers to control games by tilting the laptop, but that
seems like something that's reasonable to have to configure for yourself).
I've named this without the INPUT_ prefix with the intention that it
can be applied to both evdev devices and hidraw devices (RFE: uaccess on game controllers' raw HID interfaces #22681).
input-id: Mark known HID game controllers with ID_GAME_CONTROLLER
The initial hwdb is taken from steam-devices, which is mostly a list
of gamepads suitable for use with Steam's Steam Input input-remapping
features, but it can be expanded later to include non-gamepads.
Many of these controllers can be connected either via Bluetooth or with
a USB cable, with the same vendor/product ID either way; for those, make
the bus part of the modalias match both. A few devices have a different
product ID when connected via USB or via Bluetooth, and some devices
have an optional wireless dongle for a non-standard (non-Bluetooth)
wireless protocol, which shows up as a distinct USB product ID; list
those dongles separately.
Being a game controller is really a fact about the physical device rather
than a fact about a specific kernel interface (evdev and/or raw HID),
so match against the HID parent device's modalias. This lets us avoid
the differences in syntax between USB and Bluetooth devices, and between
evdev and raw HID "views" of the same device.
The steam-devices udev rules match all HID devices under Valve's vendor
ID, regardless of product ID, but for this commit I've only included
devices that correspond to the publically available (and now discontinued)
Steam Controller, with commented-out entries for other Steam Controller
versions that are listed in SDL source code. Additional devices can be
added or uncommented later if desired.
Detection of "joysticks" via input_id's heuristics (based on counting
buttons and axes) continues to be necessary for non-HID game controllers,
in particular Xbox gamepads via the xpad driver and legacy MIDI-port
joysticks.
uaccess: Put uaccess on all evdev interfaces of game controllers
The subset of these that had ID_INPUT_JOYSTICK were already
user-accessible, but now that we are marking HID game controllers as
such, we can extend that to things like the motion controls integrated
into the Playstation 3, 4 and 5 controllers.
Resolves: Dualshock/Sixaxis gyroscope input device is not accessible to the user #17691
uaccess: Put uaccess on all raw HID interfaces of game controllers
The usual way to communicate with game controllers on Linux is the
semi-high-level evdev interface, but games and game-related software
sometimes benefit from lower-level access to the raw HID device nodes.
This is particularly desirable for Wine, because the lower-level Windows
APIs for game controllers are analogous to Linux hidraw device nodes,
and Windows games make active use of this. If raw HID access is
available for a HID controller like a Playstation gamepad, then Wine can
pass it through as-is, and games that work with a particular controller
on Windows will generally work on Linux too; if only the semi-high-level
evdev interface is available, then Wine has to fake up a HID device
the information provided by evdev, which involves some information loss
and can result in breaking the game's assumptions (for example putting
joystick axes in the "wrong" order could break a game that "knows" that
axes 2 and 3 are the right stick).
Raw HID access can also provide controller-specific functions, like
control over the lightbar on a Playstation gamepad, and similar
device-specific functionality of Steam Controller gamepads. Some of
these are also exposed via Linux-specific kernel APIs, but games and
their middleware libraries that want to manipulate this sort of thing
will generally need to know how to do it via raw HID anyway (otherwise
they wouldn't work on Windows and macOS), so it's natural to want to
reuse the same tested code on Linux.
It is important that we do not expose raw HID access to all HID
devices, because that would be enough to log keyboard inputs.
Resolves: RFE: uaccess on game controllers' raw HID interfaces #22681