initial commit
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# Kernel 7.0 uinput regression: UI_DEV_SETUP required
|
||||
|
||||
## Summary
|
||||
|
||||
Kernel 7.0.0-22-generic broke the legacy uinput creation sequence.
|
||||
Sunshine's Inputtino and libevdev both use the old method and fail silently.
|
||||
|
||||
## Pre-diagnostic: is uinput built-in or a module?
|
||||
|
||||
Before running the C or Python diagnostic below, check if uinput is a loadable module or built into the kernel:
|
||||
|
||||
```bash
|
||||
modinfo uinput 2>&1 | grep filename
|
||||
# "filename: (builtin)" → uinput is compiled into the kernel
|
||||
# "filename: /lib/modules/..." → uinput is a loadable module
|
||||
|
||||
grep CONFIG_INPUT_UINPUT /boot/config-$(uname -r)
|
||||
# "CONFIG_INPUT_UINPUT=y" → built-in (no modprobe needed)
|
||||
# "CONFIG_INPUT_UINPUT=m" → module (modprobe uinput to load)
|
||||
```
|
||||
|
||||
**If built-in:** `modprobe uinput` will succeed silently but `lsmod | grep uinput` will show nothing — this is expected. The device node at `/dev/uinput` exists regardless. Permissions (user in `input` group) are what matter. Skip any module-loading troubleshooting steps.
|
||||
|
||||
**If a module:** `sudo modprobe uinput` and verify with `lsmod | grep uinput`. To persist across reboots: `echo "uinput" | sudo tee /etc/modules-load.d/uinput.conf`.
|
||||
|
||||
## Root cause
|
||||
|
||||
The uinput subsystem on kernel 7.0+ REQUIRES `UI_DEV_SETUP` ioctl before
|
||||
`UI_DEV_CREATE`. The old method (writing `struct uinput_user_dev` via write()
|
||||
or calling `UI_DEV_CREATE` without prior setup) is rejected with EINVAL (-22).
|
||||
|
||||
Sunshine v2026.516.143833 links against libevdev 1.13.6, which uses the old method.
|
||||
Inputtino (Sunshine's actual input backend, not libevdev) also uses the old method.
|
||||
|
||||
## Diagnostic (C test)
|
||||
|
||||
```c
|
||||
#include <linux/uinput.h>
|
||||
int fd = open("/dev/uinput", O_WRONLY);
|
||||
ioctl(fd, UI_DEV_CREATE); // FAILS: EINVAL on kernel 7.0
|
||||
|
||||
// Correct kernel 7.0 sequence:
|
||||
struct uinput_setup usetup;
|
||||
memset(&usetup, 0, sizeof(usetup));
|
||||
usetup.id.bustype = BUS_USB;
|
||||
strcpy(usetup.name, "test");
|
||||
ioctl(fd, UI_DEV_SETUP, &usetup); // NEW: Required on 7.0+
|
||||
ioctl(fd, UI_DEV_CREATE); // SUCCESS
|
||||
```
|
||||
|
||||
## Diagnostic (Python test)
|
||||
|
||||
```python
|
||||
import ctypes
|
||||
libevdev = ctypes.CDLL('libevdev.so.2')
|
||||
dev = libevdev.libevdev_new()
|
||||
libevdev.libevdev_set_name(dev, b'test')
|
||||
libevdev.libevdev_enable_event_type(dev, 1)
|
||||
uifd = ctypes.c_int(-1)
|
||||
ret = libevdev.libevdev_uinput_create_from_device(dev, 3, ctypes.byref(uifd))
|
||||
# Returns -25 (ENOTTY) on kernel 7.0 — uinput broken
|
||||
# Returns 0 on kernel 6.x — uinput works
|
||||
```
|
||||
|
||||
## Why the LD_PRELOAD shim may not work
|
||||
|
||||
The shim (`scripts/uinput-shim.c`) intercepts `libevdev_uinput_create_from_device`.
|
||||
However, Sunshine's PRIMARY input backend is **Inputtino** (subproject inside Sunshine
|
||||
binary), not libevdev. Inputtino creates keyboard/mouse devices through a separate
|
||||
code path that may not call through libevdev's public API. If the shim is deployed
|
||||
and input still doesn't work, Inputtino is bypassing it.
|
||||
|
||||
## Proven workarounds
|
||||
|
||||
1. **xdotool bridge** (`scripts/sunshine-input-bridge.py`) — tails Sunshine log,
|
||||
injects via xdotool XTEST. Works on any kernel.
|
||||
2. **Steam Remote Play** (`templates/remote_play.vdf`) — different protocol,
|
||||
no uinput involved.
|
||||
3. **Boot kernel 6.x** if available.
|
||||
|
||||
## Kernel version
|
||||
|
||||
Tested broken: 7.0.0-22-generic (Ubuntu 26.04)
|
||||
Tested working: Linux 6.x series
|
||||
Reference in New Issue
Block a user