895f57c63a
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>
124 lines
4.4 KiB
Markdown
124 lines
4.4 KiB
Markdown
# 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)
|
|
|
|
```c
|
|
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](../../daedalus-fourier/memory/feedback_correctness_before_speed.md).
|
|
|
|
## 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.
|