blob: 076bb39fd9edc382838b05ecf85c4bbcfe156119 [file] [log] [blame]
//! Virtualize a SPI master bus to enable multiple users of the SPI bus.
use core::cell::Cell;
use kernel::common::cells::{OptionalCell, TakeCell};
use kernel::common::{List, ListLink, ListNode};
use kernel::hil;
use kernel::ReturnCode;
/// The Mux struct manages multiple Spi clients. Each client may have
/// at most one outstanding Spi request.
pub struct MuxSpiMaster<'a, Spi: hil::spi::SpiMaster> {
spi: &'a Spi,
devices: List<'a, VirtualSpiMasterDevice<'a, Spi>>,
inflight: OptionalCell<&'a VirtualSpiMasterDevice<'a, Spi>>,
}
impl<Spi: hil::spi::SpiMaster> hil::spi::SpiMasterClient for MuxSpiMaster<'_, Spi> {
fn read_write_done(
&self,
write_buffer: &'static mut [u8],
read_buffer: Option<&'static mut [u8]>,
len: usize,
) {
self.inflight.take().map(move |device| {
self.do_next_op();
device.read_write_done(write_buffer, read_buffer, len);
});
}
}
impl<'a, Spi: hil::spi::SpiMaster> MuxSpiMaster<'a, Spi> {
pub const fn new(spi: &'a Spi) -> MuxSpiMaster<'a, Spi> {
MuxSpiMaster {
spi: spi,
devices: List::new(),
inflight: OptionalCell::empty(),
}
}
fn do_next_op(&self) {
if self.inflight.is_none() {
let mnode = self
.devices
.iter()
.find(|node| node.operation.get() != Op::Idle);
mnode.map(|node| {
self.spi.specify_chip_select(node.chip_select.get());
let op = node.operation.get();
// Need to set idle here in case callback changes state
node.operation.set(Op::Idle);
match op {
Op::Configure(cpol, cpal, rate) => {
// The `chip_select` type will be correct based on
// what implemented `SpiMaster`.
self.spi.set_clock(cpol);
self.spi.set_phase(cpal);
self.spi.set_rate(rate);
}
Op::ReadWriteBytes(len) => {
// Only async operations want to block by setting
// the devices as inflight.
self.inflight.set(node);
node.txbuffer.take().map(|txbuffer| {
let rxbuffer = node.rxbuffer.take();
self.spi.read_write_bytes(txbuffer, rxbuffer, len);
});
}
Op::SetPolarity(pol) => {
self.spi.set_clock(pol);
}
Op::SetPhase(pal) => {
self.spi.set_phase(pal);
}
Op::SetRate(rate) => {
self.spi.set_rate(rate);
}
Op::Idle => {} // Can't get here...
}
});
}
}
}
#[derive(Copy, Clone, PartialEq)]
enum Op {
Idle,
Configure(hil::spi::ClockPolarity, hil::spi::ClockPhase, u32),
ReadWriteBytes(usize),
SetPolarity(hil::spi::ClockPolarity),
SetPhase(hil::spi::ClockPhase),
SetRate(u32),
}
pub struct VirtualSpiMasterDevice<'a, Spi: hil::spi::SpiMaster> {
mux: &'a MuxSpiMaster<'a, Spi>,
chip_select: Cell<Spi::ChipSelect>,
txbuffer: TakeCell<'static, [u8]>,
rxbuffer: TakeCell<'static, [u8]>,
operation: Cell<Op>,
next: ListLink<'a, VirtualSpiMasterDevice<'a, Spi>>,
client: OptionalCell<&'a dyn hil::spi::SpiMasterClient>,
}
impl<'a, Spi: hil::spi::SpiMaster> VirtualSpiMasterDevice<'a, Spi> {
pub const fn new(
mux: &'a MuxSpiMaster<'a, Spi>,
chip_select: Spi::ChipSelect,
) -> VirtualSpiMasterDevice<'a, Spi> {
VirtualSpiMasterDevice {
mux: mux,
chip_select: Cell::new(chip_select),
txbuffer: TakeCell::empty(),
rxbuffer: TakeCell::empty(),
operation: Cell::new(Op::Idle),
next: ListLink::empty(),
client: OptionalCell::empty(),
}
}
pub fn set_client(&'a self, client: &'a dyn hil::spi::SpiMasterClient) {
self.mux.devices.push_head(self);
self.client.set(client);
}
}
impl<Spi: hil::spi::SpiMaster> hil::spi::SpiMasterClient for VirtualSpiMasterDevice<'_, Spi> {
fn read_write_done(
&self,
write_buffer: &'static mut [u8],
read_buffer: Option<&'static mut [u8]>,
len: usize,
) {
self.client.map(move |client| {
client.read_write_done(write_buffer, read_buffer, len);
});
}
}
impl<'a, Spi: hil::spi::SpiMaster> ListNode<'a, VirtualSpiMasterDevice<'a, Spi>>
for VirtualSpiMasterDevice<'a, Spi>
{
fn next(&'a self) -> &'a ListLink<'a, VirtualSpiMasterDevice<'a, Spi>> {
&self.next
}
}
impl<Spi: hil::spi::SpiMaster> hil::spi::SpiMasterDevice for VirtualSpiMasterDevice<'_, Spi> {
fn configure(&self, cpol: hil::spi::ClockPolarity, cpal: hil::spi::ClockPhase, rate: u32) {
self.operation.set(Op::Configure(cpol, cpal, rate));
self.mux.do_next_op();
}
fn read_write_bytes(
&self,
write_buffer: &'static mut [u8],
read_buffer: Option<&'static mut [u8]>,
len: usize,
) -> ReturnCode {
self.txbuffer.replace(write_buffer);
self.rxbuffer.put(read_buffer);
self.operation.set(Op::ReadWriteBytes(len));
self.mux.do_next_op();
ReturnCode::SUCCESS
}
fn set_polarity(&self, cpol: hil::spi::ClockPolarity) {
self.operation.set(Op::SetPolarity(cpol));
self.mux.do_next_op();
}
fn set_phase(&self, cpal: hil::spi::ClockPhase) {
self.operation.set(Op::SetPhase(cpal));
self.mux.do_next_op();
}
fn set_rate(&self, rate: u32) {
self.operation.set(Op::SetRate(rate));
self.mux.do_next_op();
}
fn get_polarity(&self) -> hil::spi::ClockPolarity {
self.mux.spi.get_clock()
}
fn get_phase(&self) -> hil::spi::ClockPhase {
self.mux.spi.get_phase()
}
fn get_rate(&self) -> u32 {
self.mux.spi.get_rate()
}
fn hold_low(&self) {
self.mux.spi.hold_low();
}
fn release_low(&self) {
self.mux.spi.release_low();
}
}
pub struct VirtualSpiSlaveDevice<'a, Spi: hil::spi::SpiSlave> {
spi: &'a Spi,
client: OptionalCell<&'a dyn hil::spi::SpiSlaveClient>,
}
impl<'a, Spi: hil::spi::SpiSlave> VirtualSpiSlaveDevice<'a, Spi> {
pub const fn new(spi: &'a Spi) -> VirtualSpiSlaveDevice<'a, Spi> {
VirtualSpiSlaveDevice {
spi: spi,
client: OptionalCell::empty(),
}
}
pub fn set_client(&'a self, client: &'a dyn hil::spi::SpiSlaveClient) {
self.client.set(client);
}
}
impl<Spi: hil::spi::SpiSlave> hil::spi::SpiSlaveClient for VirtualSpiSlaveDevice<'_, Spi> {
fn read_write_done(
&self,
write_buffer: Option<&'static mut [u8]>,
read_buffer: Option<&'static mut [u8]>,
len: usize,
) {
self.client.map(move |client| {
client.read_write_done(write_buffer, read_buffer, len);
});
}
fn chip_selected(&self) {
self.client.map(move |client| {
client.chip_selected();
});
}
}
impl<Spi: hil::spi::SpiSlave> hil::spi::SpiSlaveDevice for VirtualSpiSlaveDevice<'_, Spi> {
fn configure(&self, cpol: hil::spi::ClockPolarity, cpal: hil::spi::ClockPhase) {
self.spi.set_clock(cpol);
self.spi.set_phase(cpal);
}
fn read_write_bytes(
&self,
write_buffer: Option<&'static mut [u8]>,
read_buffer: Option<&'static mut [u8]>,
len: usize,
) -> ReturnCode {
self.spi.read_write_bytes(write_buffer, read_buffer, len)
}
fn set_polarity(&self, cpol: hil::spi::ClockPolarity) {
self.spi.set_clock(cpol);
}
fn set_phase(&self, cpal: hil::spi::ClockPhase) {
self.spi.set_phase(cpal);
}
fn get_polarity(&self) -> hil::spi::ClockPolarity {
self.spi.get_clock()
}
fn get_phase(&self) -> hil::spi::ClockPhase {
self.spi.get_phase()
}
}