Skip to content
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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

smcv
Copy link
Contributor

@smcv smcv commented Mar 24, 2022

  • 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:

    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

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>
@smcv
Copy link
Contributor Author

smcv commented Mar 24, 2022

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?

@smcv
Copy link
Contributor Author

smcv commented Mar 24, 2022

ID_GAME_CONTROLLER as the generic term for devices intended to control games is slightly unfortunate, because it conflicts with the terminology used in SDL: in SDL, every game input device is a "joystick", and the subset of "joysticks" that look like an Xbox/Playstation gamepad are "game controllers". However, I can't think of a better name for these.

Suggestions welcome for a better name that encompasses joysticks, gamepads, steering wheels, accelerometers, gyroscopes, Guitar Hero guitars and so on...

@smcv
Copy link
Contributor Author

smcv commented Mar 24, 2022

I haven't (yet) attempted to parse the HID descriptors like fido_id does, but even if I implement that, I suspect we'd want a hwdb as a fallback; and we'd need the ID_GAME_CONTROLLER part of this PR in any case. So this is hopefully a step in the right direction, even if parsing the HID descriptor might give us more coverage.

@smcv
Copy link
Contributor Author

smcv commented Mar 24, 2022

An open question: steam-devices also sets up raw HID access to an assortment of VR devices (Valve Index and others). Should these be treated as in-scope for ID_GAME_CONTROLLER, or would they make more sense as something like ID_VIRTUAL_REALITY?

@medhefgo medhefgo added the hwdb label Mar 24, 2022
@@ -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"
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think so.

Copy link
Member

@keszybz keszybz Apr 6, 2022

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"

Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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?)

Copy link
Contributor

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.

@smcv
Copy link
Contributor Author

smcv commented Mar 24, 2022

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.

@poettering
Copy link
Member

I don't really have an intuition for how "expensive" these lookups are.

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 70-camera.rules is handling this.

@bluca bluca requested review from hadess and whot March 24, 2022 23:10
@hadess
Copy link
Contributor

hadess commented Mar 25, 2022

input-id: Make ID_INPUT_JOYSTICK imply ID_GAME_CONTROLLER

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.

input-id: Mark known HID game controllers with ID_GAME_CONTROLLER

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.

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.

There aren't any "MIDI-port joysticks". There's MIDI devices connecting to a gameport.

uaccess: Put uaccess on all evdev interfaces of game controllers

This is fine.

uaccess: Put uaccess on all raw HID interfaces of game controllers

This isn't good at all.

There's no equivalent of EVIOCREVOKE for hidraw devices, so there's no loss of access on that device when switching users, or when the user logs out, which makes eavesdropping possible.

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.

@swick
Copy link
Contributor

swick commented Apr 5, 2022

uaccess: Put uaccess on all evdev interfaces of game controllers

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
Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#22990

@keszybz
Copy link
Member

keszybz commented Apr 6, 2022

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.

@smcv
Copy link
Contributor Author

smcv commented Apr 6, 2022

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.

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

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 input_id that can walk up the tree until it reaches a HID ancestor, then iterate through its other evdev descendants and see whether any of them look like JOYSTICKs. Is that OK? Or is there a declarative approach that I'm not seeing?

This could resolve #17691, but would not do anything to help #22681, unless we ran imperative C code similar to input_id for each hidraw device.

There's no equivalent of EVIOCREVOKE for hidraw devices, so there's no loss of access on that device when switching users, or when the user logs out, which makes eavesdropping possible.

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.

