Skip to content

Commit 6d05942

Browse files
committed
Add base spi api
Co-developed-by: Martin Schmidt <[email protected]> Signed-off-by: Martin Schmidt <[email protected]> Co-developed-by: Arthur Cohen <[email protected]> Signed-off-by: Arthur Cohen <[email protected]> Add base spi api spi: Add spi low level support spi: Add drop implementation for spi::Registration spi: Add base for spi driver "methods" spi: Add spi_method! to define SPI methods easily spi: Refactor module spi: Fix overzealous typo fix spi: Rename Registration -> DriverRegistration for clarity spi: Add base abstraction for spi_*() functions spi: spi_method!, pass SpiDevice as mutable spi: Make DriverRegistration Sync + Send spi: Do not register a Pin<Box> spi: Remove builder pattern spi: Register driver in new_pinned Rename KernelResult to Result Signed-off-by: Esteban Blanc <[email protected]>
1 parent fc2b177 commit 6d05942

File tree

3 files changed

+184
-0
lines changed

3 files changed

+184
-0
lines changed

rust/kernel/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/miscdevice.h>
1313
#include <linux/poll.h>
1414
#include <linux/mm.h>
15+
#include <linux/spi/spi.h>
1516
#include <uapi/linux/android/binder.h>
1617

1718
// `bindgen` gets confused at certain things

rust/kernel/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ pub mod user_ptr;
6868
pub use crate::error::{Error, Result};
6969
pub use crate::types::{CStr, Mode};
7070

71+
#[cfg(CONFIG_SPI)]
72+
pub mod spi;
73+
7174
/// Page size defined in terms of the `PAGE_SHIFT` macro from C.
7275
///
7376
/// [`PAGE_SHIFT`]: ../../../include/asm-generic/page.h

