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>
75 lines
2.5 KiB
C
75 lines
2.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
|
|
/*
|
|
* daedalus-v4l2 — kernel ↔ daemon wire protocol.
|
|
*
|
|
* Shared header used by both the kernel module
|
|
* (drivers/daedalus_v4l2_chardev.c) and the userspace daemon
|
|
* (daemon/src/main.c). ABI: pre-1.0 — no stability guarantees
|
|
* until DAEDALUS_PROTO_VERSION reaches 1.
|
|
*
|
|
* Transport: a single-instance chardev at /dev/daedalus-v4l2.
|
|
* The userspace daemon opens the chardev O_RDWR, then drives a
|
|
* blocking read() / write() loop:
|
|
*
|
|
* write(): submit a response to a prior request (RESP_*).
|
|
* read(): block until the next request from the kernel
|
|
* (REQ_*) is available.
|
|
*
|
|
* Each message is a `struct daedalus_msg_hdr` followed by an
|
|
* optional variable-length payload of `hdr.payload_len` bytes.
|
|
*
|
|
* Phase 8.2 (chardev bridge): only PING/PONG implemented.
|
|
* Phase 8.4 (VP9 end-to-end): adds DECODE_FRAME request,
|
|
* FRAME_READY response.
|
|
*/
|
|
#ifndef DAEDALUS_V4L2_PROTO_H
|
|
#define DAEDALUS_V4L2_PROTO_H
|
|
|
|
#include <linux/types.h>
|
|
|
|
#define DAEDALUS_PROTO_MAGIC 0x44303456u /* 'D04V' */
|
|
#define DAEDALUS_PROTO_VERSION 0u /* pre-1.0 */
|
|
|
|
/**
|
|
* enum daedalus_msg_type - wire-protocol message types
|
|
* @DAEDALUS_MSG_PING: request: payload is opaque echo data
|
|
* @DAEDALUS_MSG_PONG: response: payload echoes the matching ping
|
|
* @DAEDALUS_MSG_HELLO: response: daemon announces itself on connect
|
|
*
|
|
* Phase 8.2 implements PING / PONG / HELLO. Later phases add
|
|
* REQ_DECODE / RESP_FRAME / etc.
|
|
*
|
|
* Request types (kernel → daemon) live in 0x0000_0000..0x7fff_ffff.
|
|
* Response types (daemon → kernel) live in 0x8000_0000..0xffff_ffff.
|
|
*/
|
|
enum daedalus_msg_type {
|
|
DAEDALUS_MSG_PING = 0x00000001u,
|
|
DAEDALUS_MSG_HELLO = 0x80000001u,
|
|
DAEDALUS_MSG_PONG = 0x80000002u,
|
|
};
|
|
|
|
/**
|
|
* struct daedalus_msg_hdr - on-the-wire message header
|
|
* @magic: must be DAEDALUS_PROTO_MAGIC; rejects gibberish
|
|
* @version: protocol version (DAEDALUS_PROTO_VERSION)
|
|
* @type: one of enum daedalus_msg_type
|
|
* @cookie: caller-supplied identifier; copied verbatim into
|
|
* the matching response so the daemon can pair
|
|
* response with request
|
|
* @payload_len: number of bytes immediately following this
|
|
* struct (max DAEDALUS_PROTO_MAX_PAYLOAD)
|
|
* @reserved: must be zero for future use
|
|
*/
|
|
struct daedalus_msg_hdr {
|
|
__u32 magic;
|
|
__u32 version;
|
|
__u32 type;
|
|
__u32 cookie;
|
|
__u32 payload_len;
|
|
__u32 reserved;
|
|
};
|
|
|
|
#define DAEDALUS_PROTO_MAX_PAYLOAD (64u * 1024u) /* 64 KiB */
|
|
|
|
#endif /* DAEDALUS_V4L2_PROTO_H */
|