input-id: Make ID_INPUT_JOYSTICK imply ID_GAME_CONTROLLER

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.

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 hidraw device to its HID ancestor, then enumerates evdev descendants to see whether any of them would have had ID_INPUT_JOYSTICK (which there was concern about on #22681).

This also means that every single device that offers a firmware update interface on top of a HID API will be at risk there.

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.

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

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.

@swick
Copy link
Contributor

swick commented Apr 6, 2022

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.

@swick
Copy link
Contributor

swick commented Apr 6, 2022

@bentiss is that something HID-BPF could be used for?

@bentiss
Copy link
Contributor

bentiss commented Apr 6, 2022

@bentiss is that something HID-BPF could be used for?

It depends on what you want it to do.
HID-BPF will have full access of the device, and you need CAP_SYSADMIN to be able to load a BPF program into HID.

However...

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.

This seems like a good target for HID-BPF. One of the use case I have is to implement a firewall for HID devices.
So in the Playstation controller, we could open the hidraw interface to users, and block any attempt to talk to the firmware update endpoint.
For persistent configuration, well, it makes sense to allow some configuration, but BPF having knowledge to all HID communications, we could also imagine a program that stores the current configuration and resets it once the hidraw users ends its work by closing the fd.

And to be honest, getting the kernel to create such a restricted HID device doesn't sound impossible.

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.

@poettering
Copy link
Member

And to be honest, getting the kernel to create such a restricted HID device doesn't sound impossible.

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.

@poettering
Copy link
Member

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.

@slouken
Copy link

slouken commented Apr 7, 2022

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.

@poettering
Copy link
Member

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?

@poettering
Copy link
Member

(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)

@swick
Copy link
Contributor

swick commented Apr 12, 2022

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.

HIDIOCREVOKE without clearing the short-lived state seems horrible. Switch to a tty, session crashes and your controller doesn't stop vibrating on your desk and your gaming wheel doesn't stop turning left.

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.

@whot
Copy link
Contributor

whot commented Apr 12, 2022

HIDIOCREVOKE without clearing the short-lived state seems horrible. Switch to a tty, session crashes and your controller doesn't stop vibrating on your desk and your gaming wheel doesn't stop turning left.

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?

@swick
Copy link
Contributor

swick commented Apr 13, 2022

Without knowing how to reset every single device to a neutral state, is this something that can be fixed?

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.

And wouldn't you get the same problem if you game (that holds the /dev/hidraw fd) crashes after uploading force feedback?

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.

@poettering
Copy link
Member

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?

@poettering
Copy link
Member

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?

@whot
Copy link
Contributor

whot commented Apr 26, 2022

For the archives, #23140 is the draft MR to add HIDIOCREVOKE support. This is complimentary to this PR here and there's some discussion on how a BPF-based approach (in addition to the ioctl) may make things more flexible, in particular #23140 (comment) and the few comments above.

@poettering
Copy link
Member

btw, where are we with this?

@smcv
Copy link
Contributor Author

smcv commented Jun 9, 2023

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:

  • introduce ID_GAME_CONTROLLER as above (or ID_HIDRAW_GAME_CONTROLLER if preferred)
  • either or both of:
    • add a hwdb with some special-cases to detect well-known HID game controllers (official Playstation series, Nintendo Wii/Switch and Steam Controller would be a good starting point, with third-party controllers added later as a follow-up) and mark them as ID_GAME_CONTROLLER
    • parse the HID descriptor and automatically put ID_GAME_CONTROLLER on anything that has usage pages that indicate a game controller
      • or as a safety-catch to prevent accidentally allowing keyloggers, perhaps on anything that has usage pages that indicate a game controller but does not have usage pages that indicate a keyboard/mouse
  • maybe put uaccess on hidraw gaming devices as the SDL developers have requested
    • some developers have asserted that this is unacceptable unless/until we have a hidraw equivalent of EVIOCREVOKE
    • ... but some other developers disagree and say my original proposal seems fine
  • or, if systemd is unwilling to have a default policy with uaccess on hidraw gaming devices, maybe a compromise position would be to only provide the mechanism that would make that convenient, but not the policy, allowing sysadmins to opt-in to putting uaccess on these devices (either directly or by installing something like steam-devices)

@smcv
Copy link
Contributor Author

smcv commented Jun 9, 2023

Another thing that has come up while discussing this topic is that we have an INPUT_PROP_ACCELEROMETER that positively identifies a device as an accelerometer, but there is nothing that positively identifies a device as a "joystick" (including flight sticks, gamepads, driving simulator controllers and so on). So, if we see a device with XYZ absolute axes, no buttons, and INPUT_PROP_ACCELEROMETER, then we know it's definitely an accelerometer; but if we see a device with XYZ axes, no buttons, and no special properties, then we cannot tell (without looking up the vendor/product IDs in a hwdb) whether that's an accelerometer that has not been identified as such because the kernel or driver is outdated, or a set of three driving simulator pedals. At the moment, udev assumes it's an accelerometer.

We could benefit from the kernel gaining an INPUT_PROP_JOYSTICK that positively identifies joysticks so that udev's heuristic based on counting axes and buttons won't mis-identify them as accelerometers. I'm not a kernel developer, but I've asked colleagues who are to look into the possibility of adding this.

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.

add a hwdb with some special-cases to detect well-known HID game controllers

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.

parse the HID descriptor and automatically put ID_GAME_CONTROLLER on anything that has usage pages that indicate a game controller

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.

@poettering
Copy link
Member

poettering commented Jun 9, 2023

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)

@poettering
Copy link
Member

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...)

@poettering
Copy link
Member

  • parse the HID descriptor and automatically put ID_GAME_CONTROLLER on anything that has usage pages that indicate a game controller

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.

@poettering
Copy link
Member

poettering commented Jun 9, 2023

so, to summarize:

  1. add ID_GAME_CONTROLLER=1, ideally via explicit detection in input_id. if this doesn't work for all devices (or for none), then also implement the same via hwdb. (only use hwdb devices where input_id doesn't suffice).
  2. Make sure that ID_GAME_CONTROLLER=1 evdev devices work via logind's TakeDevice, i.e. are marked for seat usage and stuff
  3. make sure hidraw bpf is a thing for "firewalling"
  4. make sure revoking hid devices is a thing
  5. teach logind's TakeDevice() hidraw devices, too, with both hid bpf and revoking enforced, plus unbind/bind on session switches (at least for now)

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?