rust/kernel/spi.rs

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
use crate::bindings;
4+
use crate::c_types;
5+
use crate::error::{Error, Result};
6+
use crate::CStr;
7+
use alloc::boxed::Box;
8+
use core::pin::Pin;
9+
10+
#[derive(Clone, Copy)]
11+
pub struct SpiDevice(*mut bindings::spi_device);
12+
13+
impl SpiDevice {
14+
pub fn from_ptr(dev: *mut bindings::spi_device) -> Self {
15+
SpiDevice(dev)
16+
}
17+
18+
pub fn to_ptr(&mut self) -> *mut bindings::spi_device {
19+
self.0
20+
}
21+
}
22+
23+
pub struct DriverRegistration {
24+
this_module: &'static crate::ThisModule,
25+
registered: bool,
26+
name: CStr<'static>,
27+
probe: Option<SpiMethod>,
28+
remove: Option<SpiMethod>,
29+
shutdown: Option<SpiMethodVoid>,
30+
spi_driver: Option<bindings::spi_driver>,
31+
}
32+
33+
impl DriverRegistration {
34+
fn new(
35+
this_module: &'static crate::ThisModule,
36+
name: CStr<'static>,
37+
probe: Option<SpiMethod>,
38+
remove: Option<SpiMethod>,
39+
shutdown: Option<SpiMethodVoid>,
40+
) -> Self {
41+
DriverRegistration {
42+
this_module,
43+
name,
44+
registered: false,
45+
probe,
46+
remove,
47+
shutdown,
48+
spi_driver: None,
49+
}
50+
}
51+
52+
// FIXME: Add documentation
53+
pub fn new_pinned(
54+
this_module: &'static crate::ThisModule,
55+
name: CStr<'static>,
56+
probe: Option<SpiMethod>,
57+
remove: Option<SpiMethod>,
58+
shutdown: Option<SpiMethodVoid>,
59+
) -> Result<Pin<Box<Self>>> {
60+
let mut registration = Pin::from(Box::try_new(Self::new(
61+
this_module,
62+
name,
63+
probe,
64+
remove,
65+
shutdown,
66+
))?);
67+
68+
registration.as_mut().register()?;
69+
70+
Ok(registration)
71+
}
72+
73+
// FIXME: Add documentation
74+
pub fn register(self: Pin<&mut Self>) -> Result {
75+
let mut spi_driver = bindings::spi_driver::default();
76+
spi_driver.driver.name = self.name.as_ptr() as *const c_types::c_char;
77+
spi_driver.probe = self.probe;
78+
spi_driver.remove = self.remove;
79+
spi_driver.shutdown = self.shutdown;
80+
81+
let this = unsafe { self.get_unchecked_mut() };
82+
if this.registered {
83+
return Err(Error::EINVAL);
84+
}
85+
86+
this.spi_driver = Some(spi_driver);
87+
88+
let res = unsafe { bindings::__spi_register_driver(this.this_module.0, &mut spi_driver) };
89+
90+
match res {
91+
0 => {
92+
this.registered = true;
93+
Ok(())
94+
}
95+
_ => Err(Error::from_kernel_errno(res)),
96+
}
97+
}
98+
}
99+
100+
impl Drop for DriverRegistration {
101+
fn drop(&mut self) {
102+
unsafe { bindings::driver_unregister(&mut self.spi_driver.unwrap().driver) }
103+
// FIXME: No unwrap? But it's safe?
104+
}
105+
}
106+
107+
// FIXME: Fix SAFETY documentation
108+
109+
// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it
110+
// is safe to pass `&Registration` to multiple threads because it offers no interior mutability.
111+
unsafe impl Sync for DriverRegistration {}
112+
113+
// SAFETY: The only method is `register()`, which requires a (pinned) mutable `Registration`, so it
114+
// is safe to pass `&Registration` to multiple threads because it offers no interior mutability.
115+
unsafe impl Send for DriverRegistration {}
116+
117+
type SpiMethod = unsafe extern "C" fn(*mut bindings::spi_device) -> c_types::c_int;
118+
type SpiMethodVoid = unsafe extern "C" fn(*mut bindings::spi_device) -> ();
119+
120+
#[macro_export]
121+
macro_rules! spi_method {
122+
(fn $method_name:ident (mut $device_name:ident : SpiDevice) -> Result $block:block) => {
123+
unsafe extern "C" fn $method_name(dev: *mut kernel::bindings::spi_device) -> kernel::c_types::c_int {
124+
use kernel::spi::SpiDevice;
125+
126+
fn inner(mut $device_name: SpiDevice) -> Result $block
127+
128+
match inner(SpiDevice::from_ptr(dev)) {
129+
Ok(_) => 0,
130+
Err(e) => e.to_kernel_errno(),
131+
}
132+
}
133+
};
134+
(fn $method_name:ident (mut $device_name:ident : SpiDevice) $block:block) => {
135+
unsafe extern "C" fn $method_name(dev: *mut kernel::bindings::spi_device) {
136+
use kernel::spi::SpiDevice;
137+
138+
fn inner(mut $device_name: SpiDevice) $block
139+
140+
inner(SpiDevice::from_ptr(dev))
141+
}
142+
};
143+
}
144+
145+
pub struct Spi;
146+
147+
impl Spi {
148+
pub fn write_then_read(
149+
dev: &mut SpiDevice,
150+
tx_buf: &[u8],
151+
n_tx: usize,
152+
rx_buf: &mut [u8],
153+
n_rx: usize,
154+
) -> Result {
155+
let res = unsafe {
156+
bindings::spi_write_then_read(
157+
dev.to_ptr(),
158+
tx_buf.as_ptr() as *const c_types::c_void,
159+
n_tx as c_types::c_uint,
160+
rx_buf.as_ptr() as *mut c_types::c_void,
161+
n_rx as c_types::c_uint,
162+
)
163+
};
164+
165+
match res {
166+
0 => Ok(()), // 0 indicates a valid transfer,
167+
err => Err(Error::from_kernel_errno(err)), // A negative number indicates an error
168+
}
169+
}
170+
171+
#[inline]
172+
pub fn write(dev: &mut SpiDevice, tx_buf: &[u8], n_tx: usize) -> Result {
173+
Spi::write_then_read(dev, tx_buf, n_tx, &mut [0u8; 0], 0)
174+
}
175+
176+
#[inline]
177+
pub fn read(dev: &mut SpiDevice, rx_buf: &mut [u8], n_rx: usize) -> Result {
178+
Spi::write_then_read(dev, &[0u8; 0], 0, rx_buf, n_rx)
179+
}
180+
}

0 commit comments

Comments
 (0)