Files
hermes-config/skills/gaming/game-streaming/references/kernel-7-uinput-regression.md
T
2026-07-12 10:17:17 -04:00

3.2 KiB

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:

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)

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

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