From d0e02b167e0b0ac740e79059bb50f87dc6283f2d Mon Sep 17 00:00:00 2001 From: Esteban Blanc Date: Thu, 29 Apr 2021 12:33:09 +0200 Subject: [PATCH 1/5] rust: add SPI API This add support for the SPI API for Rust modules. Features: - Register/Unregister - Probe, Remove and Shutdown - write_then_read, write, read Co-developed-by: Martin Schmidt Signed-off-by: Martin Schmidt Co-developed-by: Arthur Cohen Signed-off-by: Arthur Cohen Signed-off-by: Esteban Blanc --- rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 3 + rust/kernel/spi.rs | 239 ++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 rust/kernel/spi.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 65b98831b97560..e6f221f62821d9 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -17,6 +17,7 @@ #include #include #include +#include /* `bindgen` gets confused at certain things. */ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index be68d5e567b1a1..bdfc53ae7da7ed 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -59,6 +59,9 @@ pub use uapi; #[doc(hidden)] pub use build_error::build_error; +#[cfg(CONFIG_SPI)] +pub mod spi; + /// Prefix to appear before log messages printed from within the `kernel` crate. const __LOG_PREFIX: &[u8] = b"rust_kernel\0"; diff --git a/rust/kernel/spi.rs b/rust/kernel/spi.rs new file mode 100644 index 00000000000000..e42b19a2f2cd8d --- /dev/null +++ b/rust/kernel/spi.rs @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! This module provides safer and higher level abstraction over the kernel's SPI types +//! and functions. +//! +//! C header: [`include/linux/spi/spi.h`](../../../../include/linux/spi/spi.h) + +use crate::bindings; +use crate::error::{code::*, from_result, Error, Result}; +use crate::str::CStr; +use alloc::boxed::Box; +use core::marker::PhantomData; +use core::pin::Pin; +use macros::vtable; + +/// Wrapper struct around the kernel's `spi_device`. +#[derive(Clone, Copy)] +pub struct SpiDevice(*mut bindings::spi_device); + +impl SpiDevice { + /// Create an [`SpiDevice`] from a mutable spi_device raw pointer. This function is unsafe + /// as the pointer might be invalid. + /// + /// The pointer must be valid. This can be achieved by calling `to_ptr` on a previously + /// constructed, safe `SpiDevice` instance, or by making sure that the pointer points + /// to valid memory. + /// + /// You probably do not want to use this abstraction directly. It is mainly used + /// by this abstraction to wrap valid pointers given by the Kernel to the different + /// SPI methods: `probe`, `remove` and `shutdown`. + pub unsafe fn from_ptr(dev: *mut bindings::spi_device) -> Self { + SpiDevice(dev) + } + + /// Access the raw pointer from an [`SpiDevice`] instance. + pub fn to_ptr(&mut self) -> *mut bindings::spi_device { + self.0 + } +} + +/// Corresponds to the kernel's spi_driver's methods. Implement this trait on a type to +/// express the need of a custom probe, remove or shutdown function for your SPI driver. +#[vtable] +pub trait SpiMethods { + /// Corresponds to the kernel's `spi_driver`'s `probe` method field. + fn probe(_spi_dev: &mut SpiDevice) -> Result { + unreachable!("There should be a NULL in the probe filed of spi_driver's"); + } + + /// Corresponds to the kernel's `spi_driver`'s `remove` method field. + fn remove(_spi_dev: &mut SpiDevice) { + unreachable!("There should be a NULL in the remove filed of spi_driver's"); + } + + /// Corresponds to the kernel's `spi_driver`'s `shutdown` method field. + fn shutdown(_spi_dev: &mut SpiDevice) { + unreachable!("There should be a NULL in the shutdown filed of spi_driver's"); + } +} + +/// Registration of an SPI driver. +pub struct DriverRegistration { + this_module: &'static crate::ThisModule, + registered: bool, + name: &'static CStr, + spi_driver: bindings::spi_driver, + _p: PhantomData, +} + +impl DriverRegistration { + fn new(this_module: &'static crate::ThisModule, name: &'static CStr) -> Self { + DriverRegistration { + this_module, + name, + registered: false, + spi_driver: bindings::spi_driver::default(), + _p: PhantomData, + } + } + + /// Create a new `DriverRegistration` and register it. This is equivalent to creating + /// a static `spi_driver` and then calling `spi_driver_register` on it in C. + /// + /// # Examples + /// + /// ``` + /// let spi_driver = + /// spi::DriverRegistration::new_pinned::(&THIS_MODULE, cstr!("my_driver_name"))?; + /// ``` + pub fn new_pinned( + this_module: &'static crate::ThisModule, + name: &'static CStr, + ) -> Result>> { + let mut registration = Pin::from(Box::try_new(Self::new(this_module, name))?); + + registration.as_mut().register()?; + + Ok(registration) + } + + /// Register a [`DriverRegistration`]. This is equivalent to calling `spi_driver_register` + /// on your `spi_driver` in C, without creating it first. + fn register(self: Pin<&mut Self>) -> Result { + // SAFETY: We do not move out of the reference we get, and are only registering + // `this` once over the course of the module, since we check that the `registered` + // field was not already set to true. + let this = unsafe { self.get_unchecked_mut() }; + if this.registered { + return Err(EINVAL); + } + + let mut spi_driver = this.spi_driver; + + if T::HAS_PROBE { + spi_driver.probe = Some(probe_callback::); + } + if T::HAS_REMOVE { + spi_driver.remove = Some(remove_callback::); + } + if T::HAS_SHUTDOWN { + spi_driver.remove = Some(shutdown_callback::); + } + + spi_driver.driver.name = this.name.as_ptr() as *const core::ffi::c_char; + + // SAFETY: Since we are using a pinned `self`, we can register the driver safely as + // if we were using a static instance. The kernel will access this driver over the + // entire lifespan of a module and therefore needs a pointer valid for the entirety + // of this lifetime. + let res = + unsafe { bindings::__spi_register_driver(this.this_module.0, &mut this.spi_driver) }; + + if res != 0 { + return Err(Error::from_errno(res)); + } + + this.registered = true; + + Ok(()) + } +} + +impl Drop for DriverRegistration { + fn drop(&mut self) { + // SAFETY: We are simply unregistering an `spi_driver` that we know to be valid. + // [`DriverRegistration`] instances can only be created by being registered at the + // same time, so we are sure that we'll never unregister an unregistered `spi_driver`. + unsafe { bindings::driver_unregister(&mut self.spi_driver.driver) } + } +} + +unsafe extern "C" fn probe_callback( + spi: *mut bindings::spi_device, +) -> core::ffi::c_int { + from_result(|| Ok(T::probe(&mut SpiDevice(spi))?)) +} + +unsafe extern "C" fn remove_callback(spi: *mut bindings::spi_device) { + T::remove(&mut SpiDevice(spi)); +} + +unsafe extern "C" fn shutdown_callback(spi: *mut bindings::spi_device) { + T::shutdown(&mut SpiDevice(spi)); +} + +// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it +// is safe to pass `&Registration` to multiple threads because it offers no interior mutability. +unsafe impl Sync for DriverRegistration {} + +// SAFETY: All functions work from any thread. +unsafe impl Send for DriverRegistration {} + +/// High level abstraction over the kernel's SPI functions such as `spi_write_then_read`. +// TODO this should be a mod, right? +pub struct Spi; + +impl Spi { + /// Corresponds to the kernel's `spi_write_then_read`. + /// + /// # Examples + /// + /// ``` + /// let to_write = "rust-for-linux".as_bytes(); + /// let mut to_receive = [0u8; 10]; // let's receive 10 bytes back + /// + /// // `spi_device` was previously provided by the kernel in that case + /// let transfer_result = Spi::write_then_read(spi_device, &to_write, &mut to_receive); + /// ``` + pub fn write_then_read(dev: &mut SpiDevice, tx_buf: &[u8], rx_buf: &mut [u8]) -> Result { + // SAFETY: The `dev` argument must uphold the safety guarantees made when creating + // the [`SpiDevice`] instance. It should therefore point to a valid `spi_device` + // and valid memory. We also know that a rust slice will always contain a proper + // size and that it is safe to use as is. Converting from a Rust pointer to a + // generic C `void*` pointer is normal, and does not pose size issues on the + // kernel's side, which will use the given Transfer Receive sizes as bytes. + let res = unsafe { + bindings::spi_write_then_read( + dev.to_ptr(), + tx_buf.as_ptr() as *const core::ffi::c_void, + tx_buf.len() as core::ffi::c_uint, + rx_buf.as_mut_ptr() as *mut core::ffi::c_void, + rx_buf.len() as core::ffi::c_uint, + ) + }; + + match res { + 0 => Ok(()), // 0 indicates a valid transfer, + err => Err(Error::from_errno(err)), // A negative number indicates an error + } + } + + /// Corresponds to the kernel's `spi_write`. + /// + /// # Examples + /// + /// ``` + /// let to_write = "rust-for-linux".as_bytes(); + /// + /// // `spi_device` was previously provided by the kernel in that case + /// let write_result = Spi::write(spi_device, &to_write); + /// ``` + pub fn write(dev: &mut SpiDevice, tx_buf: &[u8]) -> Result { + Spi::write_then_read(dev, tx_buf, &mut [0u8; 0]) + } + + /// Corresponds to the kernel's `spi_read`. + /// + /// # Examples + /// + /// ``` + /// let mut to_receive = [0u8; 10]; // let's receive 10 bytes + /// + /// // `spi_device` was previously provided by the kernel in that case + /// let transfer_result = Spi::read(spi_device, &mut to_receive); + /// ``` + pub fn read(dev: &mut SpiDevice, rx_buf: &mut [u8]) -> Result { + Spi::write_then_read(dev, &[0u8; 0], rx_buf) + } +} From eb5852fc511e3a42d919b0b9a6b68f4a8d49bd97 Mon Sep 17 00:00:00 2001 From: Esteban Blanc Date: Mon, 26 Jun 2023 14:25:07 +0200 Subject: [PATCH 2/5] rust: spi: Add id_table support for registration Signed-off-by: Esteban Blanc --- rust/kernel/spi.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/rust/kernel/spi.rs b/rust/kernel/spi.rs index e42b19a2f2cd8d..121082852dc54e 100644 --- a/rust/kernel/spi.rs +++ b/rust/kernel/spi.rs @@ -6,7 +6,9 @@ //! C header: [`include/linux/spi/spi.h`](../../../../include/linux/spi/spi.h) use crate::bindings; +use crate::c_str; use crate::error::{code::*, from_result, Error, Result}; +use crate::static_assert; use crate::str::CStr; use alloc::boxed::Box; use core::marker::PhantomData; @@ -63,16 +65,22 @@ pub struct DriverRegistration { this_module: &'static crate::ThisModule, registered: bool, name: &'static CStr, + id_table: Option<&'static [SpiDeviceId]>, spi_driver: bindings::spi_driver, _p: PhantomData, } impl DriverRegistration { - fn new(this_module: &'static crate::ThisModule, name: &'static CStr) -> Self { + fn new( + this_module: &'static crate::ThisModule, + name: &'static CStr, + id_table: Option<&'static [SpiDeviceId]>, + ) -> Self { DriverRegistration { this_module, name, registered: false, + id_table, spi_driver: bindings::spi_driver::default(), _p: PhantomData, } @@ -90,8 +98,9 @@ impl DriverRegistration { pub fn new_pinned( this_module: &'static crate::ThisModule, name: &'static CStr, + id_table: Option<&'static [SpiDeviceId]>, ) -> Result>> { - let mut registration = Pin::from(Box::try_new(Self::new(this_module, name))?); + let mut registration = Pin::from(Box::try_new(Self::new(this_module, name, id_table))?); registration.as_mut().register()?; @@ -111,6 +120,10 @@ impl DriverRegistration { let mut spi_driver = this.spi_driver; + if let Some(id_table) = this.id_table { + spi_driver.id_table = id_table.as_ptr() as *const bindings::spi_device_id; + } + if T::HAS_PROBE { spi_driver.probe = Some(probe_callback::); } @@ -170,6 +183,78 @@ unsafe impl Sync for DriverRegistration {} // SAFETY: All functions work from any thread. unsafe impl Send for DriverRegistration {} +/// We need a union because we can't get an address from a pointer at compile time. +#[repr(C)] +union DriverData { + ptr: &'static (), + val: usize, +} + +/// Wrapper struct around the kernel's `struct spi_device_id`. +#[repr(C)] +pub struct SpiDeviceId { + name: [i8; 32], + driver_data: DriverData, +} + +static_assert!( + core::mem::size_of::() == core::mem::size_of::() +); + +impl SpiDeviceId { + /// Creates a new [SpiDeviceId] with a given name. + ///

