- Shell 56.2%
- C 41.7%
- Makefile 2.1%
|
All checks were successful
CI / build (push) Successful in 8s
xpad360 is a runtime dependency of gadgetd, which spawns one process per seat pad via --xpad360 /usr/local/bin/xpad360. It had neither a build system nor CI: the build was a gcc line in a README comment, so the binary in production was traceable to no commit and checked by nothing. splitux-together v0.13.0 closed that same gap for gadgetd and tightened the coupling between the two, which left this the weak link. Makefile carries the three helper builds that were prose. `make check` rebuilds with -Werror -Wextra, which they pass today; that is worth blocking on here because these drive raw_gadget and dummy_hcd, where a sloppy pointer or a truncated read wedges the host rather than returning a wrong answer. CI builds under those flags, asserts the outputs are real ELFs, and parses the shell scripts. It does NOT try to bind a gadget: that needs dummy_hcd, /dev/raw-gadget and root, none of which exist in a build container. A tag now produces an artifact so a deployed xpad360 traces to a commit. Runner: label xpad-gadget-ci, an ephemeral podman poller on the lab following the fleet convention (deps baked into the image, no `container:` directive). |
||
|---|---|---|
| .github/workflows | ||
| vm | ||
| .gitignore | ||
| eps_dump.c | ||
| evread.c | ||
| gadget-slam.sh | ||
| Makefile | ||
| README.md | ||
| smoke-feed.sh | ||
| smoke-host.sh | ||
| smoke-oc2-play.sh | ||
| smoke-oc2-shots.sh | ||
| smoke-oc2.sh | ||
| xpad360.c | ||
xpad-gadget — userspace USB Xbox 360 controller emulator
Presents a USB device (VID 045e / PID 028e, interface class FF / subclass
5D / proto 01) over /dev/raw-gadget bound to the dummy_hcd UDC, so the
in-kernel xpad driver binds it and creates a real USB-attached evdev node
named "Microsoft X-Box 360 pad".
Why
Overcooked 2 (Unity / old InControl) binds gamepads by name AND then NULL-deref
crashes on any virtual (uinput/uhid) controller — it only accepts a genuinely
USB-attached xpad device. splitux-together's seat pad is uinput, so OC2 can't
use it. This emulates a real USB Xbox 360 pad in userspace so xpad binds it; the
seat input is then fed in as Xbox 360 USB report packets. See memory
joydev-replacement-cuse-jsshim.md for the full root-cause history.
⚠️ Safety
raw_gadget + dummy_hcd is a kernel fuzzing-grade interface. A buggy gadget
can hard-panic the host kernel (it did once, during development — full reboot).
All development happens in a throwaway VM that boots the host CachyOS kernel,
where a panic only kills the guest. Do not iterate gadget descriptor/control-flow
changes on bare metal.
Files
xpad360.c— the emulator. Build:gcc -O2 -Wall -o xpad360 xpad360.c -lpthread -lm.--demo: auto-wiggle sticks + tap A (visual input proof).- default : neutral; drive via stdin text commands (A/B/X/Y/LB/RB/START/UP/... ,
LX <n>,LT <n>,REL,Q). --buggy: deliberately reproduce the original bare-metal failure (bad IN endpoint addr0x05+ abruptexiton ep0 error) — forensic use only.
eps_dump.c— dumps the dummy_hcd endpoint table viaEPS_INFO(debugging).vm/— the throwaway test VM harness:build.sh [init] [out.cpio.gz]— builds staticxpad360+ RAM-only initramfs from host files (busybox + the host's.komodules). No network, no disk.run.sh [serial.log] [timeout]— boots/boot/vmlinuz-linux-cachyosin KVM (-cpu host,panic=1 -no-reboot), captures serial (incl. any panic).init— default 3-pass stress test (bind / TERM / KILL / re-bind).init-forensic— reproduces the exact-71/EBUSY host signature, then recovers.init-ffstress.sh+ff_consumer.c— desktop-consumer rumble/read stress.
Status (2026-06-23)
VM-validated on kernel 7.0.12-1-cachyos: xpad binds the emulated pad (real USB
sysfs parent …/dummy_hcd.0/usb1/1-1/1-1:1.0/…), input flows (~2976 B/2s via the
demo), and it survives graceful + abrupt (kill -9) teardown, re-bind, wedged-UDC
recovery, and a force-feedback consumer hammering during teardown — no panics.
The original host crash's exact error signature was reproduced in the VM and is
survivable; the host panic itself did not reproduce (likely timing/heap or
a live-desktop interaction the headless VM lacks) → residual non-zero risk for a
bare-metal run remains.
Endpoint addressing note
dummy_hcd's EPS_INFO.addr is the bare endpoint number (direction is in
caps). Fixed-number endpoints are 1,2,5,6,7,10,11,12,15; the ep-X endpoints
(addr 0xff) take any address. We advertise IN 0x83 / OUT 0x04 (numbers 3/4,
used by no fixed endpoint) so EP_ENABLE resolves to a generic ANY endpoint with
no type ambiguity. Control OUT status stages MUST use EP0_READ (not EP0_WRITE,
which returns EBUSY) — this was half of the original bug.
Next
- Careful one-shot bare-metal validation of the known-good binary (quiet desktop
/ TTY, fresh
modprobe), confirm xpad binds on the host, then launch OC2local:gamepadagainst it and confirm it boots + reads input WITHOUT crashing. - Integrate into seat-streamer: port
VirtualGamepadto drive this raw-gadget USB device (feed seat state as Xbox 360 reports); keep kbd/mouse on uinput.