Skip to content

Commit

Permalink
Merge pull request #542 from alistair23/alistair/spi
Browse files Browse the repository at this point in the history
spi_controller: Initial support
  • Loading branch information
jrvanwhy authored Jun 4, 2024
2 parents 4375ae5 + 6c0357f commit a41c0d2
Show file tree
Hide file tree
Showing 7 changed files with 389 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ rust-version = "1.75"
rust_embedded = [
"embedded-hal",
"libtock_platform/rust_embedded",
"libtock_gpio/rust_embedded"
"libtock_gpio/rust_embedded",
]

[dependencies]
Expand All @@ -43,6 +43,7 @@ libtock_proximity = { path = "apis/proximity" }
libtock_rng = { path = "apis/rng" }
libtock_runtime = { path = "runtime" }
libtock_sound_pressure = { path = "apis/sound_pressure" }
libtock_spi_controller = { path = "apis/spi_controller" }
libtock_temperature = { path = "apis/temperature" }

embedded-hal = { version = "1.0", optional = true }
Expand Down
15 changes: 15 additions & 0 deletions apis/spi_controller/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "libtock_spi_controller"
version = "0.1.0"
authors = [
"Tock Project Developers <[email protected]>",
"Alistair Francis <[email protected]>",
]
license = "Apache-2.0 OR MIT"
edition = "2021"
repository = "https://www.github.com/tock/libtock-rs"
rust-version.workspace = true
description = "libtock SPI controller driver"

[dependencies]
libtock_platform = { path = "../../platform" }
224 changes: 224 additions & 0 deletions apis/spi_controller/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#![no_std]

use core::cell::Cell;
use libtock_platform as platform;
use libtock_platform::allow_rw::AllowRw;
use libtock_platform::share;
use libtock_platform::subscribe::Subscribe;
use libtock_platform::AllowRo;
use libtock_platform::{DefaultConfig, ErrorCode, Syscalls};

pub struct SpiController<S: Syscalls, C: Config = DefaultConfig>(S, C);

impl<S: Syscalls, C: Config> SpiController<S, C> {
pub fn exists() -> Result<(), ErrorCode> {
S::command(DRIVER_NUM, spi_controller_cmd::EXISTS, 0, 0).to_result()
}

/// # Summary
///
/// Perform an I2C write followed by a read.
///
/// TODO: Add async support
///
/// # Parameter
///
/// * `addr`: Slave device address
/// * `buf`: Buffer
/// * `w_len`: Number of bytes to write from @w_buf
/// * `r_len`: Number of bytes to read into @r_buf
///
/// # Returns
/// On success: Returns Ok(())
/// On failure: Err(ErrorCode)
pub fn spi_controller_write_read_sync(
w_buf: &[u8],
r_buf: &mut [u8],
len: u32,
) -> Result<(), ErrorCode> {
if len as usize > w_buf.len() || len as usize > r_buf.len() {
return Err(ErrorCode::NoMem);
}

let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None);
share::scope::<
(
AllowRw<_, DRIVER_NUM, { rw_allow::READ }>,
AllowRo<_, DRIVER_NUM, { ro_allow::WRITE }>,
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>,
),
_,
_,
>(|handle| {
let (allow_rw, allow_ro, subscribe) = handle.split();
S::allow_rw::<C, DRIVER_NUM, { rw_allow::READ }>(allow_rw, r_buf)?;
S::allow_ro::<C, DRIVER_NUM, { ro_allow::WRITE }>(allow_ro, w_buf)?;
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?;

S::command(DRIVER_NUM, spi_controller_cmd::READ_WRITE_BYTES, len, 0).to_result()?;

loop {
S::yield_wait();
if let Some((r0, status, _)) = called.get() {
assert_eq!(r0, len);
return match status {
0 => Ok(()),
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)),
};
}
}
})
}

pub fn spi_controller_write_sync(w_buf: &[u8], len: u32) -> Result<(), ErrorCode> {
if len as usize > w_buf.len() {
return Err(ErrorCode::NoMem);
}

let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None);
share::scope::<
(
AllowRo<_, DRIVER_NUM, { ro_allow::WRITE }>,
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>,
),
_,
_,
>(|handle| {
let (allow_ro, subscribe) = handle.split();
S::allow_ro::<C, DRIVER_NUM, { ro_allow::WRITE }>(allow_ro, w_buf)?;
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?;

S::command(DRIVER_NUM, spi_controller_cmd::READ_WRITE_BYTES, len, 0).to_result()?;

loop {
S::yield_wait();
if let Some((r0, status, _)) = called.get() {
assert_eq!(r0, len);
return match status {
0 => Ok(()),
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)),
};
}
}
})
}

pub fn spi_controller_read_sync(r_buf: &mut [u8], len: u32) -> Result<(), ErrorCode> {
if len as usize > r_buf.len() {
return Err(ErrorCode::NoMem);
}

let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None);
share::scope::<
(
AllowRw<_, DRIVER_NUM, { rw_allow::READ }>,
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>,
),
_,
_,
>(|handle| {
let (allow_rw, subscribe) = handle.split();
S::allow_rw::<C, DRIVER_NUM, { rw_allow::READ }>(allow_rw, r_buf)?;
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?;

S::command(DRIVER_NUM, spi_controller_cmd::READ_BYTES, len, 0).to_result()?;

loop {
S::yield_wait();
if let Some((r0, status, _)) = called.get() {
assert_eq!(r0, len);
return match status {
0 => Ok(()),
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)),
};
}
}
})
}