+ /// Warning: The name will be truncated to a maximum of 32 chars + ///

+ /// + /// To specify `driver_data` content, see [SpiDeviceId::with_driver_data_pointer] and + /// [SpiDeviceId::with_driver_data_number]. + pub const fn new(name: &CStr) -> Self { + let name = name.as_bytes_with_nul(); + let name_len = name.len(); + + let mut array = [0; 32]; + let min_len = if name_len > 32 { 32 } else { name_len }; + + let mut i = 0; + while i < min_len { + array[i] = name[i] as i8; + i += 1; + } + + SpiDeviceId { + name: array, + driver_data: DriverData { val: 0 }, + } + } + + /// Add a pointer to the `driver_data` field. + pub const fn with_driver_data_pointer(mut self, driver_data: &'static T) -> Self { + // SAFETY: On the C side this will only be used as an integer, we don't care about the + // type. This function is called with a reference so the deref is safe as the pointer is + // valid + self.driver_data = DriverData { + ptr: unsafe { &*(driver_data as *const T as *const ()) }, + }; + + // unsafe { core::mem::transmute::<&T, bindings::kernel_ulong_t>(driver_data) }; + self + } + + /// Add a number to the `driver_data` field. + pub const fn with_driver_data_number(mut self, driver_data: usize) -> Self { + self.driver_data = DriverData { val: driver_data }; + + self + } + + /// Used for creating a sentinel to place at the end of an array of [SpiDeviceId]. + pub const fn sentinel() -> Self { + // TODO: call Default trait instead when #![feature(const_trait_impl)] is stabilized + Self::new(c_str!("")) + } +} + /// High level abstraction over the kernel's SPI functions such as `spi_write_then_read`. // TODO this should be a mod, right? pub struct Spi; From 7dd3aec39d89023446f1b19fd43dbd64433d33e1 Mon Sep 17 00:00:00 2001 From: Esteban Blanc Date: Mon, 19 Jun 2023 18:22:41 +0200 Subject: [PATCH 3/5] samples: rust: Add spi_dummy Signed-off-by: Esteban Blanc --- samples/rust/Kconfig | 10 +++++++ samples/rust/Makefile | 1 + samples/rust/spi_dummy.rs | 62 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 samples/rust/spi_dummy.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index b0f74a81c8f9ad..4abe7e87c8aa34 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -37,4 +37,14 @@ config SAMPLE_RUST_HOSTPROGS If unsure, say N. +config SAMPLE_RUST_SPI_DUMMY + tristate "SPI dummy" + help + This option builds the Rust SPI dummy sample. + + To compile this as a module, choose M here: + the module will be called rust_spi_dummy. + + If unsure, say N. + endif # SAMPLES_RUST diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 03086dabbea44f..dd6965ac942ecd 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o +obj-$(CONFIG_SAMPLE_RUST_SPI_DUMMY) += spi_dummy.o subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs diff --git a/samples/rust/spi_dummy.rs b/samples/rust/spi_dummy.rs new file mode 100644 index 00000000000000..0babbf11843886 --- /dev/null +++ b/samples/rust/spi_dummy.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 + +use alloc::boxed::Box; +use core::pin::Pin; +use kernel::c_str; +use kernel::prelude::*; +use kernel::spi::*; + +module! { + type: SPIDummy, + name: "rust_spi_dummy", + author: "ks0n", + description: "SPI Dummy Driver", + license: "GPL", +} + +struct SPIDummyMethods; + +#[vtable] +impl SpiMethods for SPIDummyMethods { + fn probe(spi_device: &mut SpiDevice) -> Result { + pr_info!("[SPI-RS] SPI Registered\n"); + pr_info!( + "[SPI-RS] SPI Registered, spi_device = {:#?}\n", + spi_device.to_ptr() + ); + + Ok(0) + } +} + +struct SPIDummy { + _spi: Pin>>, +} + +const TEST: u8 = 0; +const fn test() -> usize { + 42 +} + +static ID_TABLE: &[SpiDeviceId] = &[ + SpiDeviceId::new(c_str!("test1")).with_driver_data_pointer(&TEST), + SpiDeviceId::new(c_str!("test2")).with_driver_data_pointer(&test), + SpiDeviceId::new(c_str!("test3")).with_driver_data_number(42), + SpiDeviceId::sentinel(), +]; + +impl kernel::Module for SPIDummy { + fn init(module: &'static ThisModule) -> Result { + pr_info!("[SPI-RS] Init\n"); + + let spi = DriverRegistration::new_pinned(module, c_str!("SPIDummy"), Some(&ID_TABLE))?; + + Ok(SPIDummy { _spi: spi }) + } +} + +impl Drop for SPIDummy { + fn drop(&mut self) { + pr_info!("[SPI-RS] Exit\n"); + } +} From c7fdc1072d38e88d91ea5367fa2902e3ca5bd146 Mon Sep 17 00:00:00 2001 From: Esteban Blanc Date: Tue, 27 Jun 2023 09:36:02 +0200 Subject: [PATCH 4/5] FIXME: rust: spi: Add SpiTransfer and SpiMessage Signed-off-by: Esteban Blanc --- rust/kernel/spi.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/rust/kernel/spi.rs b/rust/kernel/spi.rs index 121082852dc54e..bf3f98c3130e58 100644 --- a/rust/kernel/spi.rs +++ b/rust/kernel/spi.rs @@ -255,6 +255,49 @@ impl SpiDeviceId { } } +/// Wrapper struct around the kernel's `struct spi_transfer`. +pub struct SpiTransfer(bindings::spi_transfer); + +impl SpiTransfer { + pub fn new() -> Self { + SpiTransfer(bindings::spi_transfer::default()) + } + + pub fn with_tx_buf(mut self, buf: &[u8]) -> Self { + // Make sure no tx_buf nor rx_buf has been provided before + // TODO: replace with a `Result` + assert!(self.0.len == 0); + + self.0.tx_buf = buf.as_ptr() as *const core::ffi::c_void; + self.0.len = buf.len() as core::ffi::c_uint; + + self + } + + pub fn with_rx_buf(mut self, buf: &mut[u8]) -> Self { + // Make sure no tx_buf nor rx_buf has been provided before + // TODO: replace with a `Result` + assert!(self.0.len == 0); + + self.0.rx_buf = buf.as_mut_ptr() as *mut core::ffi::c_void; + self.0.len = buf.len() as core::ffi::c_uint; + + self + } +} + +#[derive(Default)] +pub struct SpiMessage(bindings::spi_message); + +impl SpiMessage { + pub fn new() -> Self { + Self::default() + } + /// Equivalent of `spi_message_init` + pub fn spi_message_init(&mut self) { + } +} + /// High level abstraction over the kernel's SPI functions such as `spi_write_then_read`. // TODO this should be a mod, right? pub struct Spi; From 1ae284e2a483e3f6a5e6f2ab4a2e3cbdeb6bd0f7 Mon Sep 17 00:00:00 2001 From: Esteban Blanc Date: Tue, 19 Mar 2024 17:21:36 +0100 Subject: [PATCH 5/5] FIXM: rust: spi: Add spi_message_init Signed-off-by: Esteban Blanc --- rust/helpers.c | 8 ++++++++ rust/kernel/spi.rs | 1 + 2 files changed, 9 insertions(+) diff --git a/rust/helpers.c b/rust/helpers.c index 70e59efd92bc43..fe68b224071abd 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -31,6 +31,7 @@ #include #include #include +#include __noreturn void rust_helper_BUG(void) { @@ -157,6 +158,13 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func, } EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key); +void rust_helper_spi_message_init(struct spi_message *m) +{ + memset(m, 0, sizeof *m); + spi_message_init_no_memset(m); +} +EXPORT_SYMBOL_GPL(rust_helper_spi_message_init); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/spi.rs b/rust/kernel/spi.rs index bf3f98c3130e58..83215d575d5f3e 100644 --- a/rust/kernel/spi.rs +++ b/rust/kernel/spi.rs @@ -295,6 +295,7 @@ impl SpiMessage { } /// Equivalent of `spi_message_init` pub fn spi_message_init(&mut self) { + unsafe { bindings::spi_message_init(&mut self.0) } } }