eudev starts at boot; Xorg finds its input devices through it

udevd and a one-shot trigger run from schinit, 10-schos.conf no longer lists
event0/event1, and the docs say what udev covers and what it does not.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
This commit is contained in:
Nexoniarz 2026-10-26 11:44:21 +01:00
commit b3f72cd95e
13 changed files with 169 additions and 50 deletions

View file

@ -94,6 +94,7 @@ Read in this order if you are new:
| [fat.md](fat.md) | FAT12/16/32 with long names: USB sticks, SD cards, the ESP. |
| [devices.md](devices.md) | Buses, classes, binding, and the vtables consumers talk to. |
| [usb.md](usb.md) | xHCI, the USB core, and the three class drivers. |
| [udev.md](udev.md) | eudev on SchOS: the device table, the `/sys` tree, uevents, datagram sockets. |
| [graphics.md](graphics.md) | Framebuffer, `/dev/fb0`, text rendering, the console, PSOGM and GMMOS. |
| [desktop.md](desktop.md) | Xorg on the framebuffer, Window Maker, and what they stand on. |
| [packages.md](packages.md) | The apk root: Alpine's packages, and SchOS as one of them. |

View file

@ -49,11 +49,9 @@ the real program is for.
`xterm` runs and exits, and the server stays up.
- **`/root/.xinitrc`** is `exec wmaker`. Without it xinit falls back to twm and an xterm, which
are not in the image.
- **`/etc/X11/xorg.conf.d/10-schos.conf`** names the input devices. Alpine's Xorg asks udev for
them, SchOS has no udev, and X came up with no keyboard and no mouse: the pointer did not
move and nothing could end the session. SchOS has exactly two input nodes whatever the
machine, so a fixed list is the whole answer (`AutoAddDevices` off, Ctrl+Alt+Backspace
enabled).
- **`/etc/X11/xorg.conf.d/10-schos.conf`** holds what the defaults do not give: Ctrl+Alt+Backspace
enabled, the keyboard options, and the framebuffer driver. Input devices are *found*, not
named: Xorg asks libudev, which reads what `udevd` wrote to `/run/udev` ([udev.md](udev.md)).
- **`/etc/asound.conf`** is for sound, not X ([audio.md](audio.md)).
## Using it
@ -71,8 +69,9 @@ through ACPI, and reach X as a mouse: pointer, click and tap.
- **No acceleration.** Software rendering to the framebuffer: no DRM, no glamor, no DRI, no
OpenGL beyond what software gives. GPU drivers are a plan ([../plan/gpu.md](../plan/gpu.md)).
- **No udev.** Input devices are named in a config file, not found; `/sys` has no `devices`
tree and no uevent socket.
- **udev covers the kernel's own devices only.** Input, terminals, memory devices, the
framebuffer and disks are announced; PCI and USB devices are in `/sys/bus/*/devices` but are
not yet in the `/sys/devices` tree or on the uevent socket ([udev.md](udev.md)).
- **Touchpads are mice**: no two-finger scroll, no gestures, no palm rejection.
- **No resolution change from inside X.** The mode is the console's; `schmode` changes the
`vgpud` head only, and the loader's menu is where a real panel's mode is chosen.

View file

