/* 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 #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; }; /* * Wire-protocol payload cap. Sized to comfortably hold real-world * H.264 / VP9 / AV1 access-unit bitstreams: * - 720p H.264 worst-case I-frame: ~200 KiB * - 1080p H.264 worst-case I-frame: ~500 KiB * - 4K H.264 worst-case I-frame: ~2 MiB (would need a bump) * 1 MiB is the conservative end of what cedrus / rkvdec / hantro * report as OUTPUT_MPLANE sizeimage. Allocations (chardev kmalloc * / kmemdup, daemon read buffer, vb2 plane backing) are sized per- * payload at runtime; this only sets the ceiling. Issue #19. */ #define DAEDALUS_PROTO_MAX_PAYLOAD (1024u * 1024u) /* 1 MiB */ /* -- 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, }; /** * DAEDALUS_REQ_FLAG_H264_META - daedalus_req_decode.flags bit * * Set when a struct daedalus_h264_meta is present between the * daedalus_req_decode prefix and the slice bitstream. Required for * H.264 (codec_id == DAEDALUS_CODEC_H264) since libavcodec needs * SPS/PPS that the V4L2 stateless API delivers as separate ctrls, * not in the OUTPUT buffer. Other codecs ignore this bit. */ #define DAEDALUS_REQ_FLAG_H264_META 0x00000001u /** * struct daedalus_req_decode - REQ_DECODE payload prefix * @codec_id: enum daedalus_codec_id * @bitstream_len: bytes of bitstream following this struct * (after any optional metadata blocks — see flags) * @capture_width: CAPTURE buffer width in pixels * @capture_height: CAPTURE buffer height in pixels * @capture_pix_fmt: V4L2 fourcc of the CAPTURE format * (e.g. V4L2_PIX_FMT_NV12M) * @capture_num_planes: number of dmabuf planes the daemon should * fetch via DAEDALUS_IOC_GET_DMABUF (1..3) * @capture_plane_size: per-plane sizeimage from V4L2 S_FMT * (plane[0..N-1]). Unused entries = 0. * @capture_plane_stride: per-plane bytesperline from V4L2 S_FMT. * @flags: bitmask of DAEDALUS_REQ_FLAG_* * * Wire layout for a REQ_DECODE payload: * * struct daedalus_req_decode req; * IF (req.flags & DAEDALUS_REQ_FLAG_H264_META): * struct daedalus_h264_meta meta; * u8 bitstream[req.bitstream_len]; * * Total payload_len = sizeof(req) + (meta ? sizeof(meta) : 0) * + req.bitstream_len. * * The daemon uses (capture_*) to fetch + mmap the right CAPTURE * plane via DAEDALUS_IOC_GET_DMABUF, then decodes pixels * directly into the dmabuf. */ struct daedalus_req_decode { __u32 codec_id; __u32 bitstream_len; __u32 capture_width; __u32 capture_height; __u32 capture_pix_fmt; __u32 capture_num_planes; __u32 capture_plane_size[3]; __u32 capture_plane_stride[3]; __u32 flags; }; /** * struct daedalus_h264_meta - H.264 stateless-decode metadata * * Optional block following the daedalus_req_decode prefix when * DAEDALUS_REQ_FLAG_H264_META is set in req.flags. Carries the * structured controls the kernel collected from * V4L2_CID_STATELESS_H264_* — the daemon converts them into * AnnexB SPS+PPS NAL units (via an Exp-Golomb writer) and * prepends those NAL units to the slice bitstream before * handing it to libavcodec. * * The kernel never inspects these fields beyond capturing them * verbatim from the v4l2_ctrl_handler at device_run time; the * field semantics are governed entirely by the linux uABI * V4L2 stateless H.264 control definitions. * * Wire-stable across phases. If the kernel V4L2 H.264 control * structs grow new fields the protocol version bumps with them. */ struct daedalus_h264_meta { struct v4l2_ctrl_h264_sps sps; struct v4l2_ctrl_h264_pps pps; struct v4l2_ctrl_h264_scaling_matrix scaling_matrix; struct v4l2_ctrl_h264_decode_params decode_params; }; /** * 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; }; /* -- chardev ioctl ABI ----------------------------------------------- */ /** * struct daedalus_get_dmabuf - DAEDALUS_IOC_GET_DMABUF args * @cookie: cookie from the matching REQ_DECODE (in) * @plane: plane index, 0-based (in) * @flags: O_CLOEXEC etc.; passed through to dma_buf_fd (in) * @fd: exported dmabuf fd, installed in the calling * (daemon) task's fd table (out) * * The daemon calls this ioctl from REQ_DECODE handling to obtain * a per-plane dmabuf fd for the CAPTURE buffer the kernel * scheduled. The kernel resolves cookie → in-flight V4L2 * request → CAPTURE vb2 buffer, then calls vb2_core_expbuf in * the daemon's task context (so the fd lands in the daemon's * fd table). Daemon mmaps the fd, writes decoded pixels in * place, munmaps and close()s — then sends RESP_FRAME. */ struct daedalus_get_dmabuf { __u32 cookie; __u32 plane; __u32 flags; __s32 fd; }; #define DAEDALUS_IOC_MAGIC 'D' #define DAEDALUS_IOC_GET_DMABUF \ _IOWR(DAEDALUS_IOC_MAGIC, 1, struct daedalus_get_dmabuf) #endif /* DAEDALUS_V4L2_PROTO_H */