/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * daedalus-v4l2 — kernel-internal device/state declarations. * * Shared between daedalus_v4l2_main.c (V4L2 m2m driver) and * daedalus_v4l2_chardev.c (kernel↔daemon bridge). The chardev * needs to look up in-flight V4L2 requests by cookie to complete * the m2m job when RESP_FRAME arrives — that path lives in * daedalus_complete_resp_frame(). */ #ifndef DAEDALUS_V4L2_MAIN_H #define DAEDALUS_V4L2_MAIN_H #include #include #include #include #include #include #include #include "daedalus_v4l2_proto.h" /** * struct daedalus_dev - top-level device state (singleton for now) * @pdev: owning platform device (synthesised in module_init) * @v4l2_dev: V4L2 device parent for any video_device we register * @vdev: video_device exposed as /dev/videoNN * @m2m_dev: mem2mem device shared by all per-open contexts * @m2m_lock: serialises vb2 queue + v4l2 ioctl ops * @inflight: list of struct daedalus_inflight (REQ_DECODE sent, * RESP_FRAME not yet returned) * @inflight_lock: protects @inflight * * Singleton per-module instance. Multi-instance support (one * decoder per /dev/videoNN) would require breaking g_daedalus_dev * out of daedalus_v4l2_main.c; not needed yet. */ struct daedalus_dev { struct platform_device *pdev; struct v4l2_device v4l2_dev; struct video_device vdev; struct v4l2_m2m_dev *m2m_dev; struct media_device mdev; struct mutex m2m_lock; struct list_head inflight; struct mutex inflight_lock; }; /* Module-wide singleton accessor (chardev needs this for RESP_FRAME). */ struct daedalus_dev *daedalus_get_dev(void); /** * daedalus_next_cookie() - shared cookie allocator * * Returns the next monotonically increasing request cookie. * Used by both the V4L2 m2m device_run path (for REQ_DECODE * from real OUTPUT buffers) and the chardev debugfs * test_decode path (for hand-crafted REQ_DECODE injection), * so the two namespaces never collide and RESP_FRAME logs * stay deterministic. */ u32 daedalus_next_cookie(void); /** * daedalus_complete_resp_frame() - chardev RESP_FRAME completion * @cookie: cookie carried by the matching REQ_DECODE * @fr: RESP_FRAME header from the daemon * @pixels: inline pixel bytes following the header in the * chardev payload (may be NULL if @pixels_len == 0) * @pixels_len: number of inline pixel bytes * * Called from the chardev write() path on RESP_FRAME. Looks up * the in-flight request, copies inline pixel data into the * CAPTURE vb2 buffer if available (Phase 8.5 path; Phase 8.6 * skips the copy because the daemon decoded directly into the * dmabuf), then completes both src+dst buffers and finishes * the m2m job. Silently drops responses for unknown cookies * (pr_warn_ratelimited). */ void daedalus_complete_resp_frame(u32 cookie, const struct daedalus_resp_frame *fr, const u8 *pixels, size_t pixels_len); /** * daedalus_export_capture_dmabuf() - chardev GET_DMABUF backend * @cookie: cookie from the matching REQ_DECODE * @plane: plane index (0-based) within the CAPTURE buffer * @flags: flags for dma_buf_fd (O_CLOEXEC etc.) * @out_fd: out: installed dmabuf fd in the calling task's * fd table (only valid when return value == 0) * * Called from the chardev DAEDALUS_IOC_GET_DMABUF ioctl * handler. Looks up the in-flight V4L2 request by cookie, * exports the CAPTURE vb2 buffer's plane as a dma_buf via * vb2_core_expbuf in the daemon's task context. Caller must * NOT touch out_fd on non-zero return. * * Return: 0 on success, -EINVAL for unknown cookie or bad * plane, propagated -errno from vb2_core_expbuf otherwise. */ int daedalus_export_capture_dmabuf(u32 cookie, u32 plane, u32 flags, int *out_fd); /** * daedalus_drain_inflight_on_disconnect() - fail all in-flight m2m jobs * * Called from daedalus_chardev_release() when the daemon disconnects * (graceful close, SIGKILL, daemon crash — anything that triggers * chardev release). Walks the in-flight list and, for every entry, * marks both src+dst buffers VB2_BUF_STATE_ERROR and calls * v4l2_m2m_buf_done_and_job_finish() to clear the m2m scheduler's * "job_running" flag. * * Without this, v4l2_m2m_cancel_job() (called from * v4l2_m2m_ctx_release() during the consumer's close() / task exit) * blocks forever waiting for a job_finish that the dead daemon will * never send — the consumer enters TASK_UNINTERRUPTIBLE and survives * SIGKILL until reboot. See issue #146 for the full trace. * * Safe to call with an empty in-flight list; no-op in that case. * Must NOT be called from atomic context — uses inflight_lock * (sleeping mutex) and v4l2_m2m_buf_done_and_job_finish (which can * sleep via vb2 buffer-done dispatch). */ void daedalus_drain_inflight_on_disconnect(void); #endif /* DAEDALUS_V4L2_MAIN_H */