diff --git a/rust/kernel/drm/kms/plane.rs b/rust/kernel/drm/kms/plane.rs index b05f7385412af88da8879e86d923393075007b07..fb0265e2ddb6faceb6e91f3d2720f72b0d882703 100644 --- a/rust/kernel/drm/kms/plane.rs +++ b/rust/kernel/drm/kms/plane.rs @@ -2,36 +2,54 @@ //! KMS atomic plane abstractions for rust. +use alloc::boxed::Box; use crate::{ - container_of, bindings, + container_of, + drm::device::Device, + error::to_result, init::Zeroable, prelude::*, }; use core::{ + cell::UnsafeCell, marker::PhantomPinned, - mem + mem, + ptr::{NonNull, addr_of_mut, null_mut}, }; use macros::pin_data; +use super::KmsDriver; /// The main data structure for a drm_plane. This holds the actual drm_plane struct, along with /// whatever the driver's private data type (T) is. #[repr(C)] #[pin_data] // TODO: verify this is how we want to pin things? pub struct Plane<T: DriverPlane> { - plane: bindings::drm_plane, + plane: UnsafeCell<bindings::drm_plane>, #[pin] inner: T, #[pin] _p: PhantomPinned, } +unsafe impl Zeroable for bindings::drm_plane {} + /// The main trait for implementing the drm_plane API. This contains the various trait methods that /// need to be implemented by a driver. The private driver data for the plane is contained in /// whatever struct the driver defines which implements this trait. pub trait DriverPlane: Sized { + /// The return type of the new() function. Should be `impl PinInit<Self, Error>`. + /// TODO: Remove this when return_position_impl_trait_in_trait is stable. + type Initializer: PinInit<Self, Error>; + + /// The parent driver implementation + type Driver: KmsDriver; + /// The type for this driver's drm_plane_state implementation type State: DriverPlaneState; + + /// Create a new plane for this driver + fn new() -> Self::Initializer; } impl<T: DriverPlane> Plane<T> { @@ -45,8 +63,8 @@ impl<T: DriverPlane> Plane<T> { destroy: None, reset: Some(bindings::drm_atomic_helper_plane_reset), set_property: None, - atomic_duplicate_state: Some(atomic_duplicate_state_callback::<T>), - atomic_destroy_state: None, // TODO: required (some sort of clone?) + atomic_duplicate_state: Some(atomic_duplicate_state_callback::<T::State>), + atomic_destroy_state: Some(atomic_destroy_state_callback::<T::State>), atomic_set_property: None, // TODO someday atomic_get_property: None, // TODO someday late_register: None, // TODO someday @@ -54,16 +72,47 @@ impl<T: DriverPlane> Plane<T> { atomic_print_state: None, // TODO: Display someday??? format_mod_supported: None // TODO someday }; + + fn new( + dev: &Device<T::Driver>, + possible_crtcs: u32, + formats: &'static [u32], + format_modifiers: &'static [u64], + type_: bindings::drm_plane_type, + name: &CStr, + ) -> Result<Pin<Box<Self>>> { + // TODO: Figure out some way of making sure that format_modifiers has the correct + // terminating sentinel (DRM_FORMAT_MOD_INVALID) + + let plane: Pin<Box<Self>> = Box::pin_init(try_pin_init!(Self { + // SAFETY: This struct is expected to be zero-initialized + plane: UnsafeCell::new(bindings::drm_plane { ..Default::default() }), + inner <- T::new(), + _p: PhantomPinned + }))?; + + to_result(unsafe { + bindings::drm_universal_plane_init( + dev.drm.get(), + plane.plane.get(), + possible_crtcs, + &Self::FUNCS, + formats.as_ptr(), + formats.len() as _, + format_modifiers.as_ptr(), + type_, + name.as_char_ptr() + ) + })?; + + Ok(plane) + } } -#[pin_data] -#[derive(Clone)] +#[repr(C)] pub struct PlaneState<T: DriverPlaneState> { state: bindings::drm_plane_state, - #[pin] inner: T, - #[pin] - _p: PhantomPinned, } /// Traits which must be implemented by KMS drivers for DRM planes. @@ -72,17 +121,76 @@ pub trait DriverPlaneState: Clone + Sized { type Plane: DriverPlane; } -unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverPlane>( +impl<T: DriverPlaneState> PlaneState<T> { + /// Consume this struct without dropping it, and return a pointer to it's base `drm_plane_state` + /// which can be handed off to DRM. + fn into_raw(self: Box<Self>) -> *mut bindings::drm_plane_state { + let this = Box::into_raw(self); + + unsafe { addr_of_mut!((*this).state) } + } + + /// Consume a raw pointer and recover the original `Box<PlaneState<T>>` + /// + /// SAFETY: Callers must ensure that ptr contains a non-null pointer + unsafe fn from_raw(ptr: *mut bindings::drm_plane_state) -> Box<Self> { + unsafe { Box::from_raw(container_of!(ptr, Self, state) as *mut _) } + } + + /// Obtain a mutable reference back to the PlaneState<T> + /// + /// SAFETY: Callers must ensure that ptr contains a non-null pointer + unsafe fn as_mut<'a>(ptr: *mut bindings::drm_plane_state) -> &'a mut Self { + unsafe { &mut *(container_of!(ptr, Self, state) as *mut _) } + } + + /// Obtain a mutable pointer to the base plane state, for use in FFI calls + fn as_ptr(&mut self) -> *mut bindings::drm_plane_state { + addr_of_mut!(self.state) + } +} + +unsafe impl Zeroable for bindings::drm_plane_state { } + +unsafe extern "C" fn atomic_duplicate_state_callback<T: DriverPlaneState>( plane: *mut bindings::drm_plane ) -> *mut bindings::drm_plane_state { + // SAFETY: `plane` has to be non-null, since it holds the vtable for this function - which is + // the only possible entrypoint the caller could have used let state = unsafe { (*plane).state }; - // SAFETY: `plane` is guaranteed by FFI call signatures to be PlaneState<S> - // TODO: Do we need container_of!? I think we might actually be able to just cast this if the - // layout is identical - let state = container_of!(state, PlaneState<T::State>, state); + if state.is_null() { + return null_mut(); + } + + // SAFETY: We just verified that `state` is non-null, and we're guaranteed by our bindings that + // `state` is of type `PlaneState<T>`. + let state = unsafe { PlaneState::<T>::as_mut(state) }; + + let mut new: Result<Box<PlaneState<T>>> = Box::try_init(try_init!(PlaneState::<T> { + state: bindings::drm_plane_state { ..Default::default() }, + inner: state.inner.clone() + })); + + if let Ok(mut new) = new { + // SAFETY: Just a lil' FFI call, nothing special here + unsafe { bindings::__drm_atomic_helper_plane_duplicate_state(plane, new.as_ptr()) }; + + new.into_raw() + } else { + null_mut() + } +} - let new = unsafe { (&*state).clone() }; +unsafe extern "C" fn atomic_destroy_state_callback<T: DriverPlaneState>( + _plane: *mut bindings::drm_plane, + plane_state: *mut bindings::drm_plane_state +) { + // SAFETY: This callback wouldn't be called unless there a plane state to destroy + unsafe { bindings::__drm_atomic_helper_plane_destroy_state(plane_state) }; - todo!() + // SAFETY: We're guaranteed by type invariants that plane_state is of type PlaneState<T>, and + // since this is the destructor callback for DRM - we're guaranteed to hold the only remaining + // reference to this state + unsafe { PlaneState::<T>::from_raw(plane_state) }; }