pub fn spi_controller_inplace_write_read_sync(
r_buf: &mut [u8],
len: u32,
) -> Result<(), ErrorCode> {
if len as usize > r_buf.len() {
return Err(ErrorCode::NoMem);
}

let called: Cell<Option<(u32, u32, u32)>> = Cell::new(None);
share::scope::<
(
AllowRw<_, DRIVER_NUM, { rw_allow::READ }>,
Subscribe<_, DRIVER_NUM, { subscribe::COMPLETE }>,
),
_,
_,
>(|handle| {
let (allow_rw, subscribe) = handle.split();
S::allow_rw::<C, DRIVER_NUM, { rw_allow::READ }>(allow_rw, r_buf)?;
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::COMPLETE }>(subscribe, &called)?;

S::command(
DRIVER_NUM,
spi_controller_cmd::INPLACE_READ_WRITE_BYTES,
len,
0,
)
.to_result()?;

loop {
S::yield_wait();
if let Some((r0, status, _)) = called.get() {
assert_eq!(r0, len);
return match status {
0 => Ok(()),
e_status => Err(e_status.try_into().unwrap_or(ErrorCode::Fail)),
};
}
}
})
}
}

/// System call configuration trait for `SpiController`.
pub trait Config:
platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config
{
}
impl<T: platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config>
Config for T
{
}

// -----------------------------------------------------------------------------
// Driver number and command IDs
// -----------------------------------------------------------------------------
const DRIVER_NUM: u32 = 0x20001;

#[allow(unused)]
mod subscribe {
pub const COMPLETE: u32 = 0;
}

#[allow(unused)]
mod ro_allow {
pub const WRITE: u32 = 0;
}

#[allow(unused)]
mod rw_allow {
pub const READ: u32 = 0;
}

#[allow(unused)]
mod spi_controller_cmd {
pub const EXISTS: u32 = 0;
pub const READ_WRITE_BYTES: u32 = 2;
pub const SET_BAUD: u32 = 5;
pub const GET_BAUD: u32 = 6;
pub const SET_PHASE: u32 = 7;
pub const GET_PHASE: u32 = 8;
pub const SET_POLARITY: u32 = 9;
pub const GET_POLARITY: u32 = 10;
pub const READ_BYTES: u32 = 11;
pub const INPLACE_READ_WRITE_BYTES: u32 = 12;
}
82 changes: 82 additions & 0 deletions examples/spi_controller_write_read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
//! This sample demonstrates setting up the SPI controller (assuming board has support)
#![no_main]
#![no_std]
use core::fmt::Write;
use libtock::console::Console;
use libtock::runtime::{set_main, stack_size};
use libtock::spi_controller::SpiController;

set_main! {main}
stack_size! {0x400}

const OPERATION_LEN: usize = 0x08;

fn main() {
let tx_buf: [u8; OPERATION_LEN] = [0x12; OPERATION_LEN];
let mut rx_buf: [u8; OPERATION_LEN] = [0; OPERATION_LEN];

writeln!(Console::writer(), "spi-controller: write-read\r").unwrap();
if let Err(why) =
SpiController::spi_controller_write_read_sync(&tx_buf, &mut rx_buf, OPERATION_LEN as u32)
{
writeln!(
Console::writer(),
"spi-controller: write-read operation failed {:?}\r",
why
)
.unwrap();
} else {
writeln!(
Console::writer(),
"spi-controller: write-read: wrote {:x?}: read {:x?}\r",
tx_buf,
rx_buf
)
.unwrap();
}

writeln!(Console::writer(), "spi-controller: write\r").unwrap();
if let Err(why) = SpiController::spi_controller_write_sync(&tx_buf, OPERATION_LEN as u32) {
writeln!(
Console::writer(),
"spi-controller: write operation failed {:?}\r",
why
)
.unwrap();
} else {
writeln!(Console::writer(), "spi-controller: wrote {:x?}\r", tx_buf).unwrap();
}

writeln!(Console::writer(), "spi-controller: read\r").unwrap();
if let Err(why) = SpiController::spi_controller_read_sync(&mut rx_buf, OPERATION_LEN as u32) {
writeln!(
Console::writer(),
"spi-controller: read operation failed {:?}\r",
why
)
.unwrap();
} else {
writeln!(Console::writer(), "spi-controller: read {:x?}\r", rx_buf).unwrap();
}

writeln!(Console::writer(), "spi-controller: inplace write-read\r").unwrap();
if let Err(why) =
SpiController::spi_controller_inplace_write_read_sync(&mut rx_buf, OPERATION_LEN as u32)
{
writeln!(
Console::writer(),
"spi-controller: inplace write-read operation failed {:?}\r",
why
)
.unwrap();
} else {
writeln!(
Console::writer(),
"spi-controller: inplace write-read: wrote {:x?}: read {:x?}\r",
tx_buf,
rx_buf
)
.unwrap();
}
}
8 changes: 8 additions & 0 deletions platform/src/error_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,11 @@ impl embedded_hal::digital::Error for ErrorCode {
ErrorKind::Other
}
}

#[cfg(feature = "rust_embedded")]
impl embedded_hal::spi::Error for ErrorCode {
fn kind(&self) -> embedded_hal::spi::ErrorKind {
use embedded_hal::spi::ErrorKind;
ErrorKind::Other
}
}
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ pub mod sound_pressure {
use libtock_sound_pressure as sound_pressure;
pub type SoundPressure = sound_pressure::SoundPressure<super::runtime::TockSyscalls>;
}
#[cfg(feature = "rust_embedded")]
pub mod spi_controller;
#[cfg(not(feature = "rust_embedded"))]
pub mod spi_controller {
use libtock_spi_controller as spi_controller;
pub type SpiController = spi_controller::SpiController<super::runtime::TockSyscalls>;
}
pub mod temperature {
use libtock_temperature as temperature;
pub type Temperature = temperature::Temperature<super::runtime::TockSyscalls>;
Expand Down
Loading

0 comments on commit a41c0d2

Please sign in to comment.