@bentiss
Copy link
Contributor

bentiss commented Jun 9, 2023

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?

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:

  • detect when a device is opened in hidraw mode
  • prevent "normal" events to be forwarded to the evdev nodes when hidraw is opened

This should allow for some "safer" use of hidraw but still doesn't help with the revoke FWIW.

(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...)

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

what's the status on the kernel side for hidraw device revoke and hidraw bpf?

HID-BPF made it in kernel v6.3

It has a couple of restrictions that prevent the firewall use case:

  • it currently only allow to filter/change events emitted by the device to the userspace (primary use case is to fix events coming from a device)
  • in the PS4 example on the LKML I realized that the kernel header doesn't allow for an introspection of the hidraw node state. But that one could easily be backported to 6.3.x IMO

@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.

@poettering
Copy link
Member

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.

@smcv
Copy link
Contributor Author

smcv commented Jun 9, 2023

It appears to me in the long run the goal should be to have logind manage them though, like other input devices

Which other input devices? There are two completely different models for input devices right now:

  • Keyboards and mouse-like devices (including trackpads and so on) cannot normally be opened by unprivileged users. Traditionally, Xorg ran as root and opened them that way. Now, Wayland compositors (or Xorg) have to ask logind to open them and provide a fd, which logind can revoke when we switch between VTs. Unprivileged user programs get their keyboard and mouse events from the Wayland compositor or the X server.

  • Joysticks (including flight sticks, steering wheels, gamepads, Guitar Hero guitars, etc.) can normally be opened by unprivileged programs. Traditionally, this worked by adding users to a group, but that was bad because it gave local and remote users the same level of access, so now it's done with uaccess ACLs instead. Unprivileged user programs like game engines open the evdev device node directly. Game engines, and game middleware libraries like SDL, expect to be able to do that, and preventing it would be a backwards-compatibility break.

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.

@smcv
Copy link
Contributor Author

smcv commented Jun 9, 2023

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.

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:

  • hidraw7 (or whatever): raw HID, ID_GAME_CONTROLLER
  • input20 (or whatever): evdev representation of its joysticks and buttons, ID_GAME_CONTROLLER + ID_INPUT_JOYSTICK
  • input21: evdev representation of its built-in accelerometer and gyroscope, ID_GAME_CONTROLLER + ID_INPUT_ACCELEROMETER
  • input22: evdev representation of its built-in trackpad, ID_GAME_CONTROLLER + ID_INPUT_TOUCHPAD
  • js0: legacy representation of joysticks and buttons, ID_GAME_CONTROLLER + ID_INPUT_JOYSTICK

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?

@whot
Copy link
Contributor

whot commented Jun 12, 2023

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))

@poettering
Copy link
Member

Which other input devices? There are two completely different models for input devices right now:

I was suggesting the way how mice/kbd are handled. I am aware though that this would break existing behaviour of course.

@poettering
Copy link
Member

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.

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.

@poettering
Copy link
Member

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?

yeah

@swick
Copy link
Contributor

swick commented Jun 12, 2023

Unprivileged user programs like game engines open the evdev device node directly. Game engines, and game middleware libraries like SDL, expect to be able to do that, and preventing it would be a backwards-compatibility break.

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 TakeDevice and not making them uaccess would be the right solution.

In the longer term I also think that we might be able to remove the evdev joysticks/game controllers uaccess. We could start by not mounting /dev/input in flatpak by default to push people to implement inputfd and then eventually provide some wrappers for old apps which can't be ported.

@poettering
Copy link
Member

poettering commented Jun 12, 2023

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.

@smcv
Copy link
Contributor Author

smcv commented Jun 13, 2023

We could start by not mounting /dev/input in flatpak by default

Flatpak already does not mount /dev/input by default: the default is a minimal /dev, and apps wanting other device nodes (including DRI/DRM!) need to ask for them. Games and game-adjacent software that need input device access currently use --device=all, but I think it would be very misleading for that to have a result that is not "all the device nodes accessible by the user".

There is currently no support for a narrower-scoped --device=input (analogous to --device=dri and --device-kvm, which do exist). If it existed, it would presumably bind-mount /dev/input.

Games and game-adjacent software that want to access input devices at a lower level use /dev/hidraw*. Because those aren't in a subdirectory, it is not possible for Flatpak to mount those at a finer granularity than --device=all without breaking the ability to hot-plug game controllers (it could bind-mount the individual device nodes that happen to have existed at app startup, but that would prevent access to newly-hotplugged controllers).

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
9 participants