lkml.org 
[lkml]   [2023]   [Apr]   [6]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5/6] rust: media: add {video|v4l2}_device_register support
    Date
    Add {video|v4l2}_device_register support, essentially introducing
    (initial) support for v4l2 drivers in Rust.

    Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
    ---
    rust/bindings/bindings_helper.h | 4 +
    rust/kernel/media/v4l2/dev.rs | 369 +++++++++++++++++++
    rust/kernel/media/v4l2/device.rs | 115 ++++++
    rust/kernel/media/v4l2/ioctls.rs | 608 +++++++++++++++++++++++++++++++
    rust/kernel/media/v4l2/mod.rs | 3 +
    5 files changed, 1099 insertions(+)
    create mode 100644 rust/kernel/media/v4l2/dev.rs
    create mode 100644 rust/kernel/media/v4l2/device.rs
    create mode 100644 rust/kernel/media/v4l2/ioctls.rs

    diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
    index 3153894f426b..bf2e833e511c 100644
    --- a/rust/bindings/bindings_helper.h
    +++ b/rust/bindings/bindings_helper.h
    @@ -36,6 +36,10 @@
    #include <linux/uaccess.h>
    #include <linux/uio.h>
    #include <linux/videodev2.h>
    +#include <media/v4l2-dev.h>
    +#include <media/v4l2-device.h>
    +#include <media/v4l2-fh.h>
    +#include <media/v4l2-ioctl.h>
    #include <media/videobuf2-core.h>
    #include <media/videobuf2-v4l2.h>
    #include <media/videobuf2-dma-sg.h>
    diff --git a/rust/kernel/media/v4l2/dev.rs b/rust/kernel/media/v4l2/dev.rs
    new file mode 100644
    index 000000000000..32a7573f3439
    --- /dev/null
    +++ b/rust/kernel/media/v4l2/dev.rs
    @@ -0,0 +1,369 @@
    +// SPDX-License-Identifier: GPL-2.0
    +
    +//! V4L2 Driver Helper API
    +//!
    +//! C header: [`include/media/v4l2-dev.h`](../../../../include/media/v4l2-dev.h)
    +
    +use core::marker::PhantomData;
    +use core::pin::Pin;
    +
    +use crate::device;
    +use crate::media::v4l2::capabilities;
    +use crate::media::v4l2::device::V4l2Device;
    +use crate::prelude::*;
    +use crate::sync::smutex::Mutex;
    +use crate::sync::Arc;
    +use crate::ForeignOwnable;
    +use crate::Result;
    +use crate::ThisModule;
    +
    +/// The driver trait to be implemented by driver authors.
    +pub trait Driver: crate::media::v4l2::ioctls::Operations<PrivateData = Self::DriverData> {
    + /// The driver's private data.
    + type DriverData: ForeignOwnable + Sync + Send;
    +}
    +
    +/// VflDevNodeTypes as described by `VFL_TYPE_*` constants.
    +#[allow(missing_docs)]
    +pub enum VflDevNodeType {
    + Video = bindings::vfl_devnode_type_VFL_TYPE_VIDEO as isize,
    + Vbi = bindings::vfl_devnode_type_VFL_TYPE_VBI as isize,
    + Radio = bindings::vfl_devnode_type_VFL_TYPE_RADIO as isize,
    + Subdev = bindings::vfl_devnode_type_VFL_TYPE_SUBDEV as isize,
    + Sdr = bindings::vfl_devnode_type_VFL_TYPE_SDR as isize,
    + Touch = bindings::vfl_devnode_type_VFL_TYPE_TOUCH as isize,
    +}
    +
    +/// A Video Device.
    +pub struct VideoDevice<T> {
    + /// A raw pointer to `struct video_device` in C.
    + pub(crate) device: bindings::video_device,
    + /// A phantom for T. We need T to access the associated constants set by
    + /// drivers.
    + pub(crate) _phantom: PhantomData<T>,
    + /// Associated V4l2Device that must be kept alive while the VideoDevice is
    + /// in use.
    + _v4l2_device: Arc<Mutex<V4l2Device>>,
    +}
    +
    +impl<T> VideoDevice<T> {
    + fn new(v4l2_device: Arc<Mutex<V4l2Device>>) -> Result<Self> {
    + let mut device = bindings::video_device::default();
    +
    + let mut guard = v4l2_device.as_ref().lock();
    + let inner = &mut *guard;
    + let v4l2_device_ptr = inner.device_mut() as *mut bindings::v4l2_device;
    +
    + device.v4l2_dev = v4l2_device_ptr;
    + drop(guard);
    +
    + Ok(Self {
    + device,
    + _v4l2_device: v4l2_device,
    + _phantom: PhantomData,
    + })
    + }
    +
    + fn _raw(&self) -> &bindings::video_device {
    + &self.device
    + }
    +
    + fn raw_mut(&mut self) -> &mut bindings::video_device {
    + &mut self.device
    + }
    +}
    +
    +impl<T> Drop for VideoDevice<T> {
    + fn drop(&mut self) {
    + unsafe { bindings::video_device_release(self.raw_mut()) }
    + }
    +}
    +
    +/// Functions to be passed to the `fops` member.
    +#[derive(Default)]
    +#[allow(missing_docs)]
    +pub struct V4l2FileOperations {
    + pub read: Option<
    + unsafe extern "C" fn(
    + arg1: *mut bindings::file,
    + arg2: *mut core::ffi::c_char,
    + arg3: usize,
    + arg4: *mut bindings::loff_t,
    + ) -> isize,
    + >,
    + pub poll: Option<
    + unsafe extern "C" fn(
    + arg1: *mut bindings::file,
    + arg2: *mut bindings::poll_table_struct,
    + ) -> bindings::__poll_t,
    + >,
    + pub mmap: Option<
    + unsafe extern "C" fn(
    + arg1: *mut bindings::file,
    + arg2: *mut bindings::vm_area_struct,
    + ) -> core::ffi::c_int,
    + >,
    + pub open: Option<unsafe extern "C" fn(arg1: *mut bindings::file) -> core::ffi::c_int>,
    +}
    +
    +/// Functions to be passed to the `ioctl` member.
    +#[derive(Default)]
    +#[allow(missing_docs)]
    +pub struct V4l2IoctlOperations {
    + pub reqbufs: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + b: *mut bindings::v4l2_requestbuffers,
    + ) -> core::ffi::c_int,
    + >,
    + pub querybuf: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + b: *mut bindings::v4l2_buffer,
    + ) -> core::ffi::c_int,
    + >,
    + pub qbuf: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + b: *mut bindings::v4l2_buffer,
    + ) -> core::ffi::c_int,
    + >,
    + pub expbuf: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + e: *mut bindings::v4l2_exportbuffer,
    + ) -> core::ffi::c_int,
    + >,
    + pub dqbuf: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + b: *mut bindings::v4l2_buffer,
    + ) -> core::ffi::c_int,
    + >,
    + pub create_bufs: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + b: *mut bindings::v4l2_create_buffers,
    + ) -> core::ffi::c_int,
    + >,
    + pub prepare_buf: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + b: *mut bindings::v4l2_buffer,
    + ) -> core::ffi::c_int,
    + >,
    + pub streamon: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + i: bindings::v4l2_buf_type,
    + ) -> core::ffi::c_int,
    + >,
    + pub streamoff: Option<
    + unsafe extern "C" fn(
    + file: *mut bindings::file,
    + fh: *mut core::ffi::c_void,
    + i: bindings::v4l2_buf_type,
    + ) -> core::ffi::c_int,
    + >,
    +}
    +
    +/// The video registration struct. See `video_device_register!`.
    +pub struct VideoRegistration<T: Driver> {
    + device: VideoDevice<T>,
    + registered: bool,
    + fops: bindings::v4l2_file_operations,
    + ioctls: bindings::v4l2_ioctl_ops,
    +}
    +
    +impl<T: Driver> VideoRegistration<T> {
    + /// Creates a new `VideoRegistration`. The driver can manually choose
    + /// alternate ioctl implementations using the `ioctl_operations` argument.
    + pub fn new(
    + _parent: &dyn device::RawDevice,
    + v4l2_device: Arc<Mutex<V4l2Device>>,
    + file_operations: V4l2FileOperations,
    + ioctl_operations: V4l2IoctlOperations,
    + ) -> Result<Self> {
    + let video_device = VideoDevice::new(v4l2_device)?;
    +
    + let fops = bindings::v4l2_file_operations {
    + owner: core::ptr::null_mut(),
    + read: file_operations.read,
    + write: None,
    + poll: file_operations.poll,
    + unlocked_ioctl: Some(bindings::video_ioctl2),
    + compat_ioctl32: None,
    + get_unmapped_area: None,
    + mmap: file_operations.mmap,
    + open: file_operations.open,
    + //file_operations::release() is not mandatory, let's try to release things on drop.
    + release: None,
    + };
    +
    + let mut ioctls = *crate::media::v4l2::ioctls::OperationsVtable::<T>::build();
    +
    + if let Some(op) = &ioctl_operations.reqbufs {
    + ioctls.vidioc_reqbufs = Some(*op);
    + }
    +
    + if let Some(op) = &ioctl_operations.querybuf {
    + ioctls.vidioc_querybuf = Some(*op);
    + }
    +
    + if let Some(op) = &ioctl_operations.qbuf {
    + ioctls.vidioc_qbuf = Some(*op);
    + }
    +
    + if let Some(op) = &ioctl_operations.expbuf {
    + ioctls.vidioc_expbuf = Some(*op);
    + }
    +
    + if let Some(op) = &ioctl_operations.dqbuf {
    + ioctls.vidioc_dqbuf = Some(*op);
    + }
    +
    + if let Some(op) = &ioctl_operations.create_bufs {
    + ioctls.vidioc_create_bufs = Some(*op);
    + }
    +
    + if let Some(op) = &ioctl_operations.prepare_buf {
    + ioctls.vidioc_prepare_buf = Some(*op)
    + }
    +
    + if let Some(op) = &ioctl_operations.streamon {
    + ioctls.vidioc_streamon = Some(*op);
    + }
    +
    + if let Some(op) = &ioctl_operations.streamoff {
    + ioctls.vidioc_streamoff = Some(*op);
    + }
    +
    + Ok(Self {
    + device: video_device,
    + registered: false,
    + fops,
    + ioctls,
    + })
    + }
    +
    + /// Registers a video device with the rest of the kernel.
    + ///
    + /// Users are encouraged to use the [`video_device_register`] macro because it automatically
    + /// picks up THIS_MODULE.
    + pub fn register(
    + self: Pin<&mut Self>,
    + data: T::DriverData,
    + vfl_devnode_type: VflDevNodeType,
    + nr: i32,
    + module: &'static ThisModule,
    + capabilities: &[capabilities::Capabilities],
    + ) -> Result {
    + if self.registered {
    + // Already registered.
    + return Err(EINVAL);
    + }
    +
    + // SAFETY: We never move out of `this` in this method.
    + let this = unsafe { self.get_unchecked_mut() };
    + let video_device = this.device.raw_mut();
    +
    + let mut caps = 0;
    + for capability in capabilities {
    + caps |= *capability as u32;
    + }
    +
    + video_device.device_caps = caps;
    +
    + // We can release our resources on drop.
    + video_device.release = Some(bindings::video_device_release_empty);
    +
    + let data_pointer = <T::DriverData as ForeignOwnable>::into_foreign(data);
    +
    + this.fops.owner = module.0;
    + video_device.fops = &this.fops;
    + video_device.ioctl_ops = &this.ioctls;
    +
    + // SAFETY: video_device was properly allocated during video_register_device
    + // Use a single unsafe block based on the safety comment above to avoid
    + // repetition of unsafe on pointer accesses below.
    + unsafe {
    + let ret = bindings::__video_register_device(
    + video_device,
    + vfl_devnode_type as _,
    + nr,
    + 1,
    + module.0,
    + );
    +
    + if ret < 0 {
    + // Reassemble into ForeignOwnable so it can be dropped.
    + // SAFETY: `data_pointer` was returned by `into_foreign` above.
    + T::DriverData::from_foreign(data_pointer);
    + return Err(Error::from_kernel_errno(ret));
    + }
    +
    + let video_device_parent = &mut video_device.dev;
    + video_device_parent.driver_data = data_pointer as _;
    + }
    +
    + this.registered = true;
    + Ok(())
    + }
    +}
    +
    +impl<T: Driver> Drop for VideoRegistration<T> {
    + fn drop(&mut self) {
    + if self.registered {
    + // SAFETY: device has been properly initialized as per the type invariant.
    + let video_device_parent = &mut self.device.raw_mut().dev;
    +
    + // Obtain a pointer to this before freeing, so it can be assembled
    + // back into ForeignOwnable and then dropped.
    + let data_pointer = video_device_parent.driver_data;
    +
    + // SAFETY: pointer was properly allocated in VideoDevice::new()
    + unsafe { bindings::video_unregister_device(self.device.raw_mut()) }
    +
    + // Free data as well.
    + // SAFETY: `data_pointer` was returned by `into_foreign` during registration.
    + unsafe { <T::DriverData as ForeignOwnable>::from_foreign(data_pointer) };
    + }
    + }
    +}
    +
    +// SAFETY: `Registration` doesn't offer any methods or access to fields when
    +// shared between threads or CPUs, so it is safe to share it.
    +unsafe impl<T: Driver> Sync for VideoRegistration<T> {}
    +
    +// SAFETY: Registration and unregistration from the v4l2 subsystem can happen
    +// from any thread. Additionally, `T::Data` (which is dropped during
    +// unregistration) is `Send`, so it is ok to move `Registration` to different
    +// threads.
    +#[allow(clippy::non_send_fields_in_send_ty)]
    +unsafe impl<T: Driver> Send for VideoRegistration<T> {}
    +
    +/// Registers a video device with the rest of the kernel.
    +///
    +/// It automatically picks up THIS_MODULE.
    +#[allow(clippy::crate_in_macro_def)]
    +#[macro_export]
    +macro_rules! video_device_register {
    + ($reg:expr, $data:expr, $vfl_devnode_type:expr, $nr:expr, $capabilities:expr $(,)?) => {{
    + $crate::media::v4l2::dev::VideoRegistration::register(
    + $reg,
    + $data,
    + $vfl_devnode_type,
    + $nr,
    + &crate::THIS_MODULE,
    + $capabilities,
    + )
    + }};
    +}
    diff --git a/rust/kernel/media/v4l2/device.rs b/rust/kernel/media/v4l2/device.rs
    new file mode 100644
    index 000000000000..d7fafab787a6
    --- /dev/null
    +++ b/rust/kernel/media/v4l2/device.rs
    @@ -0,0 +1,115 @@
    +// SPDX-License-Identifier: GPL-2.0
    +#![allow(missing_docs)]
    +
    +//! V4L2 Device support helper.
    +//!
    +//! C header: [`include/media/v4l2-device.h`](../../../../include/media/v4l2-device.h)
    +
    +use core::cell::UnsafeCell;
    +use core::pin::Pin;
    +
    +use crate::device;
    +use crate::error;
    +use crate::prelude::*;
    +use crate::sync::smutex::Mutex;
    +use crate::sync::Arc;
    +use crate::ThisModule;
    +
    +/// A V4L2 device.
    +pub struct V4l2Device {
    + /// The underlying `struct v4l2_device` in C.
    + device: UnsafeCell<bindings::v4l2_device>,
    +}
    +
    +impl V4l2Device {
    + fn new() -> Self {
    + Self {
    + device: Default::default(),
    + }
    + }
    +
    + pub(crate) fn device_mut(&mut self) -> &mut bindings::v4l2_device {
    + self.device.get_mut()
    + }
    +}
    +
    +pub struct V4l2Registration {
    + device: Arc<Mutex<V4l2Device>>,
    + registered: bool,
    +}
    +
    +impl V4l2Registration {
    + pub fn new() -> Result<Self> {
    + let device = Arc::try_new(Mutex::new(V4l2Device::new()))?;
    + Ok(Self {
    + device,
    + registered: false,
    + })
    + }
    +
    + /// Registers a video device with the rest of the kernel.
    + ///
    + /// Users are encouraged to use the [`v4l2_device_register`] macro because it automatically
    + /// picks up THIS_MODULE.
    + pub fn register(
    + self: Pin<&mut Self>,
    + _module: &'static ThisModule,
    + parent: &dyn device::RawDevice,
    + ) -> Result {
    + if self.registered {
    + // Already registered.
    + return Err(EINVAL);
    + }
    +
    + // SAFETY: We never move out of `this` in this method.
    + let this = unsafe { self.get_unchecked_mut() };
    +
    + let device = UnsafeCell::new(bindings::v4l2_device::default());
    +
    + // SAFETY: We know that `RawDevice` is valid due to its safety
    + // requirements. Furthermore, `device` has been stack-allocated above
    + // and thus it is valid.
    + let res = unsafe { bindings::v4l2_device_register(parent.raw_device(), device.get()) };
    + error::to_result(res)?;
    +
    + this.device.as_ref().lock().device = device;
    + this.registered = true;
    + Ok(())
    + }
    +
    + pub fn device(&self) -> Arc<Mutex<V4l2Device>> {
    + self.device.clone()
    + }
    +}
    +
    +impl Drop for V4l2Registration {
    + fn drop(&mut self) {
    + if self.registered {
    + let mut device = self.device.as_ref().lock();
    + let v4l2_dev = &mut *device;
    +
    + unsafe { bindings::v4l2_device_unregister(v4l2_dev.device.get()) }
    + }
    + }
    +}
    +
    +// SAFETY: The only field made available by `Registration` is Send + Sync.
    +// Other than that, it doesn't offer any methods or access to fields when shared
    +// between threads or CPUs, so it is safe to share it.
    +unsafe impl Sync for V4l2Registration {}
    +
    +// SAFETY: Registration and unregistration from the v4l2 subsystem can happen
    +// from any thread.
    +#[allow(clippy::non_send_fields_in_send_ty)]
    +unsafe impl Send for V4l2Registration {}
    +
    +/// Registers a V4L2 device with the rest of the kernel.
    +///
    +/// It automatically picks up THIS_MODULE.
    +#[allow(clippy::crate_in_macro_def)]
    +#[macro_export]
    +macro_rules! v4l2_device_register {
    + ($reg:expr, $parent:expr $(,)?) => {{
    + $crate::media::v4l2::device::V4l2Registration::register($reg, &crate::THIS_MODULE, $parent)
    + }};
    +}
    diff --git a/rust/kernel/media/v4l2/ioctls.rs b/rust/kernel/media/v4l2/ioctls.rs
    new file mode 100644
    index 000000000000..7c2b45c45ad6
    --- /dev/null
    +++ b/rust/kernel/media/v4l2/ioctls.rs
    @@ -0,0 +1,608 @@
    +// SPDX-License-Identifier: GPL-2.0
    +#![allow(missing_docs)]
    +
    +//! V4L2 Ioctls
    +//!
    +//! C header: [`include/media/v4l2-ioctls.h`](../../../../include/media/v4l2-ioctls.h)
    +
    +use bindings::*;
    +use core::marker::PhantomData;
    +
    +use crate::error::from_kernel_result;
    +use crate::media::v4l2;
    +use crate::prelude::*;
    +use crate::ForeignOwnable;
    +
    +#[vtable]
    +pub trait Operations {
    + type PrivateData: ForeignOwnable;
    +
    + fn vidioc_querycap(
    + _private_data: &mut Self::PrivateData,
    + _cap: v4l2::capabilities::CapabilitiesRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_enum_fmt_vid_cap(
    + _private_data: &mut Self::PrivateData,
    + _f: v4l2::format::FormatDescRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_enum_framesizes(
    + _private_data: &mut Self::PrivateData,
    + _fsize: v4l2::framesize::FrameSizeRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_g_fmt_vid_cap(
    + _private_data: &mut Self::PrivateData,
    + _f: v4l2::format::FormatRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_s_fmt_vid_cap(
    + _private_data: &mut Self::PrivateData,
    + _f: v4l2::format::FormatRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_try_fmt_vid_cap(
    + _private_data: &mut Self::PrivateData,
    + _f: v4l2::format::FormatRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_reqbufs(
    + _private_data: &mut Self::PrivateData,
    + _b: v4l2::mmap::RequestBuffersRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_querybuf(_private_data: &mut Self::PrivateData, _b: v4l2::mmap::BufferRef) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_qbuf(_private_data: &mut Self::PrivateData, _b: v4l2::mmap::BufferRef) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_expbuf(
    + _private_data: &mut Self::PrivateData,
    + _e: v4l2::mmap::ExportBufferRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_dqbuf(_private_data: &mut Self::PrivateData, _b: v4l2::mmap::BufferRef) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_create_bufs(
    + _private_data: &mut Self::PrivateData,
    + _b: v4l2::mmap::CreateBuffersRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_prepare_buf(
    + _private_data: &mut Self::PrivateData,
    + _b: v4l2::mmap::BufferRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_streamon(_private_data: &mut Self::PrivateData, _i: v4l2::enums::BufType) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_streamoff(_private_data: &mut Self::PrivateData, _i: v4l2::enums::BufType) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_enum_input(
    + _private_data: &mut Self::PrivateData,
    + _inp: v4l2::inputs::InputRef,
    + ) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_g_input(_private_data: &mut Self::PrivateData, _i: &mut u32) -> Result {
    + Ok(())
    + }
    +
    + fn vidioc_s_input(_private_data: &mut Self::PrivateData, _i: u32) -> Result {
    + Ok(())
    + }
    +}
    +
    +pub(crate) struct OperationsVtable<T: Operations>(PhantomData<T>);
    +
    +impl<T: Operations> OperationsVtable<T> {
    + fn private_data(file: *mut file) -> crate::ScopeGuard<T::PrivateData, fn(T::PrivateData)> {
    + // SAFETY: This was set during the video device registration process.
    + let video_device = unsafe { bindings::video_devdata(file) };
    + let data_ptr = unsafe { (*video_device).dev.driver_data };
    +
    + // SAFETY: This was set during the video device registration process and
    + // now is being passed in by the kernel. We ensure exclusive access
    + // while the guard is alive.
    + unsafe { T::PrivateData::borrow_mut(data_ptr) }
    + }
    +
    + unsafe extern "C" fn vidioc_querycap_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + cap: *mut v4l2_capability,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let cap = unsafe { v4l2::capabilities::CapabilitiesRef::from_ptr(cap) } ;
    + T::vidioc_querycap(&mut private_data, cap)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_enum_fmt_vid_cap_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + f: *mut v4l2_fmtdesc,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let fmt_desc = unsafe { v4l2::format::FormatDescRef::from_ptr(f) } ;
    + T::vidioc_enum_fmt_vid_cap(&mut private_data, fmt_desc)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_enum_framesizes(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + fsize: *mut v4l2_frmsizeenum,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let framesizes = unsafe { v4l2::framesize::FrameSizeRef::from_ptr(fsize) } ;
    + T::vidioc_enum_framesizes(&mut private_data, framesizes)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_g_fmt_vid_cap_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + f: *mut v4l2_format,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let fmt = unsafe { v4l2::format::FormatRef::from_ptr(f) } ;
    + T::vidioc_g_fmt_vid_cap(&mut private_data, fmt)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_s_fmt_vid_cap_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + f: *mut v4l2_format,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let fmt = unsafe { v4l2::format::FormatRef::from_ptr(f) } ;
    + T::vidioc_s_fmt_vid_cap(&mut private_data, fmt)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_try_fmt_vid_cap_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + f: *mut v4l2_format,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let fmt = unsafe { v4l2::format::FormatRef::from_ptr(f) } ;
    + T::vidioc_try_fmt_vid_cap(&mut private_data, fmt)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_reqbufs_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + b: *mut v4l2_requestbuffers,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let rb = unsafe { v4l2::mmap::RequestBuffersRef::from_ptr(b) } ;
    + T::vidioc_reqbufs(&mut private_data, rb)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_querybuf_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + b: *mut v4l2_buffer,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let buf = unsafe { v4l2::mmap::BufferRef::from_ptr(b) } ;
    + T::vidioc_querybuf(&mut private_data, buf)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_qbuf_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + b: *mut v4l2_buffer,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let buf = unsafe { v4l2::mmap::BufferRef::from_ptr(b) } ;
    + T::vidioc_qbuf(&mut private_data, buf)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_expbuf_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + e: *mut v4l2_exportbuffer,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let expbuf = unsafe { v4l2::mmap::ExportBufferRef::from_ptr(e) } ;
    + T::vidioc_expbuf(&mut private_data, expbuf)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_dqbuf_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + b: *mut v4l2_buffer,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let buf = unsafe { v4l2::mmap::BufferRef::from_ptr(b) } ;
    + T::vidioc_dqbuf(&mut private_data, buf)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_create_bufs_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + b: *mut v4l2_create_buffers,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let createbuf = unsafe { v4l2::mmap::CreateBuffersRef::from_ptr(b) } ;
    + T::vidioc_create_bufs(&mut private_data, createbuf)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_prepare_buf_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + b: *mut v4l2_buffer,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let buf = unsafe { v4l2::mmap::BufferRef::from_ptr(b) } ;
    + T::vidioc_prepare_buf(&mut private_data, buf)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_streamon_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + i: v4l2_buf_type,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let buf_type = v4l2::enums::BufType::try_from(i)?;
    + T::vidioc_streamon(&mut private_data, buf_type)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_streamoff_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + i: v4l2_buf_type,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let buf_type = v4l2::enums::BufType::try_from(i)?;
    + T::vidioc_streamoff(&mut private_data, buf_type)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_enum_input_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + inp: *mut v4l2_input,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + let input = unsafe { v4l2::inputs::InputRef::from_ptr(inp) };
    + T::vidioc_enum_input(&mut private_data, input)?;
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_g_input_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + i: *mut core::ffi::c_uint,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    + let mut val = 0;
    +
    + T::vidioc_g_input(&mut private_data, &mut val)?;
    +
    + // SAFETY: This pointer is passed in and checked by the kernel.
    + unsafe {*i = val; }
    + Ok(0)
    + }
    + }
    +
    + unsafe extern "C" fn vidioc_s_input_callback(
    + file: *mut file,
    + _fh: *mut core::ffi::c_void,
    + i: core::ffi::c_uint,
    + ) -> core::ffi::c_int {
    + from_kernel_result! {
    + let mut private_data = Self::private_data(file);
    +
    + T::vidioc_s_input(&mut private_data, i)?;
    +
    + Ok(0)
    + }
    + }
    +
    + const VTABLE: bindings::v4l2_ioctl_ops = bindings::v4l2_ioctl_ops {
    + vidioc_querycap: if T::HAS_VIDIOC_QUERYCAP {
    + Some(Self::vidioc_querycap_callback)
    + } else {
    + None
    + },
    + vidioc_enum_fmt_vid_cap: if T::HAS_VIDIOC_ENUM_FMT_VID_CAP {
    + Some(Self::vidioc_enum_fmt_vid_cap_callback)
    + } else {
    + None
    + },
    + vidioc_enum_fmt_vid_overlay: None,
    + vidioc_enum_fmt_vid_out: None,
    + vidioc_enum_fmt_sdr_cap: None,
    + vidioc_enum_fmt_sdr_out: None,
    + vidioc_enum_fmt_meta_cap: None,
    + vidioc_enum_fmt_meta_out: None,
    + vidioc_g_fmt_vid_cap: if T::HAS_VIDIOC_G_FMT_VID_CAP {
    + Some(Self::vidioc_g_fmt_vid_cap_callback)
    + } else {
    + None
    + },
    + vidioc_g_fmt_vid_overlay: None,
    + vidioc_g_fmt_vid_out: None,
    + vidioc_g_fmt_vid_out_overlay: None,
    + vidioc_g_fmt_vbi_cap: None,
    + vidioc_g_fmt_vbi_out: None,
    + vidioc_g_fmt_sliced_vbi_cap: None,
    + vidioc_g_fmt_sliced_vbi_out: None,
    + vidioc_g_fmt_vid_cap_mplane: None,
    + vidioc_g_fmt_vid_out_mplane: None,
    + vidioc_g_fmt_sdr_cap: None,
    + vidioc_g_fmt_sdr_out: None,
    + vidioc_g_fmt_meta_cap: None,
    + vidioc_g_fmt_meta_out: None,
    + vidioc_s_fmt_vid_cap: if T::HAS_VIDIOC_S_FMT_VID_CAP {
    + Some(Self::vidioc_s_fmt_vid_cap_callback)
    + } else {
    + None
    + },
    + vidioc_s_fmt_vid_overlay: None,
    + vidioc_s_fmt_vid_out: None,
    + vidioc_s_fmt_vid_out_overlay: None,
    + vidioc_s_fmt_vbi_cap: None,
    + vidioc_s_fmt_vbi_out: None,
    + vidioc_s_fmt_sliced_vbi_cap: None,
    + vidioc_s_fmt_sliced_vbi_out: None,
    + vidioc_s_fmt_vid_cap_mplane: None,
    + vidioc_s_fmt_vid_out_mplane: None,
    + vidioc_s_fmt_sdr_cap: None,
    + vidioc_s_fmt_sdr_out: None,
    + vidioc_s_fmt_meta_cap: None,
    + vidioc_s_fmt_meta_out: None,
    + vidioc_try_fmt_vid_cap: if T::HAS_VIDIOC_TRY_FMT_VID_CAP {
    + Some(Self::vidioc_try_fmt_vid_cap_callback)
    + } else {
    + None
    + },
    + vidioc_try_fmt_vid_overlay: None,
    + vidioc_try_fmt_vid_out: None,
    + vidioc_try_fmt_vid_out_overlay: None,
    + vidioc_try_fmt_vbi_cap: None,
    + vidioc_try_fmt_vbi_out: None,
    + vidioc_try_fmt_sliced_vbi_cap: None,
    + vidioc_try_fmt_sliced_vbi_out: None,
    + vidioc_try_fmt_vid_cap_mplane: None,
    + vidioc_try_fmt_vid_out_mplane: None,
    + vidioc_try_fmt_sdr_cap: None,
    + vidioc_try_fmt_sdr_out: None,
    + vidioc_try_fmt_meta_cap: None,
    + vidioc_try_fmt_meta_out: None,
    + vidioc_reqbufs: if T::HAS_VIDIOC_REQBUFS {
    + Some(Self::vidioc_reqbufs_callback)
    + } else {
    + None
    + },
    + vidioc_querybuf: if T::HAS_VIDIOC_QUERYBUF {
    + Some(Self::vidioc_querybuf_callback)
    + } else {
    + None
    + },
    + vidioc_qbuf: if T::HAS_VIDIOC_QBUF {
    + Some(Self::vidioc_qbuf_callback)
    + } else {
    + None
    + },
    + vidioc_expbuf: if T::HAS_VIDIOC_EXPBUF {
    + Some(Self::vidioc_expbuf_callback)
    + } else {
    + None
    + },
    + vidioc_dqbuf: if T::HAS_VIDIOC_DQBUF {
    + Some(Self::vidioc_dqbuf_callback)
    + } else {
    + None
    + },
    + vidioc_create_bufs: if T::HAS_VIDIOC_CREATE_BUFS {
    + Some(Self::vidioc_create_bufs_callback)
    + } else {
    + None
    + },
    + vidioc_prepare_buf: if T::HAS_VIDIOC_PREPARE_BUF {
    + Some(Self::vidioc_prepare_buf_callback)
    + } else {
    + None
    + },
    + vidioc_overlay: None,
    + vidioc_g_fbuf: None,
    + vidioc_s_fbuf: None,
    + vidioc_streamon: if T::HAS_VIDIOC_STREAMON {
    + Some(Self::vidioc_streamon_callback)
    + } else {
    + None
    + },
    + vidioc_streamoff: if T::HAS_VIDIOC_STREAMOFF {
    + Some(Self::vidioc_streamoff_callback)
    + } else {
    + None
    + },
    + vidioc_g_std: None,
    + vidioc_s_std: None,
    + vidioc_querystd: None,
    + vidioc_enum_input: if T::HAS_VIDIOC_ENUM_INPUT {
    + Some(Self::vidioc_enum_input_callback)
    + } else {
    + None
    + },
    + vidioc_g_input: if T::HAS_VIDIOC_G_INPUT {
    + Some(Self::vidioc_g_input_callback)
    + } else {
    + None
    + },
    + vidioc_s_input: if T::HAS_VIDIOC_S_INPUT {
    + Some(Self::vidioc_s_input_callback)
    + } else {
    + None
    + },
    + vidioc_enum_output: None,
    + vidioc_g_output: None,
    + vidioc_s_output: None,
    + vidioc_queryctrl: None,
    + vidioc_query_ext_ctrl: None,
    + vidioc_g_ctrl: None,
    + vidioc_s_ctrl: None,
    + vidioc_g_ext_ctrls: None,
    + vidioc_s_ext_ctrls: None,
    + vidioc_try_ext_ctrls: None,
    + vidioc_querymenu: None,
    + vidioc_enumaudio: None,
    + vidioc_g_audio: None,
    + vidioc_s_audio: None,
    + vidioc_enumaudout: None,
    + vidioc_g_audout: None,
    + vidioc_s_audout: None,
    + vidioc_g_modulator: None,
    + vidioc_s_modulator: None,
    + vidioc_g_pixelaspect: None,
    + vidioc_g_selection: None,
    + vidioc_s_selection: None,
    + vidioc_g_jpegcomp: None,
    + vidioc_s_jpegcomp: None,
    + vidioc_g_enc_index: None,
    + vidioc_encoder_cmd: None,
    + vidioc_try_encoder_cmd: None,
    + vidioc_decoder_cmd: None,
    + vidioc_try_decoder_cmd: None,
    + vidioc_g_parm: None,
    + vidioc_s_parm: None,
    + vidioc_g_tuner: None,
    + vidioc_s_tuner: None,
    + vidioc_g_frequency: None,
    + vidioc_s_frequency: None,
    + vidioc_enum_freq_bands: None,
    + vidioc_g_sliced_vbi_cap: None,
    + vidioc_log_status: None,
    + vidioc_s_hw_freq_seek: None,
    + vidioc_enum_framesizes: if T::HAS_VIDIOC_ENUM_FRAMESIZES {
    + Some(Self::vidioc_enum_framesizes)
    + } else {
    + None
    + },
    + vidioc_enum_frameintervals: None,
    + vidioc_s_dv_timings: None,
    + vidioc_g_dv_timings: None,
    + vidioc_query_dv_timings: None,
    + vidioc_enum_dv_timings: None,
    + vidioc_dv_timings_cap: None,
    + vidioc_g_edid: None,
    + vidioc_s_edid: None,
    + vidioc_subscribe_event: None,
    + vidioc_unsubscribe_event: None,
    + vidioc_default: None,
    + };
    +
    + pub(crate) 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 77864640f19e..c60083266202 100644
    --- a/rust/kernel/media/v4l2/mod.rs
    +++ b/rust/kernel/media/v4l2/mod.rs
    @@ -3,8 +3,11 @@
    //! Abstractions for include/media/v4l2-*.h

    pub mod capabilities;
    +pub mod dev;
    +pub mod device;
    pub mod enums;
    pub mod format;
    pub mod framesize;
    pub mod inputs;
    +pub mod ioctls;
    pub mod mmap;
    --
    2.40.0
    \
     
     \ /
      Last update: 2023-04-06 23:59    [W:7.975 / U:0.372 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site