From nobody Sat Oct 4 11:14:58 2025 Received: from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com [136.143.188.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 169C727144A; Mon, 18 Aug 2025 05:51:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.12 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496319; cv=pass; b=sgJmuNTPyb7Bc91CrjDQOzQCa9sHOvvAl+U0PnwyNHK7b9p4rYiafnza2Z0yyZH2HZqKE4dEoSFJaeGFmaBz5apQx5RlQHoi2uTailk4aYXhe/EqLczkWPB2MJNccqgp8mxnlEKqwnMz32/DZ9H6Waxsz5KjUghKN57WSxd0T5U= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496319; c=relaxed/simple; bh=4pRM8qsAkeqCRW3ktTfw7X+/A4zn15K5CjxlSidgUzs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rtJIOLRMCi/g43phhufbLm3p4NHeuzyC6qCcwXmOsqfbPNL8zGeaJ4/vgSe9nK68mnR3qdEspjcU+u3uFTwTtbPe1v9/SVQDpEEVwYxiO4V6OvsqLWsHExnDJZT0F/8Yl248aAXUW/WpM0jDHaPiH/D1QzKtzU/bnv1r+WSsPMQ= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=aptH7mzs; arc=pass smtp.client-ip=136.143.188.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="aptH7mzs" ARC-Seal: i=1; a=rsa-sha256; t=1755496297; cv=none; d=zohomail.com; s=zohoarc; b=LLwhjtk2OAlHbgKBvtF9uhUVebolmqLXZSxfnuAcC9H+4sz84QcHkIM6lvEeaYvM4QpMwVMmEvPOT/unUX+UJQkM7bhrR1XRXt1owTwDX/vGpJHyKg0CdN49aWgzvD+tRtxcaG90aR31sdby1OHhVA1i54zR2vD1tkFDoGaverE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1755496297; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=WlET7ORMtBj8k242wjZ7qO6EjSEsUmE1q7rAL2+VxKE=; b=XFhX3h1hnx5/oTsRZNd1q+FLrQrgC0y75oZpbBBJ8ykZANGPLoQuOLm3FwyqXet0Hjv+zUsmCr7+yee0ZJDqO69Ae95cg0yalLQeEAwDbz/fOtQEl0nAPUkDLjAS5sFnLepL6bL4VWdICv6avNdZQv/hW251bcnQlNzAP1oENFw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1755496297; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=WlET7ORMtBj8k242wjZ7qO6EjSEsUmE1q7rAL2+VxKE=; b=aptH7mzs2kaEwf57D4KL/mGhN8hfqqqVpMpugyCyNUDJr4gQrLnMarABRTsgeVyQ l4FHGS8RH1ZEeHizc3ZTVb6im8ZuBlJkokHyG2j8nj0RxuE6C517UDsvpxrSxS2bz1U eOFgZmDUOXIQezPX1LZTCwTqT2SiHjspipIyv27Y= Received: by mx.zohomail.com with SMTPS id 1755496294138625.560871847908; Sun, 17 Aug 2025 22:51:34 -0700 (PDT) From: Daniel Almeida Date: Mon, 18 Aug 2025 02:49:47 -0300 Subject: [PATCH 1/7] rust: media: add the media module Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250818-v4l2-v1-1-6887e772aac2@collabora.com> References: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> In-Reply-To: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, kernel@collabora.com, linux-media@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External In preparation for future commits that add support for Rust abstractions like v4l2_device, video_device, v4l2_fh and others, add the media module in lib.rs and a corresponding MAINTAINERS entry. Signed-off-by: Daniel Almeida --- MAINTAINERS | 7 +++++++ rust/kernel/lib.rs | 2 ++ rust/kernel/media/mod.rs | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fe168477caa45799dfe07de2f54de6d6a1ce0615..6fc5d57950e474d73d5d65271a0= 394efc5a8960b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15434,6 +15434,13 @@ F: include/uapi/linux/uvcvideo.h F: include/uapi/linux/v4l2-* F: include/uapi/linux/videodev2.h =20 +MEDIA RUST INFRASTRUCTURE +M: Daniel Almeida +L: linux-media@vger.kernel.org +L: rust-for-linux@vger.kernel.org +S: Supported +F: rust/media + MEDIATEK BLUETOOTH DRIVER M: Sean Wang L: linux-bluetooth@vger.kernel.org diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index ed53169e795c0badf548025a57f946fa18bc73e3..34b9e1497b2a7f70c957bff3185= 5aeac6039cf2b 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -96,6 +96,8 @@ #[cfg(CONFIG_KUNIT)] pub mod kunit; pub mod list; +#[cfg(CONFIG_MEDIA_SUPPORT)] +pub mod media; pub mod miscdevice; pub mod mm; #[cfg(CONFIG_NET)] diff --git a/rust/kernel/media/mod.rs b/rust/kernel/media/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..e4a28be7b484888a02965d0e8b5= fd5d3c969840a --- /dev/null +++ b/rust/kernel/media/mod.rs @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-copyrightText: Copyright (C) 2025 Collabora Ltd. + +//! Media infrastructure support. +//! +//! Reference: \ No newline at end of file --=20 2.50.1 From nobody Sat Oct 4 11:14:58 2025 Received: from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com [136.143.188.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 85F1B27054C; Mon, 18 Aug 2025 05:52:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.12 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496324; cv=pass; b=hAKmvJsnk41dp06jRikg0I+KvJkvckDonEjKh57AAR25f5u1SPHKsLjYeBIl6+adhNUy3EzDK7jMssMGMKbyTdmbxGbgZMD2mNZUjKiZ9lZ3uixq9WEihiWgYTMNmAtqtT3nK3zIQMzqtk5d5HIX3/RWCd0+sIP0RCdtw//uckI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496324; c=relaxed/simple; bh=8CyA/td0aV46NCdPsJKlmycDN/4Xc49eWWynic7E30M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lgQJkqQAdadAFqW2v2dDsZlny5QN/QF9gamE2BmDLkFITxJU8o9YMI1rujXsfGSx+i/HRoFp/C+MZm7c9bWa+y2DRDnOYMhSNO0pIrGvG5EhLG1jVA9QtHboqjEQY+/B3lhkZF6awFvzixQdBDc8FSq+gC9si/jR0Rymi8Jh/iw= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=MkZXRORQ; arc=pass smtp.client-ip=136.143.188.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="MkZXRORQ" ARC-Seal: i=1; a=rsa-sha256; t=1755496301; cv=none; d=zohomail.com; s=zohoarc; b=HzhXIwVp8A2uKhu1i+YP+jboetj0ChACBqFd3xUVRYbpG2Nmf4E7otwD8DVAFbfc4fs+eeu3dmrHWMSjg6Q2L7/B0cyxbzfe2nKNg7Zw6Xy1PkuhD9NqKIncbyMmW2Er+afiujMum8Lr81OS3cwyyNMz/47fW/DHH77QJCv+vAk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1755496301; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=8dxBJI5g3fLuoB4rUsRh71vQ0CR/TagAO1Uci3c6LFc=; b=FznTeQGoNT6TTdkxmLGQLLqO6wWf7g85PtnKfxos3x/LGQKc69a+ZAtzx6zVPz0PH8vABDiZ0U40yzCVEsaxmVb7EnsffQODUAB3puwjT2him71VYM7GoCuFmbqq2kXveWCR7AzY/xwEM+8y4YayLJp8Km2vt30RrmHR2uNxLSw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1755496301; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=8dxBJI5g3fLuoB4rUsRh71vQ0CR/TagAO1Uci3c6LFc=; b=MkZXRORQNj7ThsOlIQ+8CUGACyFZP1SsmnBkOP8lY6041BpnAlAWkg6tAgkMYRyp Nmj7e60xRkTRvGXiIdX2rPOw7YYOePTk4fQxRClHB0SONsjwWdAIsTrngFqF7INMqDV dZOcaIX5FP9IP9vCJEjAGylAvDqrRT/hwlI9Ct3w= Received: by mx.zohomail.com with SMTPS id 1755496298214893.3470255323006; Sun, 17 Aug 2025 22:51:38 -0700 (PDT) From: Daniel Almeida Date: Mon, 18 Aug 2025 02:49:48 -0300 Subject: [PATCH 2/7] rust: v4l2: add support for v4l2_device Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250818-v4l2-v1-2-6887e772aac2@collabora.com> References: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> In-Reply-To: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, kernel@collabora.com, linux-media@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External struct v4l2_device is the entry-point for video4linux2 drivers. This struct contains the device state and is the root for v4l2 subdevices (i.e.: struct v4l2_subdev), which play an important part on how modern video devices are modeled. For now, add the bare-minimum support to allocate a v4l2::Device an register it with the v4l2 framework. Subsequent patches will add support for video devices and more. This is one of the steps needed to get a sample v4l2 driver to probe. Signed-off-by: Daniel Almeida --- rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/v4l2-device.c | 8 ++ rust/kernel/media/mod.rs | 5 +- rust/kernel/media/v4l2/device.rs | 177 +++++++++++++++++++++++++++++++++++= ++++ rust/kernel/media/v4l2/mod.rs | 9 ++ 6 files changed, 200 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 84d60635e8a9baef1f1a1b2752dc0fa044f8542f..95651c4bc9e561d9f4949111961= f41e65d8c1585 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -75,6 +75,7 @@ #include #include #include +#include #include =20 #if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE) diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 7cf7fe95e41dd51717050648d6160bebebdf4b26..83d7e76294207a804f2ad95097a= 1e4da53fe66f1 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -47,6 +47,7 @@ #include "task.c" #include "time.c" #include "uaccess.c" +#include "v4l2-device.c" #include "vmalloc.c" #include "wait.c" #include "workqueue.c" diff --git a/rust/helpers/v4l2-device.c b/rust/helpers/v4l2-device.c new file mode 100644 index 0000000000000000000000000000000000000000..d19b46e8283ce762b4259e3df5e= cf8bb18e863e9 --- /dev/null +++ b/rust/helpers/v4l2-device.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +void rust_helper_v4l2_device_get(struct v4l2_device *v4l2_dev) +{ + v4l2_device_get(v4l2_dev); +} diff --git a/rust/kernel/media/mod.rs b/rust/kernel/media/mod.rs index e4a28be7b484888a02965d0e8b5fd5d3c969840a..476ea673867121fb68fd4695c2c= ddc5380e86421 100644 --- a/rust/kernel/media/mod.rs +++ b/rust/kernel/media/mod.rs @@ -3,4 +3,7 @@ =20 //! Media infrastructure support. //! -//! Reference: \ No newline at end of file +//! Reference: + +#[cfg(CONFIG_VIDEO_DEV =3D "y")] +pub mod v4l2; diff --git a/rust/kernel/media/v4l2/device.rs b/rust/kernel/media/v4l2/devi= ce.rs new file mode 100644 index 0000000000000000000000000000000000000000..26096672e6f6d35711ff9bdabf4= d7b20f697a4ab --- /dev/null +++ b/rust/kernel/media/v4l2/device.rs @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-copyrightText: Copyright (C) 2025 Collabora Ltd. + +//! Video for Linux 2 (V4L2) device support. +//! +//! A v4l2 [`Device] is the entry-point for video4linux2 drivers. It acts = as a +//! logical device that may back multiple video nodes. +//! +//! This struct contains the device state and is the root for v4l2 subdevi= ces +//! (i.e.: struct v4l2_subdev), which play an important part on how modern= video +//! devices are modelled. +//! +//! C headers: [`include/media/v4l2-dev.h`](srctree/include/media/v4l2-dev= .h). + +use core::{mem::MaybeUninit, ptr::NonNull}; + +use crate::{ + alloc::{self, KBox}, + device, + error::to_result, + init::InPlaceInit, + prelude::*, + types::{ARef, AlwaysRefCounted, Opaque}, +}; + +/// A logical V4L2 device handle. +/// +/// # Invariants +/// +/// - `inner` points to a valid `v4l2_device` that has been registered. +#[pin_data] +#[repr(C)] +pub struct Device { + #[pin] + inner: Opaque, + #[pin] + data: T::Data, +} + +impl Device { + /// Converts a raw pointer to a `Device` reference. + /// + /// # Safety + /// + /// - `ptr` must be a valid pointer to a `struct v4l2_device` that must + /// remain valid for the lifetime 'a. + #[expect(dead_code)] + pub(super) unsafe fn from_raw<'a>(ptr: *mut bindings::v4l2_device) -> = &'a Device { + // SAFETY: `ptr` is a valid pointer to a `struct v4l2_device` as p= er the + // safety requirements of this function. + unsafe { &*(ptr.cast::>()) } + } + + /// Returns the raw pointer to the `struct v4l2_device`. + pub(crate) fn as_raw(&self) -> *mut bindings::v4l2_device { + self.inner.get() + } + + /// # Safety + /// + /// This function must be called as the release callback of `struct v4= l2_device`. + unsafe extern "C" fn release_callback(dev: *mut bindings::v4l2_device)= { + // SAFETY: `dev` was set by calling `KBox::into_raw` on a + // `Pin>` in `Registration::new`. Now that the refc= ount + // reached zero, we are reassembling the KBox so it can be dropped. + let v4l2_dev: Pin>> =3D + unsafe { Pin::new_unchecked(Box::from_raw(dev.cast())) }; + + drop(v4l2_dev) + } +} + +impl AsRef for Device { + fn as_ref(&self) -> &device::Device { + // SAFETY: the invariants of `Device` state that the device is + // registered, and the `dev` field is set at registration time, so= the + // returned reference is valid for the lifetime of self. + unsafe { device::Device::from_raw((*self.as_raw()).dev) } + } +} + +// SAFETY: V4L2 devices are always reference counted and the get/put funct= ions +// satisfy the requirements. +unsafe impl AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: Safe as per the invariants of `Device`. + unsafe { bindings::v4l2_device_get(self.as_raw()) } + } + + unsafe fn dec_ref(obj: core::ptr::NonNull) { + // SAFETY: Safe as per the invariants of `Device`. + unsafe { bindings::v4l2_device_put(obj.cast().as_ptr()) }; + } +} + +/// SAFETY: It is safe to send `Device` to another thread. In particula= r, +/// `Device` instances can be dropped from any thread. +unsafe impl Send for Device {} + +// SAFETY: it is safe to share references to `Device` between threads a= s it +// is not possible to mutate the underlying `struct v4l2_device` through a +// shared reference. +unsafe impl Sync for Device {} + +/// The interface that must be implemented by structs that would otherwise= embed +/// a C [`struct v4l2_device`](srctree/include/media/v4l2-device.h). +pub trait Driver { + /// The type of the data associated with the device. + type Data: Sync + Send; +} + +/// Represents the registration of a [`Device`]. +/// +/// # Invariants +/// +/// - The underlying device was registered via [`bindings::v4l2_device_reg= ister`]. +pub struct Registration(ARef>); + +impl Registration { + /// Creates and registers a [`Device`] given a [`kernel::device::Devic= e`] reference and + /// the associated data. + pub fn new( + dev: &device::Device, + data: impl PinInit, + flags: alloc::Flags, + ) -> Result { + let v4l2_dev =3D try_pin_init!(Device { + inner <- Opaque::try_ffi_init(move |slot: *mut bindings::v4l2_= device| { + let v4l2_dev =3D bindings::v4l2_device { + release: Some(Device::::release_callback), + // SAFETY: All zeros is valid for this C type. + ..unsafe { MaybeUninit::zeroed().assume_init() } + }; + + // SAFETY: The initializer can write to the slot. + unsafe { slot.write(v4l2_dev) }; + + // SAFETY: It is OK to call this function on a zeroed + // v4l2_device and a valid `device::Device` reference. + to_result(unsafe { bindings::v4l2_device_register(dev.as_r= aw(), slot) }) + }), + data <- data, + }); + + let v4l2_dev =3D KBox::pin_init(v4l2_dev, flags)?; + + // SAFETY: We will be passing the ownership of the increment to AR= ef, + // which treats the underlying memory as pinned throughout its lif= etime. + // + // This is true because: + // + // - ARef does not expose a &mut T, so there is no way to move = the T + // (e.g.: via a `core::mem::swap` or similar). + // - ARef's member functions do not move the T either. + let ptr =3D KBox::into_raw(unsafe { Pin::into_inner_unchecked(v4l2= _dev) }); + + // SAFETY: + // + // - the refcount is one, and we are transfering the ownership of = that + // increment to the ARef. + // - `ptr` is non-null as it came from `KBox::into_raw`, so it is = safe + // to call `NonNulll::new_unchecked`. + Ok(Self(unsafe { ARef::from_raw(NonNull::new_unchecked(ptr)) })) + } + + /// Returns a reference to the underlying [`Device`]. + pub fn device(&self) -> &Device { + &self.0 + } +} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: Safe as per the invariants of [`Registration`]. + unsafe { bindings::v4l2_device_unregister(self.0.as_raw()) } + } +} diff --git a/rust/kernel/media/v4l2/mod.rs b/rust/kernel/media/v4l2/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..63394d0322fa1f646f3b23a5fad= f2ac34a9f666e --- /dev/null +++ b/rust/kernel/media/v4l2/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! V4L2 support. +//! +//! See the [structure of the V4L2 framework] for more information. +//! +//! [structure of the V4L2 framework]: https://www.kernel.org/doc/html/lat= est/driver-api/media/v4l2-intro.html#structure-of-the-v4l2-framework +/// Support for Video for Linux 2 (V4L2) devices. +pub mod device; --=20 2.50.1 From nobody Sat Oct 4 11:14:58 2025 Received: from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com [136.143.188.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 13D5C272805; Mon, 18 Aug 2025 05:52:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.12 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496329; cv=pass; b=Uof1Fj/Q3HXev5R8gP2TFl0CL8jsGag3byuiR9sCzC2jsksTeX72oPdqpt41IcZ6o0i3ycETJ6Vu0haBM83ix+mc1MqP0gMfHB5gdA+SnnBlKOyErIh0tg3tqhgHIaP4TpURJg/DXGw7CS7/QK0nWeDWZQYGmwOX/FSVLgGJeaA= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496329; c=relaxed/simple; bh=SYuPpLWJH24VNko7yCziTLKZcoq2a+nA2/azwEKaO9g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=kbW5fJ8xbTuwjlUZucRTyF+NaoUb5Y+aJw2uHnFjpQNTBHuwN2iMJvOzIXLgueLBKU18M7n19ZpOXFYoYN+NXwgKjYs9bbTEWhaBSWkJFkL8MajvzeEWJKfxrUBJPw7lmlGzk7csTaNWsvjLrAmXwlVESir3ZmEGSjTcL81rc1o= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=FZ0b4ep3; arc=pass smtp.client-ip=136.143.188.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="FZ0b4ep3" ARC-Seal: i=1; a=rsa-sha256; t=1755496305; cv=none; d=zohomail.com; s=zohoarc; b=nVwbwGEjZ/iMECajzajsl7A+xyN+1KdNl1VxT803+BIsaWZnyNZyB16kJkrtgnmLbNRdOMfUd5KNN0aKGWrMBZUqV/dT1DrPlg3kN5Sf+betKw7k6romkKGibMN6K2pCoxpsNT2D+DBOdKqyVjD++ZDH8fvdk9/Wt+1J4SNVUJA= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1755496305; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=XhrjeSrRtk4sckOYF9Gg78Pr/Ohbjo4YpRHB3aSpaa4=; b=GNjDB8x5m5HtWGWK3gL6kDytURbI3PJAW0hztFIIhQbXh6LzXmvGLDc8DqToMuV0+kwg+ThUKWyxT75SvMrpKNoe0bmzKO86Vrex0KJAhIwOWaiGqEVtJhuLP7oh3tmALig50sCjRP3Ex2S4qol1rGjfzLnxAtuiv+IGSy3m2cI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1755496305; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=XhrjeSrRtk4sckOYF9Gg78Pr/Ohbjo4YpRHB3aSpaa4=; b=FZ0b4ep3zEd/la/ID57ft5H/nyzeaijGy0SEImeRtrn69h/oC70lJadWqi3Y52/Y oEGLLe3XGlTip0t5lf60qH5T4dp8o3T4dKuuuIhbS5V1Zt5GCyFSGv5HcFtx8TZikG3 uT4mVIH9IvokcDLXjMV/YuGe7ppatewASZLr1k4Q= Received: by mx.zohomail.com with SMTPS id 175549630245781.41696240548583; Sun, 17 Aug 2025 22:51:42 -0700 (PDT) From: Daniel Almeida Date: Mon, 18 Aug 2025 02:49:49 -0300 Subject: [PATCH 3/7] rust: v4l2: add support for video device nodes Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250818-v4l2-v1-3-6887e772aac2@collabora.com> References: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> In-Reply-To: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, kernel@collabora.com, linux-media@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External Video device nodes back the actual /dev/videoX files. They expose a rich ioctl interface for which we will soon add support for and allow for modelling complex hardware through a topology of nodes, each modelling a particular hardware function or component. V4l2 drivers rely on video device nodes pretty extensively, so add a minimal Rust abstraction for them. The abstraction currently does the bare-minimum to let users register a V4L2 device node. It also introduces the video::Driver trait that will be implemented by Rust v4l2 drivers. This trait will then be refined in future patches. Signed-off-by: Daniel Almeida --- rust/helpers/v4l2-device.c | 16 +++ rust/kernel/media/v4l2/device.rs | 1 - rust/kernel/media/v4l2/mod.rs | 3 + rust/kernel/media/v4l2/video.rs | 251 +++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 270 insertions(+), 1 deletion(-) diff --git a/rust/helpers/v4l2-device.c b/rust/helpers/v4l2-device.c index d19b46e8283ce762b4259e3df5ecf8bb18e863e9..0ead52b9a1ccc0fbc4d7df63578= b334b17c05b70 100644 --- a/rust/helpers/v4l2-device.c +++ b/rust/helpers/v4l2-device.c @@ -6,3 +6,19 @@ void rust_helper_v4l2_device_get(struct v4l2_device *v4l2_= dev) { v4l2_device_get(v4l2_dev); } + +void rust_helper_video_get(struct video_device *vdev) +{ + get_device(&vdev->dev); +} + +void rust_helper_video_put(struct video_device *vdev) +{ + put_device(&vdev->dev); +} + +int rust_helper_video_register_device(struct video_device *vdev, + enum vfl_devnode_type type, int nr) +{ + return video_register_device(vdev, type, nr); +} diff --git a/rust/kernel/media/v4l2/device.rs b/rust/kernel/media/v4l2/devi= ce.rs index 26096672e6f6d35711ff9bdabf4d7b20f697a4ab..cbbf07ab63b7725cafecb89eb93= c497e749287e7 100644 --- a/rust/kernel/media/v4l2/device.rs +++ b/rust/kernel/media/v4l2/device.rs @@ -44,7 +44,6 @@ impl Device { /// /// - `ptr` must be a valid pointer to a `struct v4l2_device` that must /// remain valid for the lifetime 'a. - #[expect(dead_code)] pub(super) unsafe fn from_raw<'a>(ptr: *mut bindings::v4l2_device) -> = &'a Device { // SAFETY: `ptr` is a valid pointer to a `struct v4l2_device` as p= er the // safety requirements of this function. diff --git a/rust/kernel/media/v4l2/mod.rs b/rust/kernel/media/v4l2/mod.rs index 63394d0322fa1f646f3b23a5fadf2ac34a9f666e..ba1d4b7da8d8887b1604031497c= 300d7e0609cd2 100644 --- a/rust/kernel/media/v4l2/mod.rs +++ b/rust/kernel/media/v4l2/mod.rs @@ -7,3 +7,6 @@ //! [structure of the V4L2 framework]: https://www.kernel.org/doc/html/lat= est/driver-api/media/v4l2-intro.html#structure-of-the-v4l2-framework /// Support for Video for Linux 2 (V4L2) devices. pub mod device; + +/// Support for Video for Linux 2 (V4L2) video devices. +pub mod video; diff --git a/rust/kernel/media/v4l2/video.rs b/rust/kernel/media/v4l2/video= .rs new file mode 100644 index 0000000000000000000000000000000000000000..e6954d3e6ac65201bd40a0215ba= bb354ae10cd12 --- /dev/null +++ b/rust/kernel/media/v4l2/video.rs @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-copyrightText: Copyright (C) 2025 Collabora Ltd. + +//! Video for Linux 2 (V4L2) video device support. +//! +//! Video device nodes back the actual /dev/videoX files and provide an +//! interface for userspace to interact with the device, usually via the `= ioctl` +//! syscall. +//! +//! They expose a rich ioctl interface and allow for modelling complex har= dware +//! through a topology of nodes, each modelling a particular hardware func= tion +//! or component. +//! +//! C headers: [`include/media/v4l2-dev.h`](srctree/include/media/v4l2-dev= .h). + +use core::{marker::PhantomData, mem::MaybeUninit, ops::Deref, ptr::NonNull= }; + +use pin_init::PinInit; + +use crate::{ + alloc, + error::to_result, + media::v4l2::{self, video}, + prelude::*, + types::{ARef, AlwaysRefCounted, Opaque}, +}; + +/// The type of node that will be exposed to userspace. +#[repr(u32)] +pub enum NodeType { + /// For video input/output devices. + Video =3D bindings::vfl_devnode_type_VFL_TYPE_VIDEO, +} + +/// Identifies if the video device corresponds to a receiver, a transmitte= r or a +/// mem-to-mem device. +#[repr(u32)] +pub enum Direction { + /// The device is a receiver. + Rx, + /// The device is a transmitter. + Tx, + // TODO: m2m. We do not support this at the moment, so it is not possi= ble to + // specify it here as well. +} + +/// A V4L2 device node. +/// +/// V4L2 devices nodes are backed by a [`v4l2::Device`]. Each instance is +/// associated with a specific video device in the filesystem. A logical d= evice +/// can be represented by multiple instances of this struct. +/// +/// # Invariants +/// +/// - `inner` is a valid pointer to a `struct video_device`. +#[pin_data] +#[repr(C)] +pub struct Device { + #[pin] + inner: Opaque, + #[pin] + data: ::Data, +} + +impl Device { + pub(super) fn as_raw(&self) -> *mut bindings::video_device { + self.inner.get() + } + + /// Converts a raw pointer to a `Device` reference. + /// + /// # Safety + /// + /// - `ptr` must be a valid pointer to a `struct video_device` that mu= st + /// remain valid for the lifetime 'a. + #[expect(dead_code)] + pub(super) unsafe fn from_raw<'a>(ptr: *mut bindings::video_device) ->= &'a Device { + // SAFETY: `ptr` is a valid pointer to a `struct video_device` as = per the + // safety requirements of this function. + unsafe { &*(ptr.cast::>()) } + } + + /// Returns the video device number, i.e.: /dev/videoX. + pub fn num(&self) -> u16 { + // SAFETY: Safe as per the invariants of `Device`. + unsafe { (*self.as_raw()).num } + } + + /// # Safety + /// + /// This function must be called as the release callback of `struct + /// video_device`. + unsafe extern "C" fn release_callback(dev: *mut bindings::video_device= ) { + // SAFETY: `dev` was set by calling `KBox::into_raw` on a + // `Pin>` in `Registration::new`. Now that the refc= ount + // reached zero, we are reassembling the KBox so it can be dropped. + let v4l2_dev: Pin>> =3D + unsafe { Pin::new_unchecked(Box::from_raw(dev.cast())) }; + + drop(v4l2_dev) + } +} + +impl AsRef> for Device { + fn as_ref(&self) -> &v4l2::device::Device { + // SAFETY: `self.as_raw()` is a valid pointer to a `struct video_d= evice` + // as per the invariants of `Device`. + unsafe { v4l2::device::Device::from_raw((*self.as_raw()).v4l2_dev)= } + } +} + +impl Deref for Device { + type Target =3D ::Data; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +/// SAFETY: V4L2 device nodes are always reference counted and the get/put +/// functions satisfy the requirements. +unsafe impl AlwaysRefCounted for Device { + fn inc_ref(&self) { + // SAFETY: it is safe to call `bindings::video_get` because + // `self.inner` is a valid pointer to a `struct video_device` as p= er + // the invariants of `Device`. + unsafe { bindings::video_get(self.inner.get()) } + } + + unsafe fn dec_ref(obj: NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is + // non-zero. + unsafe { bindings::video_put(obj.cast().as_ptr()) }; + } +} + +// SAFETY: it is safe to send a [`Device`] to another thread. In particula= r, a +// [`Device`] can be dropped by any thread. +unsafe impl Send for Device {} + +// SAFETY: It is safe to send a &Device to another thread, as we do not= allow +// mutation through a shared reference. +unsafe impl Sync for Device {} + +/// The interface that must be implemented by structs that would otherwise= embed +/// a C [`struct video_device`](srctree/include/media/v4l2-dev.h). +pub trait Driver: v4l2::device::Driver { + /// The type of the driver's private data. + type Data; + + /// The [`NodeType`] to use when registering the device node. + const NODE_TYPE: NodeType; + + /// The [`Direction`] to use when registering the device node. + const DIRECTION: Direction; + + /// The name to use when registering the device node. + const NAME: &'static CStr; +} + +struct DeviceOptions<'a, T: Driver> { + dev: &'a v4l2::device::Device, + _phantom: PhantomData, +} + +impl<'a, T: Driver> DeviceOptions<'a, T> { + /// Creates a `video_device` ready for registration. + fn into_raw(self) -> bindings::video_device { + bindings::video_device { + v4l2_dev: self.dev.as_raw(), + name: { + let mut name: [c_char; 64] =3D [0; 64]; + let src =3D T::NAME.as_bytes(); + let len =3D core::cmp::min(src.len(), name.len()); + name[..len].copy_from_slice(&src[..len]); + name + }, + vfl_dir: T::DIRECTION as c_uint, + release: Some(Device::::release_callback), + // SAFETY: All zeros is valid for the rest of the fields in th= is C + // type. + ..unsafe { MaybeUninit::zeroed().assume_init() } + } + } +} + +/// Represents the registration of a V4L2 device node. +pub struct Registration(ARef>); + +impl Registration { + /// Returns a new `Registration` for the given device, which guarantee= s that + /// the underlying device node is properly initialized and registered,= which + /// means that it can be safely used. + pub fn new( + dev: &v4l2::device::Device, + data: impl PinInit<::Data, Error>, + flags: alloc::Flags, + ) -> Result { + let video_dev =3D try_pin_init!(Device { + inner <- Opaque::try_ffi_init(move |slot: *mut bindings::video= _device| { + let opts: DeviceOptions<'_, T> =3D DeviceOptions { + dev, + _phantom: PhantomData + }; + + // SAFETY: `DeviceOptions::into_raw` produces a valid + // `bindings::video_device` that is ready for registration. + unsafe { slot.write(opts.into_raw()) }; + + + // SAFETY: It is OK to call this function on a zeroed + // `video_device` and a valid `v4l2::Device` reference. + to_result(unsafe { bindings::video_register_device(slot, T= ::NODE_TYPE as c_uint, -1) }) + }), + data <- data, + }); + + let video_dev =3D KBox::pin_init(video_dev, flags)?; + + // SAFETY: We will be passing the ownership to ARef, which trea= ts the + // underlying memory as pinned throughout its lifetime. + // + // This is true because: + // + // - ARef does not expose a &mut T, so there is no way to move = the T + // (e.g.: via a `core::mem::swap` or similar). + // - ARef's member functions do not move the T either. + let ptr =3D KBox::into_raw(unsafe { Pin::into_inner_unchecked(vide= o_dev) }); + + // SAFETY: + // + // - the refcount is one, and we are transfering the ownership of = that + // increment to the ARef. + // - `ptr` is non-null as it came from `KBox::into_raw`, so it is = safe + // to call `NonNulll::new_unchecked`. + Ok(Self(unsafe { ARef::from_raw(NonNull::new_unchecked(ptr)) })) + } + + /// Returns a reference to the underlying video device. + pub fn device(&self) -> &video::Device { + &self.0 + } +} + +impl Drop for Registration { + fn drop(&mut self) { + // SAFETY: `self.0` is a valid `video_device` that was registered = in + // [`Registration::new`]. + unsafe { bindings::video_unregister_device(self.0.as_raw()) }; + } +} --=20 2.50.1 From nobody Sat Oct 4 11:14:58 2025 Received: from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com [136.143.188.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F337A271476; Mon, 18 Aug 2025 05:52:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.12 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496346; cv=pass; b=U1o5PeCYHRwZ3IJXP53N8Sht2px4M25M7DebFzCeZNbTVnwBSMUJK6tLgpMisa1oLYpOEONtbd55KxVzcWHGG9EEO3e/+NHxkUyvaC7486giZBZMXP2eHIinKBRlTQn7kZTd0LHT2Wa17pk3ywD4NL2T8cm+CqgJWE/W/E+bUKk= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496346; c=relaxed/simple; bh=RBZwr7i7mu4f81XKdVBGd1PjZ1NQ30apiDuf3T4SOBM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=dM2jtQ71yVwWirzLHaM2kIKuBfqn7afjQb9sTP73AZfF66womcPW2EHX4kUGzrE+4XIcPQL2low85bDI7ZgNQyEsNaZowbW7UUf599IBqlmg3h6sIvVef/LO0YOqcXNcG++/SH5eFKMIvpb7nTR8HE4l+evFIqkJ+XTXge10ht8= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=OlJCJkr/; arc=pass smtp.client-ip=136.143.188.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="OlJCJkr/" ARC-Seal: i=1; a=rsa-sha256; t=1755496309; cv=none; d=zohomail.com; s=zohoarc; b=XgAmQ2x20QJmKuhtXxRgnDvXcQyEXFzqbtQbU7bBZaF5xW6nhK+U/+pMoYECj2SOMekykWkDPnUDXgCEcyUOOA7Z1fWu2zEl5wtGhUORJ2f5Mxy7Jvc2F4cKqkBrRJI5siyw2Zd+5TK5tij73DfnvClH6S/Fk+BhCtceIjoekj0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1755496309; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=dmuZuxPv44ha8AiyUg7rl9Ghj4JlB91sUyy/1+MmJjQ=; b=j9TJmnQIsgy/aidtlutqtgOwdJlmvOXE85kUkhZhPRUiqKJ12mbH/GpWwv2lNrzPfO5aL391BqQSCA+eRVkul259gDX8pbhuz8+lEJszT14Uq36jOYO/1Qqa839i2eV7HtvFZ3oHGJf3AXDvkZOJ1/rGM/ZATwxJeUAIbZatmIE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1755496309; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=dmuZuxPv44ha8AiyUg7rl9Ghj4JlB91sUyy/1+MmJjQ=; b=OlJCJkr/76o806UYpE3tN5VQTBE/CAKxaYgJzr1xRF5LVE01/Hnp05xlxVZJloq9 edQ8AsF6P9pmE4kRIj2YziGFMAJPe5CqcGYGI2sHIkxgTO/D+WvfMB4XsAEuwgOBtPp eu/T4f5JjnwNUpjrY3kzDBBYTwNAtdRyOU747bTE= Received: by mx.zohomail.com with SMTPS id 1755496307449714.2271839560013; Sun, 17 Aug 2025 22:51:47 -0700 (PDT) From: Daniel Almeida Date: Mon, 18 Aug 2025 02:49:50 -0300 Subject: [PATCH 4/7] rust: v4l2: add support for v4l2 file handles Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250818-v4l2-v1-4-6887e772aac2@collabora.com> References: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> In-Reply-To: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, kernel@collabora.com, linux-media@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External V4L2 allow multiple opens for a variety of use-cases, where the actual meaning of opening a node more than once is defined by the driver. Each open call is associated with its own v4l2_fh instance, and these instances are collected at the struct video_device level, making up a private context that can be retrieved at later calls, like ioctl()s performed on the node's file descriptor. Add basic support for v4l2_fh and a corresponding DriverFile trait to help model it. This will be needed in future patches in order to add v4l2 ioctl support, among other things. Signed-off-by: Daniel Almeida --- rust/bindings/bindings_helper.h | 1 + rust/helpers/v4l2-device.c | 6 ++ rust/kernel/media/v4l2/file.rs | 165 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/media/v4l2/mod.rs | 3 + rust/kernel/media/v4l2/video.rs | 7 +- 5 files changed, 180 insertions(+), 2 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 95651c4bc9e561d9f4949111961f41e65d8c1585..11ff2c018515e99c8af9ad91ab0= 9f8f15ae224c1 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -76,6 +76,7 @@ #include #include #include +#include #include =20 #if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE) diff --git a/rust/helpers/v4l2-device.c b/rust/helpers/v4l2-device.c index 0ead52b9a1ccc0fbc4d7df63578b334b17c05b70..24fa41fe570d1263313e70325c4= b39f819cb4177 100644 --- a/rust/helpers/v4l2-device.c +++ b/rust/helpers/v4l2-device.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 =20 +#include #include =20 void rust_helper_v4l2_device_get(struct v4l2_device *v4l2_dev) @@ -22,3 +23,8 @@ int rust_helper_video_register_device(struct video_device= *vdev, { return video_register_device(vdev, type, nr); } + +struct video_device *rust_helper_video_devdata(struct file *filp) +{ + return video_devdata(filp); +} diff --git a/rust/kernel/media/v4l2/file.rs b/rust/kernel/media/v4l2/file.rs new file mode 100644 index 0000000000000000000000000000000000000000..37b34f8e6f251fafde5f7e6b4bd= 654519d8247a5 --- /dev/null +++ b/rust/kernel/media/v4l2/file.rs @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-copyrightText: Copyright (C) 2025 Collabora Ltd. + +//! V4L2 file handle support. +//! +//! V4L2 allow multiple opens for a variety of use-cases, where the actual +//! meaning of opening a node more than once is defined by the driver. +//! +//! Each open call is associated with its own v4l2_fh instance, and these +//! instances are collected at the struct video_device level, making up a +//! private context that can be retrieved at later calls, like ioctl()s +//! performed on the node's file descriptor. + +use core::{marker::PhantomData, mem::MaybeUninit, ops::Deref}; + +use pin_init::PinInit; + +use crate::{alloc::KBox, init::InPlaceInit, media::v4l2::video, prelude::*= , types::Opaque}; + +/// Trait that must be implemented by V4L2 drivers to represent a V4L2 fil= e. +pub trait DriverFile { + /// The parent `Driver` implementation for this `DriverFile`. + type Driver: video::Driver; + + /// A reference to the module that this driver belongs to. + const MODULE: &'static ThisModule; + + /// Called when a client opens a V4L2 node. + fn open(vdev: &video::Device) -> impl PinInit; +} + +/// An open V4L2 file handle. +/// +/// # Invariants +/// +/// `inner` is a valid instance of a `struct v4l2_fh`. +#[repr(C)] +#[pin_data] +pub struct File { + #[pin] + inner: Opaque, + #[pin] + driver_file: T, +} + +impl File { + /// Converts a raw pointer to a `File` reference. + /// + /// # Safety + /// + /// - `ptr` must be a valid pointer to a `struct v4l2_file`. + /// - `ptr` must be valid for 'a. + #[expect(dead_code)] + pub(super) unsafe fn from_raw<'a>(ptr: *mut bindings::v4l2_fh) -> &'a = File { + // SAFETY: `ptr` is a valid pointer to a `struct v4l2_file` as per= the + // safety requirements of this function. + unsafe { &*(ptr.cast::>()) } + } + + /// Returns the raw pointer to the `struct v4l2_fh`. + pub(super) fn as_raw(&self) -> *mut bindings::v4l2_fh { + self.inner.get() + } + + /// The open callback of a `struct video_device` + /// + /// # Safety + /// + /// - this function must be called as the open callback of a `struct v= ideo_device`. + /// - `raw_file` must be a valid pointer to a `struct file`. + pub(super) unsafe extern "C" fn open_callback(raw_file: *mut bindings:= :file) -> c_int { + // SAFETY: `raw_file` is a valid pointer to a `struct file` and th= is is + // the open callback for a `struct video device` as per the safety + // requirements. This means that the `struct video_device` can be + // retrieved by this FFI call. + let vdev =3D unsafe { bindings::video_devdata(raw_file) }; + + let file =3D try_pin_init!(File:: { + inner <- Opaque::ffi_init(move |slot: *mut bindings::v4l2_fh| { + // SAFETY: + // - it is safe for the initializer to write to the slot, + // - zeroed() is a valid value for `struct v4l2_fh`. + unsafe { *slot =3D MaybeUninit::zeroed().assume_init() }; + + // SAFETY: `slot` was zero-initialized and `vdev` is a val= id + // video device that was retrieved by `video_devdata`. + unsafe { bindings::v4l2_fh_init(slot, vdev) } + }), + driver_file <- { + // SAFETY: `vdev` is a valid pointer to a `struct video_de= vice` that was + // retrieved using `video_devdata`. The underlying `struct= video_device` + // remains valid for the lifetime 'a, i.e.: for the entire= scope of this + // function. + let vdev =3D unsafe { video::Device::from_raw(vdev) }; + T::open(vdev) + } + }); + + let file =3D match KBox::pin_init(file, GFP_KERNEL) { + Ok(file) =3D> file, + Err(e) =3D> return e.to_errno(), + }; + + let v4l2_fh =3D file.as_ref().as_raw(); + + // SAFETY: We are passing ownership of the `File` to the kernel, + // which treats the underlying memory as pinned throughout its lif= etime. + let ptr =3D KBox::into_raw(unsafe { Pin::into_inner_unchecked(file= ) }); + + // SAFETY: `raw_file` points to a valid `struct file` as per the s= afety + // requirements, so it is safe to dereference it. + unsafe { (*raw_file).private_data =3D ptr.cast() }; + + // SAFETY: `v4l2_fh` was initialized above in `v4l2_fh_init`. + unsafe { bindings::v4l2_fh_add(v4l2_fh) }; + + 0 + } + + /// The release callback of a `struct v4l2_fh`. + /// + /// # Safety + /// + /// - this function must be called as the release callback of a `struc= t v4l2_fh`. + /// - `raw_file` must be a valid pointer to a `struct file`. + pub(super) unsafe extern "C" fn release_callback(raw_file: *mut bindin= gs::file) -> c_int { + // SAFETY: `raw_file::private_data` was set to `Pin` = in + // `open_callback`. + let file: Pin>> =3D + unsafe { Pin::new_unchecked(Box::from_raw((*raw_file).private_= data.cast())) }; + + // SAFETY: `file.inner` is a valid pointer to a `struct v4l2_fh`. + unsafe { bindings::v4l2_fh_del(file.as_raw()) }; + // SAFETY: `file.inner` is a valid pointer to a `struct v4l2_fh`. + unsafe { bindings::v4l2_fh_exit(file.as_raw()) }; + + 0 + } +} + +impl Deref for File { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + &self.driver_file + } +} + +pub(super) struct FileVtable(PhantomData); + +impl FileVtable { + const VTABLE: bindings::v4l2_file_operations =3D bindings::v4l2_file_o= perations { + open: Some(File::::open_callback), + release: Some(File::::release_callback), + owner: T::MODULE.as_ptr(), + unlocked_ioctl: Some(bindings::video_ioctl2), + // SAFETY: All zeros is valid for the rest of the fields in this C + // type. + ..unsafe { MaybeUninit::zeroed().assume_init() } + }; + + pub(super) const fn build() -> &'static bindings::v4l2_file_operations= { + &Self::VTABLE + } +} diff --git a/rust/kernel/media/v4l2/mod.rs b/rust/kernel/media/v4l2/mod.rs index ba1d4b7da8d8887b1604031497c300d7e0609cd2..1195c18f1336891c4b9b194d4e7= e5cd40989ace9 100644 --- a/rust/kernel/media/v4l2/mod.rs +++ b/rust/kernel/media/v4l2/mod.rs @@ -10,3 +10,6 @@ =20 /// Support for Video for Linux 2 (V4L2) video devices. pub mod video; + +/// Support for Video for Linux 2 (V4L2) file handles. +pub mod file; diff --git a/rust/kernel/media/v4l2/video.rs b/rust/kernel/media/v4l2/video= .rs index e6954d3e6ac65201bd40a0215babb354ae10cd12..7ef2111c32ca55a2bced8325cd8= 83b28204dc3ee 100644 --- a/rust/kernel/media/v4l2/video.rs +++ b/rust/kernel/media/v4l2/video.rs @@ -20,7 +20,7 @@ use crate::{ alloc, error::to_result, - media::v4l2::{self, video}, + media::v4l2::{self, file::DriverFile, video}, prelude::*, types::{ARef, AlwaysRefCounted, Opaque}, }; @@ -73,7 +73,6 @@ pub(super) fn as_raw(&self) -> *mut bindings::video_devic= e { /// /// - `ptr` must be a valid pointer to a `struct video_device` that mu= st /// remain valid for the lifetime 'a. - #[expect(dead_code)] pub(super) unsafe fn from_raw<'a>(ptr: *mut bindings::video_device) ->= &'a Device { // SAFETY: `ptr` is a valid pointer to a `struct video_device` as = per the // safety requirements of this function. @@ -148,6 +147,9 @@ pub trait Driver: v4l2::device::Driver { /// The type of the driver's private data. type Data; =20 + /// The driver's file type. + type File: DriverFile; + /// The [`NodeType`] to use when registering the device node. const NODE_TYPE: NodeType; =20 @@ -177,6 +179,7 @@ fn into_raw(self) -> bindings::video_device { }, vfl_dir: T::DIRECTION as c_uint, release: Some(Device::::release_callback), + fops: super::file::FileVtable::::build(), // SAFETY: All zeros is valid for the rest of the fields in th= is C // type. ..unsafe { MaybeUninit::zeroed().assume_init() } --=20 2.50.1 From nobody Sat Oct 4 11:14:58 2025 Received: from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com [136.143.188.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B266C272E7A; Mon, 18 Aug 2025 05:52:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.12 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496334; cv=pass; b=com/KiK7enC+ngoAfmZc1NDHFvadEFbqZYA5nxLompH9fXCRG7EIChXoQbI5EwKpLAonzueqHesD/ctv5e9B9DpSIuhItt57Np3Nku5c/SjPR++nUvjcPcLGAs3xf+xCGO7ZXC1QfR/95uQQCXazTooaxjPTGJZrEqq+3eVdqnk= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496334; c=relaxed/simple; bh=L/2KyPxOpZB6Rom7rVXTTsgrhWw+wi+XFk+cV1QYgYk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tulWtPx5yccoQ5epzF3063aGze0nai9+fUgzyWp+oZh4tS3ghi6wQTlPyBfYNkh9KvUTXJYwgVPTnxsHSMHmZE0beFqDtnJ7S25Rf5iSY7QzsPFgnnn9+A26DP1KGURz5zDVBibkyVmvmAe4hl33Gbh1VJbHvk/sKonWFermSSQ= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=PELjnkcN; arc=pass smtp.client-ip=136.143.188.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="PELjnkcN" ARC-Seal: i=1; a=rsa-sha256; t=1755496313; cv=none; d=zohomail.com; s=zohoarc; b=fHF9EBfemOSFS0n/Zn29xF2sRXbiEUtDorFXDND7IftjG/OnPrJkdD8FaBBhf6W6IG+DV2JpjYtD8NWBnDf4q8AT8HKuxZSHufE2J/zo84u6yir+TbJYHPV93IPT3F3PmRajXR6Cv+gJIcppyosbQJsuf+P8NQEmRfm3aELNFko= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1755496313; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=P0lNsew9CZa+TTrY7NxMZyTBfYHLLalCKPkfOAbfnh4=; b=RDPuwNt96XP1T5+tXMiwewscOGNxCGb2pquOrEIcHpISOa8B9/UqDiq5q5+2o2Qx3NXs2nb8feHlioZjBY7+EvMS9QuTCmEcEspWBrP1I84Ik/4SHB70nNJOez/bUVU/fe87Pqo2s7DWdpu9G1PkFJw6y2rGmZuIYolHKIqgKXI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1755496313; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=P0lNsew9CZa+TTrY7NxMZyTBfYHLLalCKPkfOAbfnh4=; b=PELjnkcNhoWoErNKDlauwSgLrvYdGfFOnl3LfnOLD2gw+G2nA+S0eHuhp3Nl38/e kpyZJY+m6O2vsk5evKmZl3ojOEqzrPUJxjohTOlq+M1kBfHkuJ+VgFc9MNZaiAXr2i6 ESnpLAqIwsRsYPVBvL6B8qPjSvQ3kv0yQSt0Qxjg= Received: by mx.zohomail.com with SMTPS id 1755496311664689.9369492276834; Sun, 17 Aug 2025 22:51:51 -0700 (PDT) From: Daniel Almeida Date: Mon, 18 Aug 2025 02:49:51 -0300 Subject: [PATCH 5/7] rust: v4l2: add device capabilities Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250818-v4l2-v1-5-6887e772aac2@collabora.com> References: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> In-Reply-To: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, kernel@collabora.com, linux-media@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External All v4l2 devices must expose a given set of capabilities to the v4l2 core and to userspace. Add support for that in v4l2::caps. This will be used by the next patch in order to add support for VIDIOC_QUERYCAP. Signed-off-by: Daniel Almeida Reviewed-by: Elle Rhumsaa --- rust/kernel/media/v4l2/caps.rs | 193 ++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/media/v4l2/mod.rs | 2 + rust/kernel/media/v4l2/video.rs | 6 +- 3 files changed, 200 insertions(+), 1 deletion(-) diff --git a/rust/kernel/media/v4l2/caps.rs b/rust/kernel/media/v4l2/caps.rs new file mode 100644 index 0000000000000000000000000000000000000000..4b0164c58d13e83e728091228fa= e025dbce59bc8 --- /dev/null +++ b/rust/kernel/media/v4l2/caps.rs @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-copyrightText: Copyright (C) 2025 Collabora Ltd. + +use crate::{prelude::*, str::CStr, types::Opaque}; +use core::cmp::min; + +/// A wrapper over `struct v4l2_capability`. +/// +/// # Invariants +/// +/// - `self.0` is a valid instance of `struct v4l2_capability`. +/// - All strings in `struct v4l2_capability` are valid C strings. +/// +/// TODO: This type would benefit from an #[derive(accessor)] macro to aut= omate +/// the boilerplate below. +#[repr(transparent)] +pub struct Capabilities(Opaque); + +impl Capabilities { + /// Returns the raw pointer to the `struct v4l2_capability`. + pub fn as_raw(&self) -> *const bindings::v4l2_capability { + self.0.get() + } + + /// Converts a raw pointer to a `Capabilities` reference. + /// + /// # Safety + /// + /// - `ptr` must be a valid pointer to a `struct v4l2_capability` that= must + /// remain valid for the lifetime 'a. + /// - the returned reference must obey Rust's reference rules. + pub unsafe fn from_raw<'a>(ptr: *mut bindings::v4l2_capability) -> &'a= mut Self { + // SAFETY: `ptr` is a valid pointer to a `struct v4l2_capability` = as per the + // safety requirements of this function. + unsafe { &mut *(ptr.cast::()) } + } + + fn inner(&self) -> &bindings::v4l2_capability { + // SAFETY: safe as per the invariants of `Capabilities` + unsafe { &*self.0.get() } + } + + fn inner_mut(&mut self) -> &mut bindings::v4l2_capability { + // SAFETY: safe as per the invariants of `Capabilities` + unsafe { &mut *self.0.get() } + } + + /// Returns the `driver` field. + pub fn driver(&self) -> &CStr { + // SAFETY: safe as per the invariants of `Capabilities` + unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner().driver)= } + } + + /// Sets the `driver` field. + pub fn set_driver(&mut self, name: &CStr) -> Result { + if name.len_with_nul() > self.inner().driver.len() { + return Err(EINVAL); + } + + let cap =3D self.inner_mut(); + let src =3D name.to_bytes_with_nul(); + let n =3D min(src.len(), cap.driver.len()); + cap.driver[..n].copy_from_slice(&src[..n]); + + Ok(()) + } + + /// Returns the `card` field. + pub fn card(&self) -> &CStr { + // SAFETY: safe as per the invariants of `Capabilities` + unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner().card) } + } + + /// Sets the `card` field. + pub fn set_card(&mut self, card: &CStr) -> Result { + if card.len_with_nul() > self.inner().card.len() { + return Err(EINVAL); + } + + let cap =3D self.inner_mut(); + let src =3D card.to_bytes_with_nul(); + let n =3D min(src.len(), cap.card.len()); + cap.card[..n].copy_from_slice(&src[..n]); + + Ok(()) + } + + /// Returns the `bus_info` field. + pub fn bus_info(&self) -> &CStr { + // SAFETY: safe as per the invariants of `Capabilities` + unsafe { CStr::from_bytes_with_nul_unchecked(&self.inner().bus_inf= o) } + } + + /// Sets the `bus_info` field. + pub fn set_bus_info(&mut self, info: &CStr) -> Result { + if info.len_with_nul() > self.inner().bus_info.len() { + return Err(EINVAL); + } + + let cap =3D self.inner_mut(); + let src =3D info.to_bytes_with_nul(); + let n =3D min(src.len(), cap.bus_info.len()); + cap.bus_info[..n].copy_from_slice(&src[..n]); + + Ok(()) + } + + /// Returns the `version` field. + pub fn version(&self) -> u32 { + self.inner().version + } + + /// Sets the `version` field. + pub fn set_version(&mut self, v: u32) { + self.inner_mut().version =3D v; + } + + /// Returns the `capabilities` field. + pub fn capabilities(&self) -> u32 { + self.inner().capabilities + } + + /// Sets the `capabilities` field. + pub fn set_capabilities(&mut self, caps: u32) { + self.inner_mut().capabilities =3D caps; + } + + /// Returns the `device_caps` field. + pub fn device_caps(&self) -> Option { + if self.inner().device_caps =3D=3D 0 { + None + } else { + Some(DeviceCaps(self.inner().device_caps)) + } + } + + /// Sets the `device_caps` field. + pub fn set_device_caps(&mut self, caps: DeviceCaps) { + self.inner_mut().device_caps =3D caps.as_raw(); + } +} + +/// Device capabilities. +/// +/// They can be combined with the operators `|`, `&`, and `!`. +/// +/// Values can be used from the [`device_caps`] module. +#[derive(Clone, Copy, PartialEq)] +pub struct DeviceCaps(u32); + +impl DeviceCaps { + /// Get the raw representation of the device capabilties. + pub(crate) fn as_raw(self) -> u32 { + self.0 + } + + /// Check whether `cap` is contained in `self`. + pub fn contains(self, cap: DeviceCaps) -> bool { + (self & cap) =3D=3D cap + } +} + +impl core::ops::BitOr for DeviceCaps { + type Output =3D Self; + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +impl core::ops::BitAnd for DeviceCaps { + type Output =3D Self; + fn bitand(self, rhs: Self) -> Self::Output { + Self(self.0 & rhs.0) + } +} + +impl core::ops::Not for DeviceCaps { + type Output =3D Self; + fn not(self) -> Self::Output { + Self(!self.0) + } +} + +/// Device capabilities. +pub mod device_caps { + use super::DeviceCaps; + + /// The device is a video capture device. + pub const VIDEO_CAPTURE: DeviceCaps =3D DeviceCaps(bindings::V4L2_CAP_= VIDEO_CAPTURE); + + /// The device is a video output device. + pub const VIDEO_OUTPUT: DeviceCaps =3D DeviceCaps(bindings::V4L2_CAP_V= IDEO_OUTPUT); +} diff --git a/rust/kernel/media/v4l2/mod.rs b/rust/kernel/media/v4l2/mod.rs index 1195c18f1336891c4b9b194d4e7e5cd40989ace9..1d8241f8a2230954371965bb91b= 20e726f144dce 100644 --- a/rust/kernel/media/v4l2/mod.rs +++ b/rust/kernel/media/v4l2/mod.rs @@ -11,5 +11,7 @@ /// Support for Video for Linux 2 (V4L2) video devices. pub mod video; =20 +/// Support for Video for Linux 2 device capabilities. +pub mod caps; /// Support for Video for Linux 2 (V4L2) file handles. pub mod file; diff --git a/rust/kernel/media/v4l2/video.rs b/rust/kernel/media/v4l2/video= .rs index 7ef2111c32ca55a2bced8325cd883b28204dc3ee..c0ac99a8234d2f7a8effd4701b9= f7440236540c8 100644 --- a/rust/kernel/media/v4l2/video.rs +++ b/rust/kernel/media/v4l2/video.rs @@ -20,7 +20,7 @@ use crate::{ alloc, error::to_result, - media::v4l2::{self, file::DriverFile, video}, + media::v4l2::{self, caps::DeviceCaps, file::DriverFile, video}, prelude::*, types::{ARef, AlwaysRefCounted, Opaque}, }; @@ -158,6 +158,9 @@ pub trait Driver: v4l2::device::Driver { =20 /// The name to use when registering the device node. const NAME: &'static CStr; + + /// The capabilities offered by this device node. + const CAPS: DeviceCaps; } =20 struct DeviceOptions<'a, T: Driver> { @@ -180,6 +183,7 @@ fn into_raw(self) -> bindings::video_device { vfl_dir: T::DIRECTION as c_uint, release: Some(Device::::release_callback), fops: super::file::FileVtable::::build(), + device_caps: T::CAPS.as_raw(), // SAFETY: All zeros is valid for the rest of the fields in th= is C // type. ..unsafe { MaybeUninit::zeroed().assume_init() } --=20 2.50.1 From nobody Sat Oct 4 11:14:58 2025 Received: from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com [136.143.188.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 28C85273809; Mon, 18 Aug 2025 05:52:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.12 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496338; cv=pass; b=Sy29bnBgD1vhNhEs5pR04qirasQxSbyopdl/XxRF7SsNSRelhl40nAf3e4hJPTJCkpGm4d9zXZt9w/ei4Or/NOY4hQ7w7Wt2QDYOwEGmSK3HDyzUR0+QPzf3bdhqOOejAMS/+n/oHLChMj7I3oFIGHda+1PVL92Y7aObhFM+KjE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496338; c=relaxed/simple; bh=6pqASn4SzC1u/BEBTbp+uXsGW36FexlGozR/smmOfcI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=KZmy6fYZXxUJSf1kYL+E2pvtrUem1udinTVSZzwWFPXozE31+RQUR19SRfZrTaqdap2Fbnd6tieJopC+FpHatPikSaT9hrq7YYUoczG8e4p+19ZUS3udM0WS3mI2gnzx6H0P+oRgjAQI87wIBICuaVHAa2VTwSMCNbbnLQjLhQo= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=NoLHIfYn; arc=pass smtp.client-ip=136.143.188.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="NoLHIfYn" ARC-Seal: i=1; a=rsa-sha256; t=1755496317; cv=none; d=zohomail.com; s=zohoarc; b=mzWN4mZ8qAXPbu8AcXjX570nUHCWkDddJEmjfhWLopaw7YdBsqHp77qYlDycbHYGOC6KJesuZGS+PQYTwvm7D3v99uVejYBg9H1C6sU/4TOSfpyzM8551FLuaShjN3xsqP7UgcdJeAycwuNHMLMcQb3tRSsu5jWyd8W3ICE/7Ko= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1755496317; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=FpKivAok/pykbmMpsZP4KwMmuHYi5cOWw+sKKTmE2sA=; b=BSJhqMJxRnX+Ahd5ODvZ+Bd38KOgPdzumg1cvUTtgjJ0yJnixecPeddLSO5dc7SqF/NvdwVYB+wfNf8u5hVF9MWqP9c4pAB4O1AnpR5nR0VJVxSBCzDz5J4223T6du8BKRKKn/KFu0qDGE4BpaU/QrlmnDroWKp9xQCB55qaEwc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1755496317; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=FpKivAok/pykbmMpsZP4KwMmuHYi5cOWw+sKKTmE2sA=; b=NoLHIfYnJEjh32gsoF0TH4eExEXJ7im0Gjhx7EFOp7e4VziBhDtHdatkzrMWEnZK yk1JOsk14LxN3BKlgtSQP8x3hhtSI8cFF3cTAWZsNSxEyCaM7nlpveyC2YOCk9WoYnu DA+tPqUQdbfx62deS7FBkZhG/NA3+XX4qjBhdX5c= Received: by mx.zohomail.com with SMTPS id 1755496315573741.0467126621251; Sun, 17 Aug 2025 22:51:55 -0700 (PDT) From: Daniel Almeida Date: Mon, 18 Aug 2025 02:49:52 -0300 Subject: [PATCH 6/7] rust: v4l2: add basic ioctl support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250818-v4l2-v1-6-6887e772aac2@collabora.com> References: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> In-Reply-To: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, kernel@collabora.com, linux-media@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External Most of the v4l2 API is implemented through ioctl(), so adding support for them is essential in order to be able to write v4l2 drivers. Hook up ioctl support by filling up an instance of v4l2_ioctl_ops. For now, we only support the most basic v4l2 ioctl: VIDIOC_QUERYCAPS. This is used by userspace to retrieve information about the device, and is considered enough to implement a simple v4l2 sample Rust driver. Signed-off-by: Daniel Almeida Reviewed-by: Elle Rhumsaa --- rust/kernel/media/v4l2/file.rs | 1 - rust/kernel/media/v4l2/ioctl.rs | 92 +++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/media/v4l2/mod.rs | 3 ++ rust/kernel/media/v4l2/video.rs | 13 +++++- 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/rust/kernel/media/v4l2/file.rs b/rust/kernel/media/v4l2/file.rs index 37b34f8e6f251fafde5f7e6b4bd654519d8247a5..8817051268323866f41fd56a0c7= e8fa4b7537858 100644 --- a/rust/kernel/media/v4l2/file.rs +++ b/rust/kernel/media/v4l2/file.rs @@ -50,7 +50,6 @@ impl File { /// /// - `ptr` must be a valid pointer to a `struct v4l2_file`. /// - `ptr` must be valid for 'a. - #[expect(dead_code)] pub(super) unsafe fn from_raw<'a>(ptr: *mut bindings::v4l2_fh) -> &'a = File { // SAFETY: `ptr` is a valid pointer to a `struct v4l2_file` as per= the // safety requirements of this function. diff --git a/rust/kernel/media/v4l2/ioctl.rs b/rust/kernel/media/v4l2/ioctl= .rs new file mode 100644 index 0000000000000000000000000000000000000000..e8d20d4cb70f5722c0109ea5bad= 36041355fc7a1 --- /dev/null +++ b/rust/kernel/media/v4l2/ioctl.rs @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-copyrightText: Copyright (C) 2025 Collabora Ltd. + +//! V4L2 device node ioctl support. +//! +//! Most of the V4L2 API is implemented through the ioctl system call. This +//! module provides support for ioctl operations on V4L2 device nodes. + +use core::{marker::PhantomData, mem::MaybeUninit}; + +use crate::{ + media::v4l2::{ + self, + video::{self, Driver}, + }, + prelude::*, +}; + +/// The vtable for the ioctls of a registered Rust [`video::Device`]. +/// +/// # Invariants +/// +/// - All the callbacks in [`IoctlVtable`] are called after the underlying +/// [`video::Device`] has been registered. +pub(super) struct IoctlVtable(PhantomData); + +impl IoctlVtable { + /// # Safety + /// + /// This should only be called from the ioctl callbacks and the return= ed + /// reference should not outlive the callback itself. + unsafe fn data<'a>(file: *mut bindings::file) -> &'a ::Da= ta + where + T: 'a, + { + // SAFETY: This was set during the video device registration proce= ss. + let vdev =3D unsafe { bindings::video_devdata(file) }; + + // SAFETY: `video_device` is a valid pointer to a `struct video_de= vice` + // returned by `bindings::video_devdata` and it is valid while the + // reference is alive. + unsafe { video::Device::::from_raw(vdev) } + } + + /// # Safety + /// + /// This should only be used as the `videoc_querycap` callback. + unsafe extern "C" fn vidioc_querycap_callback( + file: *mut bindings::file, + _fh: *mut c_void, + cap: *mut bindings::v4l2_capability, + ) -> core::ffi::c_int { + // SAFETY: this is being called from an ioctl callback and the ret= urned + // reference does not outlive it. + let data =3D unsafe { Self::data(file) }; + + // SAFETY: the fact that this is being called from an ioctl callba= ck means that: + // + // - the video device has been registered. + // - `open()` has been called (as you cannot call ioctl() on a fil= e that + // has not been previously opened). + // - as a result from the statement above, a valid `v4l2_fh` was + // installed in `bindings::file::private_data`, which we then conv= ert + // into `File` here. + // - `ptr` is valid for 'a, i.e.: for the scope of this function. + let file =3D unsafe { v4l2::file::File::::from_raw((*file= ).private_data.cast()) }; + + // SAFETY: the safety requirements ensure that `cap` is a valid po= inter + // to a `struct v4l2_capability`, which fulfills the requirements = of the + // `Capabilities::from_raw`. + let cap =3D unsafe { v4l2::caps::Capabilities::from_raw(cap) }; + + match T::querycap(file, data, cap) { + Ok(()) =3D> 0, + Err(err) =3D> err.to_errno(), + } + } + + const VTABLE: bindings::v4l2_ioctl_ops =3D bindings::v4l2_ioctl_ops { + vidioc_querycap: if T::HAS_QUERYCAP { + Some(Self::vidioc_querycap_callback) + } else { + None + }, + // SAFETY: All zeros is a valid value for `bindings::v4l2_ioctl_op= s`. + ..unsafe { MaybeUninit::zeroed().assume_init() } + }; + + pub(super) const fn build() -> &'static bindings::v4l2_ioctl_ops { + &Self::VTABLE + } +} diff --git a/rust/kernel/media/v4l2/mod.rs b/rust/kernel/media/v4l2/mod.rs index 1d8241f8a2230954371965bb91b20e726f144dce..4caa9fdc1ff3ed2bd6145cc653b= 49a84873ecac2 100644 --- a/rust/kernel/media/v4l2/mod.rs +++ b/rust/kernel/media/v4l2/mod.rs @@ -15,3 +15,6 @@ pub mod caps; /// Support for Video for Linux 2 (V4L2) file handles. pub mod file; + +/// Support for Video for Linux 2 (V4L2) ioctls. +pub mod ioctl; diff --git a/rust/kernel/media/v4l2/video.rs b/rust/kernel/media/v4l2/video= .rs index c0ac99a8234d2f7a8effd4701b9f7440236540c8..2390a93a39925dbff3f809bc65a= dfac1d881309c 100644 --- a/rust/kernel/media/v4l2/video.rs +++ b/rust/kernel/media/v4l2/video.rs @@ -19,7 +19,7 @@ =20 use crate::{ alloc, - error::to_result, + error::{to_result, VTABLE_DEFAULT_ERROR}, media::v4l2::{self, caps::DeviceCaps, file::DriverFile, video}, prelude::*, types::{ARef, AlwaysRefCounted, Opaque}, @@ -143,6 +143,7 @@ unsafe impl Sync for Device {} =20 /// The interface that must be implemented by structs that would otherwise= embed /// a C [`struct video_device`](srctree/include/media/v4l2-dev.h). +#[vtable] pub trait Driver: v4l2::device::Driver { /// The type of the driver's private data. type Data; @@ -161,6 +162,15 @@ pub trait Driver: v4l2::device::Driver { =20 /// The capabilities offered by this device node. const CAPS: DeviceCaps; + + /// Driver implementation for the `querycap` ioctl. + fn querycap( + _file: &v4l2::file::File<::File>, + _data: &::Data, + _cap: &mut v4l2::caps::Capabilities, + ) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } } =20 struct DeviceOptions<'a, T: Driver> { @@ -184,6 +194,7 @@ fn into_raw(self) -> bindings::video_device { release: Some(Device::::release_callback), fops: super::file::FileVtable::::build(), device_caps: T::CAPS.as_raw(), + ioctl_ops: super::ioctl::IoctlVtable::::build(), // SAFETY: All zeros is valid for the rest of the fields in th= is C // type. ..unsafe { MaybeUninit::zeroed().assume_init() } --=20 2.50.1 From nobody Sat Oct 4 11:14:58 2025 Received: from sender4-op-o12.zoho.com (sender4-op-o12.zoho.com [136.143.188.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A4442274653; Mon, 18 Aug 2025 05:52:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=pass smtp.client-ip=136.143.188.12 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496353; cv=pass; b=N9yh9BuGXoyLyFjHMOY+8boyiNGKa+OwZPQs5I1nPIJ6L0XhS6b6hBdUd8VQbbNk/8QCeyCTr3zcD2xiNXk54XC8dNviVYDXa2loe1IIQ1xdjS668mkh2NzS7BSmPUz0vujVi7h5dM0lIpfFD4yZyKuiswmVa8DcNYKgS/fhU8w= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1755496353; c=relaxed/simple; bh=2mC6huSFMyjKGY4Vs5To1W/7YJ7ioSWozaHjI30bZoU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=L3ZNy0+5ufQdvPI64/C1T3u7Isq2UhL9WNN6sLjFEOyHEmeuV96ThU06GCEcUwIAh1ji0g0NJf3P0Ywhin0iZxi1CwzEMWjMyDe9EicIy5mz/IgwtGtnnRCwCHCPp16Bbfk+Dc8/wZ4sZqY5OUOhjPE/iUXgV+aPh89H1EQ7L7w= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com; spf=pass smtp.mailfrom=collabora.com; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b=L9hkX4pJ; arc=pass smtp.client-ip=136.143.188.12 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=collabora.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=collabora.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=collabora.com header.i=daniel.almeida@collabora.com header.b="L9hkX4pJ" ARC-Seal: i=1; a=rsa-sha256; t=1755496323; cv=none; d=zohomail.com; s=zohoarc; b=lPdPAkwp6kzlNYjZBQKF/+tZBemZKJqPA6ntJ9QVPogNmwArMrAcF1tcUXHEKwV9ZrxXt+4mhowKw8UlDzQ1MAdn78usFpkgO4PLtAxiav6q0qjyvqSHEeUc1JHdF8A+CCZW7jqh6aLJUg70ce0qzO0rVpy73vcHKLQrjoPT+qM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1755496323; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:MIME-Version:Message-ID:References:Subject:Subject:To:To:Message-Id:Reply-To; bh=43OVXWZOAxCG43vn60JKBBXKXOTNgzmdXhw4qNDpMFY=; b=nSX7YXysXwn0lzPXVv0SELLCOOn0UYJyDE2AhiMyI7R9XfHgMTBpcxbP8YgdR9+SRgQ3qkUaibfcMvR9Lwhcv1l+//BIRYzxrkc2nhjxbqKab2w+x1Pos4XJ/zxVp6zf6buVp30PmDFPMXjoU5uqJcvKHQgeKPL9UnA91TJNwDM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=collabora.com; spf=pass smtp.mailfrom=daniel.almeida@collabora.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1755496323; s=zohomail; d=collabora.com; i=daniel.almeida@collabora.com; h=From:From:Date:Date:Subject:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Message-Id:Message-Id:References:In-Reply-To:To:To:Cc:Cc:Reply-To; bh=43OVXWZOAxCG43vn60JKBBXKXOTNgzmdXhw4qNDpMFY=; b=L9hkX4pJDmhw4LYLZ0BF9jh0P09/A0sBLrWRGjamIjhulY0irhki4XJpt/WrcxU+ fFzV1GXm5IFoNiYrYlKKIPDrgWwZbp7YbEDGSJFSd+jNnrX0iVtu4eTfcvO4hswX+TO /PWwH6/v94+CFUi239/c8riyp9GNcMbKJ9nJnV/M= Received: by mx.zohomail.com with SMTPS id 1755496320579535.6656496966112; Sun, 17 Aug 2025 22:52:00 -0700 (PDT) From: Daniel Almeida Date: Mon, 18 Aug 2025 02:49:53 -0300 Subject: [PATCH 7/7] rust: samples: add the v4l2 sample driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250818-v4l2-v1-7-6887e772aac2@collabora.com> References: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> In-Reply-To: <20250818-v4l2-v1-0-6887e772aac2@collabora.com> To: Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Alexandre Courbot Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, kernel@collabora.com, linux-media@vger.kernel.org, Daniel Almeida X-Mailer: b4 0.14.2 X-ZohoMailClient: External This driver is the only user of the v4l2 abstractions at the moment. It serves as a means to validate the abstractions by proving that the device is actually registered as /dev/videoX, and it can be opened and queried by v4l2-ctl, while also serving as a display of the current v4l2 support in Rust, as well as a blueprint for more elaborate Rust v4l2 drivers in the future. Signed-off-by: Daniel Almeida Reviewed-by: Elle Rhumsaa --- MAINTAINERS | 1 + samples/rust/Kconfig | 11 +++ samples/rust/Makefile | 1 + samples/rust/rust_driver_v4l2.rs | 145 +++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 158 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6fc5d57950e474d73d5d65271a0394efc5a8960b..14521bc0585503992da582f2cee= 361666985e39f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15440,6 +15440,7 @@ L: linux-media@vger.kernel.org L: rust-for-linux@vger.kernel.org S: Supported F: rust/media +F: sample/rust/rust_driver_v4l2.rs =20 MEDIATEK BLUETOOTH DRIVER M: Sean Wang diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 7f7371a004ee0a8f67dca99c836596709a70c4fa..64422acf1e9da9d05f904e14fd4= 23b3b4aef173a 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -105,6 +105,17 @@ config SAMPLE_RUST_DRIVER_AUXILIARY =20 If unsure, say N. =20 +config SAMPLE_RUST_DRIVER_V4L2 + tristate "Video4Linux2 driver" + depends on MEDIA_SUPPORT && VIDEO_DEV + help + This option builds the Rust V4L2 driver sample. + + To compile this as a module, choose M here: + the module will be called rust_driver_v4l2. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index bd2faad63b4f3befe7d1ed5139fe25c7a8b6d7f6..57e21f0373938bb70b4cb400ea5= 50010895b4c94 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_PCI) +=3D rust_driver_pci.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) +=3D rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) +=3D rust_driver_faux.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) +=3D rust_driver_auxiliary.o +obj-$(CONFIG_SAMPLE_RUST_DRIVER_V4L2) +=3D rust_driver_v4l2.o obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) +=3D rust_configfs.o =20 rust_print-y :=3D rust_print_main.o rust_print_events.o diff --git a/samples/rust/rust_driver_v4l2.rs b/samples/rust/rust_driver_v4= l2.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3ef98a613f2fed9e8589f0761c= e7e43029c02b6 --- /dev/null +++ b/samples/rust/rust_driver_v4l2.rs @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-copyrightText: Copyright (C) 2025 Collabora Ltd. + +//! Rust V4L2 sample driver. +//! +//! This sample demonstrates how to: +//! - Register a V4L2 device (struct v4l2_device), +//! - Register a video node (struct video_device) using the Rust V4L2 +//! abstractions, +//! - Implement support for a V4L2 ioctl in a driver. +//! +//! It implements only `VIDIOC_QUERYCAP` and minimal open/close handling. + +use kernel::{ + c_str, + device::Core, + media::v4l2::{ + self, + caps::{self, DeviceCaps}, + video, + }, + of, platform, + prelude::*, + types::ARef, + ThisModule, +}; + +/// The private data associated with the V4L2 device. +#[pin_data] +struct Data {} + +/// The private data associated with a V4L2 device node, i.e. `struct +/// video_device`. +#[pin_data] +struct VideoData {} + +/// The private data associated with a V4L2 file, i.e. `struct v4l2_fh`. +#[pin_data] +struct File {} + +impl v4l2::file::DriverFile for File { + type Driver =3D SampleDriver; + + const MODULE: &'static ThisModule =3D &THIS_MODULE; + + fn open(_vdev: &v4l2::video::Device) -> impl PinInit { + try_pin_init!(Self {}) + } +} + +struct SampleDriver { + _pdev: ARef, + _v4l2_reg: v4l2::device::Registration, + video_reg: video::Registration, +} + +impl v4l2::device::Driver for SampleDriver { + type Data =3D Data; +} + +#[vtable] +impl video::Driver for SampleDriver { + type Data =3D VideoData; + type File =3D File; + + const NODE_TYPE: video::NodeType =3D video::NodeType::Video; + const DIRECTION: video::Direction =3D video::Direction::Rx; + const NAME: &'static CStr =3D c_str!("rv4l2"); + const CAPS: DeviceCaps =3D caps::device_caps::VIDEO_CAPTURE; + + fn querycap( + _file: &v4l2::file::File, + _data: &::Data, + cap: &mut caps::Capabilities, + ) -> Result { + cap.set_driver(c_str!("rv4l2"))?; + cap.set_card(c_str!("rv4l2"))?; + cap.set_bus_info(c_str!("platform:rv4l2"))?; + + cap.set_device_caps(Self::CAPS); + Ok(()) + } +} + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("test, rust-v4l2")), ())] +); + +impl platform::Driver for SampleDriver { + type IdInfo =3D (); + const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); + + fn probe( + pdev: &platform::Device, + _info: Option<&Self::IdInfo>, + ) -> Result>> { + let dev =3D pdev.as_ref(); + + let v4l2_reg =3D + v4l2::device::Registration::::new(dev, try_pin_init!(Dat= a {}), GFP_KERNEL)?; + + let video_reg =3D video::Registration::::new( + v4l2_reg.device(), + try_pin_init!(VideoData {}), + GFP_KERNEL, + )?; + + let this =3D KBox::new( + Self { + _pdev: pdev.into(), + _v4l2_reg: v4l2_reg, + video_reg, + }, + GFP_KERNEL, + )?; + + dev_info!( + dev, + "Registered /dev/video{}\n", + this.video_reg.device().num() + ); + Ok(this.into()) + } + + fn unbind(pdev: &platform::Device, _this: Pin<&Self>) { + dev_info!(pdev.as_ref(), "Unbinding Rust V4L2 sample driver\n"); + } +} + +impl Drop for SampleDriver { + fn drop(&mut self) { + dev_dbg!(self._pdev.as_ref(), "Rust V4L2 sample driver removed\n"); + } +} + +kernel::module_platform_driver! { + type: SampleDriver, + name: "rust_driver_v4l2", + authors: ["Daniel Almeida"], + description: "Rust V4L2 sample video driver", + license: "GPL v2", +} --=20 2.50.1