diff --git a/rust/kernel/spi.rs b/rust/kernel/spi.rs index bee29acc57a1af..38955e0062d5e4 100644 --- a/rust/kernel/spi.rs +++ b/rust/kernel/spi.rs @@ -24,59 +24,135 @@ pub struct DriverRegistration { this_module: &'static crate::ThisModule, registered: bool, name: CStr<'static>, - probe: Option, - remove: Option, - shutdown: Option, spi_driver: Option, } +pub struct ToUse { + pub probe: bool, + pub remove: bool, + pub shutdown: bool, +} + +pub const USE_NONE: ToUse = ToUse { + probe: false, + remove: false, + shutdown: false, +}; + +pub trait SpiMethods { + const TO_USE: ToUse; + + fn probe(_spi_dev: SpiDevice) -> Result { + Ok(()) + } + + fn remove(_spi_dev: SpiDevice) -> Result { + Ok(()) + } + + fn shutdown(_spi_dev: SpiDevice) {} +} + +/// Populate the TO_USE field in the `SpiMethods` implementer +/// +/// ```rust +/// impl SpiMethods for MySpiMethods { +/// /// Let's say you only want a probe and remove method, no shutdown +/// declare_spi_methods!(probe, remove); +/// +/// /// Define your probe and remove methods. If you don't, default implementations +/// /// will be used instead. These default implementations do NOT correspond to the +/// /// kernel's default implementations! If you wish to use the Kernel's default +/// /// spi functions implementations, do not declare them using the `declare_spi_methods` +/// /// macro. For example, here our Driver will use the Kernel's shutdown method. +/// fn probe(spi_dev: SpiDevice) -> Result { +/// // ... +/// +/// Ok(()) +/// } +/// +/// fn remove(spi_dev: SpiDevice) -> Result { +/// // ... +/// +/// Ok(()) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! declare_spi_methods { + () => { + const TO_USE: $crate::spi::ToUse = $crate::spi::USE_NONE; + }; + ($($method:ident),+) => { + const TO_USE: $crate::spi::ToUse = $crate::spi::ToUse { + $($method: true),+, + ..$crate::spi::USE_NONE + }; + }; +} + impl DriverRegistration { - fn new( - this_module: &'static crate::ThisModule, - name: CStr<'static>, - probe: Option, - remove: Option, - shutdown: Option, - ) -> Self { + fn new(this_module: &'static crate::ThisModule, name: CStr<'static>) -> Self { DriverRegistration { this_module, name, registered: false, - probe, - remove, - shutdown, spi_driver: None, } } // FIXME: Add documentation - pub fn new_pinned( + pub fn new_pinned( this_module: &'static crate::ThisModule, name: CStr<'static>, - probe: Option, - remove: Option, - shutdown: Option, ) -> Result>> { - let mut registration = Pin::from(Box::try_new(Self::new( - this_module, - name, - probe, - remove, - shutdown, - ))?); + let mut registration = Pin::from(Box::try_new(Self::new(this_module, name))?); - registration.as_mut().register()?; + registration.as_mut().register::()?; Ok(registration) } + unsafe extern "C" fn probe_wrapper( + spi_dev: *mut bindings::spi_device, + ) -> c_types::c_int { + // SAFETY: The spi_dev pointer is provided by the kernel and is sure to be valid + match T::probe(SpiDevice::from_ptr(spi_dev)) { + Ok(_) => 0, + Err(e) => e.to_kernel_errno(), + } + } + + unsafe extern "C" fn remove_wrapper( + spi_dev: *mut bindings::spi_device, + ) -> c_types::c_int { + // SAFETY: The spi_dev pointer is provided by the kernel and is sure to be valid + match T::remove(SpiDevice::from_ptr(spi_dev)) { + Ok(_) => 0, + Err(e) => e.to_kernel_errno(), + } + } + + unsafe extern "C" fn shutdown_wrapper(spi_dev: *mut bindings::spi_device) { + // SAFETY: The spi_dev pointer is provided by the kernel and is sure to be valid + T::shutdown(SpiDevice::from_ptr(spi_dev)) + } + // FIXME: Add documentation - pub fn register(self: Pin<&mut Self>) -> Result { + pub fn register(self: Pin<&mut Self>) -> Result { + fn maybe_get_wrapper(vtable_value: bool, func: F) -> Option { + match vtable_value { + false => None, + true => Some(func), + } + } + let mut spi_driver = bindings::spi_driver::default(); spi_driver.driver.name = self.name.as_ptr() as *const c_types::c_char; - spi_driver.probe = self.probe; - spi_driver.remove = self.remove; - spi_driver.shutdown = self.shutdown; + + spi_driver.probe = maybe_get_wrapper(T::TO_USE.probe, DriverRegistration::probe_wrapper::); + spi_driver.remove = maybe_get_wrapper(T::TO_USE.remove, DriverRegistration::remove_wrapper::); + spi_driver.shutdown = maybe_get_wrapper(T::TO_USE.shutdown, DriverRegistration::shutdown_wrapper::); let this = unsafe { self.get_unchecked_mut() }; if this.registered { @@ -115,36 +191,6 @@ unsafe impl Sync for DriverRegistration {} // SAFETY: All functions work from any thread. unsafe impl Send for DriverRegistration {} -type SpiMethod = unsafe extern "C" fn(*mut bindings::spi_device) -> c_types::c_int; -type SpiMethodVoid = unsafe extern "C" fn(*mut bindings::spi_device) -> (); - -#[macro_export] -macro_rules! spi_method { - (fn $method_name:ident (mut $device_name:ident : SpiDevice) -> Result $block:block) => { - unsafe extern "C" fn $method_name(dev: *mut kernel::bindings::spi_device) -> kernel::c_types::c_int { - use kernel::spi::SpiDevice; - - fn inner(mut $device_name: SpiDevice) -> Result $block - - // SAFETY: The dev pointer is provided by the kernel and is sure to be valid - match inner(unsafe { SpiDevice::from_ptr(dev) }) { - Ok(_) => 0, - Err(e) => e.to_kernel_errno(), - } - } - }; - (fn $method_name:ident (mut $device_name:ident : SpiDevice) $block:block) => { - unsafe extern "C" fn $method_name(dev: *mut kernel::bindings::spi_device) { - use kernel::spi::SpiDevice; - - fn inner(mut $device_name: SpiDevice) $block - - // SAFETY: The dev pointer is provided by the kernel and is sure to be valid - inner(unsafe { SpiDevice::from_ptr(dev) }) - } - }; -} - pub struct Spi; impl Spi {