use super::*;
use std::collections::VecDeque;
use std::ptr::NonNull;
#[derive(Debug)]
pub struct Spi {
state: NonNull<SpiState>,
}
impl Spi {
pub unsafe fn new(id: u8, avr: &Avr) -> Option<Self> {
let ioctl = IoCtl::SpiGetIrq { id };
let irq_input = avr.try_io_getirq(ioctl, ffi::SPI_IRQ_INPUT)?;
let irq_output = avr.try_io_getirq(ioctl, ffi::SPI_IRQ_OUTPUT)?;
let this = Self {
state: NonNull::from(Box::leak(Box::new(SpiState::new(irq_input)))),
};
unsafe {
Avr::irq_register_notify(
irq_output,
Some(Self::on_output),
this.state.as_ptr(),
);
}
Some(this)
}
pub fn read(&mut self) -> Option<u8> {
self.state_mut().rx.pop_front()
}
pub fn write(&mut self, byte: u8) {
self.state_mut().tx.push_back(byte);
}
fn state_mut(&mut self) -> &mut SpiState {
unsafe { self.state.as_mut() }
}
unsafe extern "C" fn on_output(
_: NonNull<ffi::avr_irq_t>,
value: u32,
mut state: NonNull<SpiState>,
) {
let state = unsafe { state.as_mut() };
state.rx.push_back(value as u8);
let byte = state.tx.pop_front().unwrap_or_default();
unsafe {
ffi::avr_raise_irq(state.irq_input.as_ptr(), byte as _);
}
}
}
impl Drop for Spi {
fn drop(&mut self) {
unsafe {
drop(Box::from_raw(self.state.as_ptr()));
}
}
}
#[derive(Debug)]
struct SpiState {
irq_input: NonNull<ffi::avr_irq_t>,
tx: VecDeque<u8>,
rx: VecDeque<u8>,
}
impl SpiState {
fn new(irq_input: NonNull<ffi::avr_irq_t>) -> Self {
Self {
irq_input,
tx: Default::default(),
rx: Default::default(),
}
}
}