@ -275,10 +275,15 @@ at the moment it is read, so a stick shows up in `/sys/bus/usb/devices` as soon
as it is attached. Text files report size 0, as `/proc`'s do; binary ones (the
tables, `config`) report their real length, since tools copy them by it.
What is missing, compared with Linux: `/sys/devices` and its symlinks (the
The kernel's own devices (input, tty, mem, graphics, block) have the Linux tree:
`/sys/devices/virtual/<class>/<name>` with `dev`, `uevent` and a `subsystem`
link, `/sys/class/<class>/<name>` and `/sys/dev/{char,block}/MAJ:MIN` as symlinks
into it, and input capabilities; `uevent` is writable, and a write is announced
on the uevent netlink socket ([udev.md](udev.md)).
What is missing, compared with Linux: PCI and USB devices in that tree (the
`bus/*/devices` entries are directories rather than links into it),
`/sys/class` beyond `dmi` and `power_supply`, `/sys/block`, a uevent socket (so
no udev), and anything writable. PCI `config` is
`/sys/class` entries for them, `/sys/block`, and most of what is writable. PCI `config` is
256 bytes, because config space is read through port `0xCF8`, which reaches
no further.

View file

@ -649,7 +649,11 @@ information was there and the code did not ask.
`SO_BROADCAST`, `TCP_NODELAY` reach lwIP's pcb; `SO_SNDBUF`/`SO_RCVBUF` report the
real size; `SO_PASSCRED` on Unix sockets) and refuses the rest with `ENOPROTOOPT`.
Notably absent: `SO_RCVTIMEO`/`SO_SNDTIMEO`.
- **`SOCK_DGRAM` over AF_UNIX does not exist** — stream only.
- **`SOCK_DGRAM` and `SOCK_SEQPACKET` over AF_UNIX** keep message boundaries (each message carries a
16-byte header in the same ring) but have no unconnected form: `socketpair` and listeners work,
`sendto` a path does not.
- **Netlink `NETLINK_KOBJECT_UEVENT`** is the one multicast family: sockets bound to group 1 hear the
kernel's device events, group 2 what udevd re-announces, and a datagram can be addressed to a port id.
- **No IPv6**, no firewall or packet filter of any kind, and a TCP stack that has
been used for downloads at tens of megabits and no more than that.
- **Three drivers in the kernel, one Linux driver outside it**, and the class is

82
docs/architecture/udev.md Normal file
View file

@ -0,0 +1,82 @@
# udev
Alpine's `eudev` runs on SchOS as an ordinary process, started by `schinit`
(`etc/schinit/udevd.job`, with `udev-trigger.job` replaying the devices that
were there first). It is what lets Xorg *find* its keyboard and mouse instead of
being told, and the road to a Wayland compositor starts here: libinput and
libseat both ask udev what exists.
The kernel's half is three pieces, all small, none of them specific to udev.
## The device table: `kernel/dev/kdev.c`
The devices the kernel implements itself — input event nodes, the memory
devices, terminals, the framebuffer, the disks — are listed once, with their
class, name, `/dev` name and parent. Everything below is generated from it.
The device numbers are the ones `stat()` reports for the node, not SchOS's
internal ones: `/dev/tty1` is `4:1` to a Linux program (`linux_tty_rdev` in
`kernel/sys/sys_fs.c`), and udev compares what it reads in `/sys` with what
`stat()` says about the node. `dev_stat_numbers()` is the one mapping both use.
## `/sys`: `kernel/fs/sysfs.c`
```
/sys/devices/virtual/input/input0/ name capabilities/{ev,key,rel,abs} properties
/sys/devices/virtual/input/input0/event0/ dev uevent subsystem -> ../../../../../class/input
/sys/class/input/event0 -> ../../devices/virtual/input/input0/event0
/sys/dev/char/13:64 ... -> ../../devices/virtual/...
```
Symlinks are real (`T_SYMLINK` inodes whose target is generated). `uevent` is
readable (`DEVPATH=`, `SUBSYSTEM=`, `MAJOR=`, `MINOR=`, `DEVNAME=`) and
writable: `echo add > uevent` — which is what `udevadm trigger` does — makes the
kernel announce the device again. A write to a PCI or USB device's `uevent` is
accepted and says nothing yet, because those devices are not in the
`/sys/devices` tree and an event's `DEVPATH` has to name something that exists.
The input capability files are what `input_id` reads to decide
`ID_INPUT_KEYBOARD` or `ID_INPUT_MOUSE`. SchOS has two input devices whatever
the machine — every keyboard on `event0`, every mouse on `event1`
(`kernel/dev/input.c`) — so each has a fixed, plausible capability set.
## `/dev`: `kernel/fs/devfs.c`
devtmpfs is no longer read-only: udev creates `/dev/char/MAJ:MIN`, `/dev/fd`,
`/dev/input/by-*` and the like. A small in-memory overlay (192 entries) holds
directories and symlinks, listed after the kernel's own nodes in whichever
directory they are in. Device nodes themselves are still the kernel's: `mknod`
and regular files in `/dev` are refused.
## The sockets: `kernel/net/unix.c`
- **`AF_UNIX` `SOCK_DGRAM` and `SOCK_SEQPACKET`** keep message boundaries.
udevd's control socket is `SEQPACKET`, and its manager and workers talk over
a `DGRAM` socketpair. A message carries its sender's credentials with it
(several workers share one end), a zero-length datagram is a datagram, and a
datagram socket never reads end-of-stream.
- **`AF_NETLINK` / `NETLINK_KOBJECT_UEVENT`** sockets bind to multicast groups:
group 1 is the kernel's events, group 2 is what udevd re-announces once its
rules have run. A datagram can also be addressed to a port id — udevd hands
an event to an idle worker that way, and the worker believes it only from the
manager's port. The kernel's messages arrive as credentials with uid 0, pid 0.
A queue that is full drops the message, as Linux does, and `udevadm trigger`
is how a reader recovers.
## What is not there
- PCI and USB devices in `/sys/devices`, and `add`/`remove` events from the
hotplug thread. A USB keyboard works because every keyboard is `event0`, not
because udev knew it arrived.
- `hwdb` (`/etc/udev/hwdb.bin`): `udevadm hwdb --update` has not been run in the
image, so `60-input-id` logs "returned non-zero" for the hwdb import. The
properties that matter come from `input_id`.
- Block device partitions, `by-id` / `by-uuid` links, and a `/sys/block`.
- `libinput` and `libseat` (next; see [../plan/roadmap.md](../plan/roadmap.md)).
## Testing
Boot the ISO and run `udevd --debug &`, then `udevadm trigger; udevadm settle`.
`/run/udev/data/c7:0` should contain
`ID_INPUT_KEYBOARD=1`, and `udevadm info /dev/input/event0` should print the
device. Under X, `/var/log/Xorg.0.log` says `config/udev: Adding input device`.

