All of lore.kernel.org
 help / color / mirror / Atom feed
From: Elle Rhumsaa <elle@weathered-steel.dev>
To: Daniel Almeida <daniel.almeida@collabora.com>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>,
	"Alexandre Courbot" <acourbot@nvidia.com>,
	linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
	kernel@collabora.com, linux-media@vger.kernel.org
Subject: Re: [PATCH 5/7] rust: v4l2: add device capabilities
Date: Wed, 20 Aug 2025 04:14:59 +0000	[thread overview]
Message-ID: <aKVLw_ouLWezuWJF@archiso> (raw)
In-Reply-To: <20250818-v4l2-v1-5-6887e772aac2@collabora.com>

On Mon, Aug 18, 2025 at 02:49:51AM -0300, Daniel Almeida wrote:
> 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 <daniel.almeida@collabora.com>
> ---
>  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..4b0164c58d13e83e728091228fae025dbce59bc8
> --- /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 automate
> +/// the boilerplate below.
> +#[repr(transparent)]
> +pub struct Capabilities(Opaque<bindings::v4l2_capability>);
> +
> +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::<Self>()) }
> +    }
> +
> +    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 = self.inner_mut();
> +        let src = name.to_bytes_with_nul();
> +        let n = 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 = self.inner_mut();
> +        let src = card.to_bytes_with_nul();
> +        let n = 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_info) }
> +    }
> +
> +    /// 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 = self.inner_mut();
> +        let src = info.to_bytes_with_nul();
> +        let n = 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 = 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 = caps;
> +    }
> +
> +    /// Returns the `device_caps` field.
> +    pub fn device_caps(&self) -> Option<DeviceCaps> {
> +        if self.inner().device_caps == 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 = 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) == cap
> +    }
> +}
> +
> +impl core::ops::BitOr for DeviceCaps {
> +    type Output = Self;
> +    fn bitor(self, rhs: Self) -> Self::Output {
> +        Self(self.0 | rhs.0)
> +    }
> +}
> +
> +impl core::ops::BitAnd for DeviceCaps {
> +    type Output = Self;
> +    fn bitand(self, rhs: Self) -> Self::Output {
> +        Self(self.0 & rhs.0)
> +    }
> +}
> +
> +impl core::ops::Not for DeviceCaps {
> +    type Output = 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 = DeviceCaps(bindings::V4L2_CAP_VIDEO_CAPTURE);
> +
> +    /// The device is a video output device.
> +    pub const VIDEO_OUTPUT: DeviceCaps = DeviceCaps(bindings::V4L2_CAP_VIDEO_OUTPUT);
> +}

These can probably be implemented as associated constants:

```rust
impl DeviceCaps {
    pub const VIDEO_CAPTURE: Self = Self(bindings::V4L2_CAP_VIDEO_CAPTURE);
    pub const VIDEO_OUTPUT: Self = Self(bindings::V4L2_CAP_VIDEO_OUTPUT);
```

That would allow for a slightly more ergonomic usage:

```rust
let _ = DeviceCaps::VIDEO_CAPTURE;
```

> diff --git a/rust/kernel/media/v4l2/mod.rs b/rust/kernel/media/v4l2/mod.rs
> index 1195c18f1336891c4b9b194d4e7e5cd40989ace9..1d8241f8a2230954371965bb91b20e726f144dce 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;
>  
> +/// 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..c0ac99a8234d2f7a8effd4701b9f7440236540c8 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 {
>  
>      /// The name to use when registering the device node.
>      const NAME: &'static CStr;
> +
> +    /// The capabilities offered by this device node.
> +    const CAPS: DeviceCaps;
>  }
>  
>  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::<T>::release_callback),
>              fops: super::file::FileVtable::<T::File>::build(),
> +            device_caps: T::CAPS.as_raw(),
>              // SAFETY: All zeros is valid for the rest of the fields in this C
>              // type.
>              ..unsafe { MaybeUninit::zeroed().assume_init() }
> 
> -- 
> 2.50.1

Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>

  reply	other threads:[~2025-08-20  4:17 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-18  5:49 [PATCH 0/7] rust: add initial v4l2 support Daniel Almeida
2025-08-18  5:49 ` [PATCH 1/7] rust: media: add the media module Daniel Almeida
2025-08-18  8:56   ` Miguel Ojeda
2025-08-18 10:28   ` Janne Grunau
2025-08-18  5:49 ` [PATCH 2/7] rust: v4l2: add support for v4l2_device Daniel Almeida
2025-08-18  9:14   ` Danilo Krummrich
2025-08-18  5:49 ` [PATCH 3/7] rust: v4l2: add support for video device nodes Daniel Almeida
2025-08-18  9:26   ` Danilo Krummrich
2025-08-18  5:49 ` [PATCH 4/7] rust: v4l2: add support for v4l2 file handles Daniel Almeida
2025-08-18  5:49 ` [PATCH 5/7] rust: v4l2: add device capabilities Daniel Almeida
2025-08-20  4:14   ` Elle Rhumsaa [this message]
2025-08-18  5:49 ` [PATCH 6/7] rust: v4l2: add basic ioctl support Daniel Almeida
2025-08-20  4:22   ` Elle Rhumsaa
2025-08-18  5:49 ` [PATCH 7/7] rust: samples: add the v4l2 sample driver Daniel Almeida
2025-08-20  4:24   ` Elle Rhumsaa
2025-08-20 12:39   ` Danilo Krummrich
2025-08-18  8:45 ` [PATCH 0/7] rust: add initial v4l2 support Miguel Ojeda
2025-12-16 17:03 ` Frederic Laing
2025-12-17 14:35   ` Daniel Almeida
2025-12-18 10:12     ` Hans Verkuil

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aKVLw_ouLWezuWJF@archiso \
    --to=elle@weathered-steel.dev \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=gary@garyguo.net \
    --cc=kernel@collabora.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.