lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250818-v4l2-v1-2-6887e772aac2@collabora.com>
Date: Mon, 18 Aug 2025 02:49:48 -0300
From: Daniel Almeida <daniel.almeida@...labora.com>
To: Miguel Ojeda <ojeda@...nel.org>, Alex Gaynor <alex.gaynor@...il.com>, 
 Boqun Feng <boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>, 
 Björn Roy Baron <bjorn3_gh@...tonmail.com>, 
 Benno Lossin <lossin@...nel.org>, Andreas Hindborg <a.hindborg@...nel.org>, 
 Alice Ryhl <aliceryhl@...gle.com>, Trevor Gross <tmgross@...ch.edu>, 
 Danilo Krummrich <dakr@...nel.org>, Alexandre Courbot <acourbot@...dia.com>
Cc: linux-kernel@...r.kernel.org, rust-for-linux@...r.kernel.org, 
 kernel@...labora.com, linux-media@...r.kernel.org, 
 Daniel Almeida <daniel.almeida@...labora.com>
Subject: [PATCH 2/7] rust: v4l2: add support for v4l2_device

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 <daniel.almeida@...labora.com>
---
 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_helper.h
index 84d60635e8a9baef1f1a1b2752dc0fa044f8542f..95651c4bc9e561d9f4949111961f41e65d8c1585 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -75,6 +75,7 @@
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/xarray.h>
+#include <media/v4l2-device.h>
 #include <trace/events/rust_sample.h>
 
 #if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 7cf7fe95e41dd51717050648d6160bebebdf4b26..83d7e76294207a804f2ad95097a1e4da53fe66f1 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..d19b46e8283ce762b4259e3df5ecf8bb18e863e9
--- /dev/null
+++ b/rust/helpers/v4l2-device.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <media/v4l2-device.h>
+
+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..476ea673867121fb68fd4695c2cddc5380e86421 100644
--- a/rust/kernel/media/mod.rs
+++ b/rust/kernel/media/mod.rs
@@ -3,4 +3,7 @@
 
 //! Media infrastructure support.
 //!
-//! Reference: <https://www.kernel.org/doc/html/latest/driver-api/media/index.html>
\ No newline at end of file
+//! Reference: <https://www.kernel.org/doc/html/latest/driver-api/media/index.html>
+
+#[cfg(CONFIG_VIDEO_DEV = "y")]
+pub mod v4l2;
diff --git a/rust/kernel/media/v4l2/device.rs b/rust/kernel/media/v4l2/device.rs
new file mode 100644
index 0000000000000000000000000000000000000000..26096672e6f6d35711ff9bdabf4d7b20f697a4ab
--- /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 subdevices
+//! (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<T: Driver> {
+    #[pin]
+    inner: Opaque<bindings::v4l2_device>,
+    #[pin]
+    data: T::Data,
+}
+
+impl<T: Driver> Device<T> {
+    /// Converts a raw pointer to a `Device<T>` 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<T> {
+        // SAFETY: `ptr` is a valid pointer to a `struct v4l2_device` as per the
+        // safety requirements of this function.
+        unsafe { &*(ptr.cast::<Device<T>>()) }
+    }
+
+    /// 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 v4l2_device`.
+    unsafe extern "C" fn release_callback(dev: *mut bindings::v4l2_device) {
+        // SAFETY: `dev` was set by calling `KBox::into_raw` on a
+        // `Pin<KBox<Device<T>>` in `Registration::new`. Now that the refcount
+        // reached zero, we are reassembling the KBox so it can be dropped.
+        let v4l2_dev: Pin<KBox<Device<T>>> =
+            unsafe { Pin::new_unchecked(Box::from_raw(dev.cast())) };
+
+        drop(v4l2_dev)
+    }
+}
+
+impl<T: Driver> AsRef<device::Device> for Device<T> {
+    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 functions
+// satisfy the requirements.
+unsafe impl<T: Driver> AlwaysRefCounted for Device<T> {
+    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<Self>) {
+        // SAFETY: Safe as per the invariants of `Device`.
+        unsafe { bindings::v4l2_device_put(obj.cast().as_ptr()) };
+    }
+}
+
+/// SAFETY: It is safe to send `Device<T>` to another thread. In particular,
+/// `Device<T>` instances can be dropped from any thread.
+unsafe impl<T: Driver> Send for Device<T> {}
+
+// SAFETY: it is safe to share references to `Device<T>` between threads as it
+// is not possible to mutate the underlying `struct v4l2_device` through a
+// shared reference.
+unsafe impl<T: Driver> Sync for Device<T> {}
+
+/// 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_register`].
+pub struct Registration<T: Driver>(ARef<Device<T>>);
+
+impl<T: Driver> Registration<T> {
+    /// Creates and registers a [`Device`] given a [`kernel::device::Device`] reference and
+    /// the associated data.
+    pub fn new(
+        dev: &device::Device,
+        data: impl PinInit<T::Data, Error>,
+        flags: alloc::Flags,
+    ) -> Result<Self> {
+        let v4l2_dev = try_pin_init!(Device {
+            inner <- Opaque::try_ffi_init(move |slot: *mut bindings::v4l2_device| {
+                let v4l2_dev = bindings::v4l2_device {
+                    release: Some(Device::<T>::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_raw(), slot) })
+            }),
+            data <- data,
+        });
+
+        let v4l2_dev = KBox::pin_init(v4l2_dev, flags)?;
+
+        // SAFETY: We will be passing the ownership of the increment to ARef<T>,
+        // which treats the underlying memory as pinned throughout its lifetime.
+        //
+        // This is true because:
+        //
+        // - ARef<T> 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<T>'s member functions do not move the T either.
+        let ptr = 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<T> {
+        &self.0
+    }
+}
+
+impl<T: Driver> Drop for Registration<T> {
+    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..63394d0322fa1f646f3b23a5fadf2ac34a9f666e
--- /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/latest/driver-api/media/v4l2-intro.html#structure-of-the-v4l2-framework
+/// Support for Video for Linux 2 (V4L2) devices.
+pub mod device;

-- 
2.50.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