View file

@ -114,7 +114,7 @@ it lives.
| `mprotect` is a no-op, nothing has NX | [kernel.md](../architecture/kernel.md) |
| No routing table | first interface wins, which is a rule and not routing — [networking.md](../architecture/networking.md) |
| No descriptor garbage collector | a socket passed over itself and never received leaks the pair |
| No udev / uevent socket | Xorg is told its input devices — [desktop.md](../architecture/desktop.md) |
| udev sees only the kernel's own devices | PCI and USB are not in the `/sys/devices` tree — [udev.md](../architecture/udev.md) |
| No EHCI driver | USB 2.0 ports wired only to EHCI do nothing — [usb.md](../architecture/usb.md) |
| Touchpads are mice | no two-finger scroll — [desktop.md](../architecture/desktop.md) |
| `ru_stime` is zero | [kernel.md](../architecture/kernel.md) |

View file

@ -62,11 +62,10 @@ compositor worth running is years of work that Alpine already packages.) The des
Wayland compositor (labwc, sway, weston, a GNOME or KDE session) is the larger step. What Wayland — and a modern X — need
from the system, in the order they are useful:
1. **udev.** Today Xorg is *told* its input devices (`xorg.conf.d/10-schos.conf`) because nothing announces them. The fix is
Alpine's `eudev` (or `udev`) running on SchOS, which wants: a **uevent netlink socket** (`NETLINK_KOBJECT_UEVENT`, with the
`add`/`remove`/`change` messages and their environment), a real **`/sys/devices` tree** with `/sys/bus/*/devices/*` and
`/sys/class/*/*` as symlinks into it, `uevent` files that can be written to replay events (`udevadm trigger`), and devtmpfs
that udev can create nodes in. libinput then enumerates evdev devices through it.
1. **udev — done for the kernel's own devices** ([udev.md](../architecture/udev.md)). eudev runs from `schinit`, the uevent
netlink socket and the `/sys/devices` tree exist, devtmpfs takes udev's links, and Xorg finds its keyboard and mouse
through it. **Left:** PCI and USB devices in the same tree, with `add`/`remove` events from the hotplug thread, so a USB
keyboard or a GPU appears to udev; libinput then enumerates evdev devices through it.
2. **libinput and libseat** (seatd is in Alpine): evdev with the properties libinput reads (`ID_INPUT_*` from udev), and a way
for a non-root compositor to take the keyboard and the display from the kernel — the `VT_PROCESS` handshake exists.
3. **A DRM device.** Wayland compositors want `/dev/dri/card0` with KMS, and Mesa wants `renderD128`. SchOS has neither: it has