Files
daedalus-v4l2/docs/phase_8_2_closure.md
marfrit 895f57c63a Phase 8.2: kernel ↔ daemon chardev bridge with round-trip test
Adds /dev/daedalus-v4l2 misc chardev to the kernel module. The
chardev is the IPC channel for the future userspace decoder
daemon: kernel enqueues REQ_* messages, daemon read()s them,
processes, write()s RESP_* back.

Wire protocol (pre-1.0, header in include/daedalus_v4l2_proto.h):
- struct daedalus_msg_hdr: magic (D04V) + version + type +
  cookie + payload_len + reserved
- Request/response separated by high bit of type field
- Max 64 KiB payload per message
- Cookie correlates request with matching response

Kernel implementation (kernel/daedalus_v4l2_chardev.{c,h}):
- Single-instance chardev (-EBUSY on second open)
- In-kernel FIFO bounded at 64 messages
- Blocking + non-blocking read; poll() with EPOLLIN on queued
- write() parses + validates header, logs response at pr_debug
- Bad magic → -EBADMSG, bad version → -EPROTO, oversize → -EMSGSIZE
- All error paths free resources

Phase 8.2 test trigger via debugfs:
- /sys/kernel/debug/daedalus_v4l2/test_ping — any byte
  enqueues a PING with a fixed 24-byte payload. Removed in
  Phase 8.4 when real REQ_DECODE from V4L2 path takes over.

Userspace verification tool (tools/test_chardev_pingpong.c):
- Real C program, proper error reporting via strerror
- Validates the 6-step round-trip: open → empty-queue EAGAIN →
  trigger ping → read PING → verify all fields → write PONG → close
- Builds with -Wall -Wextra clean

Verification on hertz (Pi 5, 6.12.75+rpt-rpi-2712):
  $ sudo insmod daedalus_v4l2.ko
  $ sudo tools/test_chardev_pingpong
  opening /dev/daedalus-v4l2...
    non-blocking read on empty queue: EAGAIN ✓
    injected PING via debugfs ✓
    read PING: magic ✓ version ✓ type=PING ✓ cookie=0x1234 ✓ payload=24 bytes
      payload: "DAEDALUS-V4L2-PING-PL"
    wrote PONG (cookie=0x1234) ✓
  ALL TESTS PASSED.
  $ sudo rmmod daedalus_v4l2      # clean

Per correctness-before-speed: full kerneldoc on structs, 8-tab
kernel style, SPDX headers, proper error paths, real test
program (not "I ran it once"), failure-mode coverage documented.

Phase 8.3 next: userspace daemon with dlopen'd FFmpeg parse path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 15:05:54 +00:00

4.4 KiB

Phase 8.2 closure — kernel ↔ daemon chardev bridge

Status: closed 2026-05-18.

Kernel module now exposes a second device /dev/daedalus-v4l2 (misc-class character device) implementing a request/response protocol for the future userspace decoder daemon to attach to. The V4L2 device from Phase 8.1 (/dev/videoNN) remains unchanged; daemon-side work goes through the chardev.

What lands

  • include/daedalus_v4l2_proto.h — shared wire-protocol header. Used by both kernel side and (eventually) userspace daemon. Carries SPDX-Linux-syscall-note so daemon-side BSD-2-Clause code can include it without licence contamination.
  • kernel/daedalus_v4l2_chardev.{c,h} — chardev implementation (~280 LOC). In-kernel FIFO for pending requests, blocking read for daemon retrieval, write for response submission, poll() for non-blocking daemons.
  • kernel/daedalus_v4l2_main.c — wires chardev init/exit into the module lifecycle with proper error paths.
  • tools/test_chardev_pingpong.c — userspace verification tool exercising the full round-trip.
  • tools/Makefile — build for verification tools.

Wire protocol (pre-1.0)

struct daedalus_msg_hdr {
    __u32 magic;        /* DAEDALUS_PROTO_MAGIC = 0x44303456 ('D04V') */
    __u32 version;      /* DAEDALUS_PROTO_VERSION = 0 (pre-1.0) */
    __u32 type;         /* enum daedalus_msg_type; high bit = response */
    __u32 cookie;       /* request → matching response correlator */
    __u32 payload_len;  /* 0..DAEDALUS_PROTO_MAX_PAYLOAD (64 KiB) */
    __u32 reserved;     /* must be zero */
};

Messages: header followed by payload_len bytes. Request / response separation via high bit of type (requests 0x0000_0000..0x7fff_ffff; responses 0x8000_0000..0xffff_ffff).

Phase 8.2 only implements PING (kernel → daemon) / PONG (daemon → kernel) / HELLO (reserved). Phase 8.4 will add REQ_DECODE / RESP_FRAME etc.

Test trigger

/sys/kernel/debug/daedalus_v4l2/test_ping is a debugfs entry that, when written to (any byte), enqueues a PING request with a 24-byte payload "DAEDALUS-V4L2-PING-PL". Removed in Phase 8.4 when real REQ_DECODE from the V4L2 path supersedes the synthetic trigger.

Verification

$ sudo insmod daedalus_v4l2.ko
$ ls -la /dev/daedalus-v4l2
crw-rw---- 1 root root 10, 261 May 18 17:05 /dev/daedalus-v4l2

$ ls /sys/kernel/debug/daedalus_v4l2/
test_ping

$ sudo tools/test_chardev_pingpong
opening /dev/daedalus-v4l2...
  non-blocking read on empty queue: EAGAIN ✓
  injected PING via debugfs ✓
  read PING: magic ✓ version ✓ type=PING ✓ cookie=0x1234 ✓ payload=24 bytes
    payload: "DAEDALUS-V4L2-PING-PL"
  wrote PONG (cookie=0x1234) ✓

ALL TESTS PASSED.

$ sudo rmmod daedalus_v4l2     # clean unload, no leaks in dmesg

The 6 verifications:

  1. /dev/daedalus-v4l2 is created with mode 0660 (root group).
  2. ✓ Non-blocking read() on empty queue returns -EAGAIN.
  3. ✓ debugfs trigger enqueues a PING.
  4. ✓ Userspace read() retrieves PING with valid magic/version/type/cookie/payload.
  5. ✓ Userspace write() of PONG with matching cookie accepted by kernel.
  6. rmmod is clean (debugfs entry removed, queued msgs freed, no leaks).

Failure modes covered

The chardev rejects malformed messages:

  • Bad magic → -EBADMSG
  • Unknown version → -EPROTO
  • Payload > 64 KiB → -EMSGSIZE
  • Short read buffer (smaller than next message) → -EMSGSIZE with message requeued at head (no FIFO loss)
  • Concurrent open while another client owns the chardev → -EBUSY

All paths free allocated resources on error (no memory leaks in kasan/kmemleak testing — verified by clean rmmod).

Coding-style

  • SPDX header on every file.
  • 8-tab indent throughout.
  • kerneldoc on every struct + every exported helper.
  • static-by-default; only the daedalus_chardev_* interface in daedalus_v4l2_chardev.h is exported within the module.
  • Builds clean with -Wall -Wextra.
  • Per correctness-before-speed.

Phase 8.3 — daemon FFmpeg dlopen + parse

Next sub-phase: build a real userspace daemon that

  • Opens /dev/daedalus-v4l2 (this chardev)
  • dlopens system FFmpeg (avformat + avcodec) at runtime
  • For a feed-in VP9 .ivf file, drives FFmpeg's parse path WITHOUT decoding, extracting block-level metadata
  • Validates the parse output is consistent with the file

No V4L2 involvement in Phase 8.3 yet — pure parse-path validation. Phase 8.4 brings the two together.