linux-media.vger.kernel.org archive mirror
 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 6/7] rust: v4l2: add basic ioctl support
Date: Wed, 20 Aug 2025 04:22:31 +0000	[thread overview]
Message-ID: <aKVNh8HRe5ADbiSZ@archiso> (raw)
In-Reply-To: <20250818-v4l2-v1-6-6887e772aac2@collabora.com>

On Mon, Aug 18, 2025 at 02:49:52AM -0300, Daniel Almeida wrote:
> 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 <daniel.almeida@collabora.com>
> ---
>  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..8817051268323866f41fd56a0c7e8fa4b7537858 100644
> --- a/rust/kernel/media/v4l2/file.rs
> +++ b/rust/kernel/media/v4l2/file.rs
> @@ -50,7 +50,6 @@ impl<T: DriverFile> File<T> {
>      ///
>      /// - `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<T> {
>          // 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..e8d20d4cb70f5722c0109ea5bad36041355fc7a1
> --- /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<T: Driver>(PhantomData<T>);
> +
> +impl<T: Driver> IoctlVtable<T> {
> +    /// # Safety
> +    ///
> +    /// This should only be called from the ioctl callbacks and the returned
> +    /// reference should not outlive the callback itself.
> +    unsafe fn data<'a>(file: *mut bindings::file) -> &'a <T as Driver>::Data
> +    where
> +        T: 'a,
> +    {
> +        // SAFETY: This was set during the video device registration process.
> +        let vdev = unsafe { bindings::video_devdata(file) };
> +
> +        // SAFETY: `video_device` is a valid pointer to a `struct video_device`
> +        // returned by `bindings::video_devdata` and it is valid while the
> +        // reference is alive.
> +        unsafe { video::Device::<T>::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 returned
> +        // reference does not outlive it.
> +        let data = unsafe { Self::data(file) };
> +
> +        // SAFETY: the fact that this is being called from an ioctl callback means that:
> +        //
> +        // - the video device has been registered.
> +        // - `open()` has been called (as you cannot call ioctl() on a file 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 convert
> +        // into `File<T>` here.
> +        // - `ptr` is valid for 'a, i.e.: for the scope of this function.
> +        let file = unsafe { v4l2::file::File::<T::File>::from_raw((*file).private_data.cast()) };
> +
> +        // SAFETY: the safety requirements ensure that `cap` is a valid pointer
> +        // to a `struct v4l2_capability`, which fulfills the requirements of the
> +        // `Capabilities::from_raw`.
> +        let cap = unsafe { v4l2::caps::Capabilities::from_raw(cap) };
> +
> +        match T::querycap(file, data, cap) {
> +            Ok(()) => 0,
> +            Err(err) => err.to_errno(),
> +        }
> +    }
> +
> +    const VTABLE: bindings::v4l2_ioctl_ops = 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_ops`.
> +        ..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..4caa9fdc1ff3ed2bd6145cc653b49a84873ecac2 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..2390a93a39925dbff3f809bc65adfac1d881309c 100644
> --- a/rust/kernel/media/v4l2/video.rs
> +++ b/rust/kernel/media/v4l2/video.rs
> @@ -19,7 +19,7 @@
>  
>  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<T: Driver> Sync for Device<T> {}
>  
>  /// 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 {
>  
>      /// The capabilities offered by this device node.
>      const CAPS: DeviceCaps;
> +
> +    /// Driver implementation for the `querycap` ioctl.
> +    fn querycap(
> +        _file: &v4l2::file::File<<Self as Driver>::File>,
> +        _data: &<Self as Driver>::Data,
> +        _cap: &mut v4l2::caps::Capabilities,
> +    ) -> Result {
> +        build_error!(VTABLE_DEFAULT_ERROR)
> +    }
>  }
>  
>  struct DeviceOptions<'a, T: Driver> {
> @@ -184,6 +194,7 @@ fn into_raw(self) -> bindings::video_device {
>              release: Some(Device::<T>::release_callback),
>              fops: super::file::FileVtable::<T::File>::build(),
>              device_caps: T::CAPS.as_raw(),
> +            ioctl_ops: super::ioctl::IoctlVtable::<T>::build(),
>              // 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:22 UTC|newest]

Thread overview: 17+ 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
2025-08-18  5:49 ` [PATCH 6/7] rust: v4l2: add basic ioctl support Daniel Almeida
2025-08-20  4:22   ` Elle Rhumsaa [this message]
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

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=aKVNh8HRe5ADbiSZ@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).