/* 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): PING / PONG. * Phase 8.4 (decode end-to-end): REQ_DECODE / RESP_FRAME. */ #ifndef DAEDALUS_V4L2_PROTO_H #define DAEDALUS_V4L2_PROTO_H #include #define DAEDALUS_PROTO_MAGIC 0x44303456u /* 'D04V' */ #define DAEDALUS_PROTO_VERSION 0u /* pre-1.0 */ /* * Wire-protocol message types. * * Request types (kernel → daemon) live in 0x0000_0000..0x7fff_ffff. * Response types (daemon → kernel) live in 0x8000_0000..0xffff_ffff. * The high bit is what distinguishes "kernel produced this" from * "daemon produced this" on the wire. * * These are #defines rather than an enum because the high-bit * values (>= 0x80000000) exceed INT_MAX, and pre-C23 enums can't * portably hold them — kernel uABI headers follow the same * convention. */ #define DAEDALUS_MSG_PING 0x00000001u #define DAEDALUS_MSG_REQ_DECODE 0x00000002u #define DAEDALUS_MSG_HELLO 0x80000001u #define DAEDALUS_MSG_PONG 0x80000002u #define DAEDALUS_MSG_RESP_FRAME 0x80000003u /** * 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 kernel 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 */ /* -- REQ_DECODE / RESP_FRAME payload structures ---------------------- */ /** * enum daedalus_codec_id - codec selector for REQ_DECODE * @DAEDALUS_CODEC_VP9: libavcodec AV_CODEC_ID_VP9 * @DAEDALUS_CODEC_AV1: libavcodec AV_CODEC_ID_AV1 (Phase 8.6) * @DAEDALUS_CODEC_H264: libavcodec AV_CODEC_ID_H264 (Phase 8.6) * * Wire-stable across phases. The daemon maps these to the * libavcodec AV_CODEC_ID_* values internally so we don't leak * FFmpeg's enum into the kernel ABI. */ enum daedalus_codec_id { DAEDALUS_CODEC_VP9 = 1, DAEDALUS_CODEC_AV1 = 2, DAEDALUS_CODEC_H264 = 3, }; /** * struct daedalus_req_decode - REQ_DECODE payload prefix * @codec_id: enum daedalus_codec_id * @bitstream_len: bytes of bitstream following this struct * @flags: reserved, must be zero * * Total payload_len for a REQ_DECODE = sizeof(struct * daedalus_req_decode) + bitstream_len. */ struct daedalus_req_decode { __u32 codec_id; __u32 bitstream_len; __u32 flags; }; /** * enum daedalus_decode_status - RESP_FRAME outcome codes * @DAEDALUS_DECODE_OK: frame produced; fields below populated * @DAEDALUS_DECODE_NO_FRAME: codec consumed input but no frame * ready yet (e.g. lacks reference) * @DAEDALUS_DECODE_ERR_OPEN: avcodec_open2 failed * @DAEDALUS_DECODE_ERR_SEND: avcodec_send_packet failed * @DAEDALUS_DECODE_ERR_RECV: avcodec_receive_frame failed * @DAEDALUS_DECODE_ERR_CODEC: unknown codec_id */ enum daedalus_decode_status { DAEDALUS_DECODE_OK = 0, DAEDALUS_DECODE_NO_FRAME = 1, DAEDALUS_DECODE_ERR_OPEN = 100, DAEDALUS_DECODE_ERR_SEND = 101, DAEDALUS_DECODE_ERR_RECV = 102, DAEDALUS_DECODE_ERR_CODEC = 103, }; /** * struct daedalus_resp_frame - RESP_FRAME payload * @status: enum daedalus_decode_status * @codec_id: echoes the request's codec_id * @width: decoded frame width in pixels (0 if !OK) * @height: decoded frame height in pixels (0 if !OK) * @pix_fmt: libavcodec AVPixelFormat as int (informational) * @luma_len: Y-plane byte count actually hashed * @chroma_len: U+V byte count actually hashed (planar combined) * @fnv1a_yuv: FNV-1a 32-bit hash of Y,U,V planes concatenated * (line-by-line, stripping any libav alignment * stride padding). Lets the kernel side compare * against an offline reference without shipping * full pixel data through the chardev. * @reserved: must be zero * * Fixed size — keeps wire parsing simple. No variable-length * pixel data in Phase 8.4; dmabuf in Phase 8.5 carries that. */ struct daedalus_resp_frame { __u32 status; __u32 codec_id; __u32 width; __u32 height; __s32 pix_fmt; __u32 luma_len; __u32 chroma_len; __u32 fnv1a_yuv; __u32 reserved; }; #endif /* DAEDALUS_V4L2_PROTO_H */