Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 105 additions & 59 deletions rust/kernel/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,59 +24,135 @@ pub struct DriverRegistration {
this_module: &'static crate::ThisModule,
registered: bool,
name: CStr<'static>,
probe: Option<SpiMethod>,
remove: Option<SpiMethod>,
shutdown: Option<SpiMethodVoid>,
spi_driver: Option<bindings::spi_driver>,
}

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
};
};
}
Comment on lines +82 to +92

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you provide an example of how to use this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And how it works 😅


impl DriverRegistration {
fn new(
this_module: &'static crate::ThisModule,
name: CStr<'static>,
probe: Option<SpiMethod>,
remove: Option<SpiMethod>,
shutdown: Option<SpiMethodVoid>,
) -> 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<T: SpiMethods>(
this_module: &'static crate::ThisModule,
name: CStr<'static>,
probe: Option<SpiMethod>,
remove: Option<SpiMethod>,
shutdown: Option<SpiMethodVoid>,
) -> Result<Pin<Box<Self>>> {
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::<T>()?;

Ok(registration)
}

unsafe extern "C" fn probe_wrapper<T: SpiMethods>(
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<T: SpiMethods>(
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<T: SpiMethods>(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<T: SpiMethods>(self: Pin<&mut Self>) -> Result {
fn maybe_get_wrapper<F>(vtable_value: bool, func: F) -> Option<F> {
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::<T>);
spi_driver.remove = maybe_get_wrapper(T::TO_USE.remove, DriverRegistration::remove_wrapper::<T>);
spi_driver.shutdown = maybe_get_wrapper(T::TO_USE.shutdown, DriverRegistration::shutdown_wrapper::<T>);

let this = unsafe { self.get_unchecked_mut() };
if this.registered {
Expand Down Expand Up @@ -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 {
Expand Down