use core::cell::RefCell;
use core::future::poll_fn;
use core::ops::Deref;
use core::ptr;
use core::task::{Poll, Waker};
use critical_section::Mutex;
use super::{Error, I2c};
use crate::pac::{bsc0, BSC0, BSC1};
static WAKERS: [Mutex<RefCell<Option<Waker>>>; 2] = [
Mutex::new(RefCell::new(None)),
Mutex::new(RefCell::new(None)),
];
pub fn on_irq() {
let bsc0 = unsafe { BSC0::steal() };
let bsc1 = unsafe { BSC1::steal() };
let woken = [mask_if_asserted(&bsc0), mask_if_asserted(&bsc1)];
critical_section::with(|cs| {
for (slot, fired) in WAKERS.iter().zip(woken) {
if fired {
if let Some(waker) = slot.borrow_ref_mut(cs).take() {
waker.wake();
}
}
}
});
}
fn mask_if_asserted(regs: &bsc0::RegisterBlock) -> bool {
let control = regs.c().read();
let (done_armed, tx_armed, rx_armed) = (
control.intd().bit_is_set(),
control.intt().bit_is_set(),
control.intr().bit_is_set(),
);
if !(done_armed || tx_armed || rx_armed) {
return false;
}
let status = regs.s().read();
let asserted = status.err().bit_is_set()
|| status.clkt().bit_is_set()
|| (done_armed && status.done().bit_is_set())
|| (tx_armed && status.txw().bit_is_set())
|| (rx_armed && status.rxr().bit_is_set());
if !asserted {
return false;
}
mask(regs);
true
}
fn mask(regs: &bsc0::RegisterBlock) {
regs.c()
.modify(|_, w| w.intd().clear_bit().intt().clear_bit().intr().clear_bit());
}
fn abandon(regs: &bsc0::RegisterBlock) {
mask(regs);
regs.c().write(|w| {
unsafe { w.clear().bits(0b11) };
w.i2cen().bit(true)
});
regs.s()
.write(|w| w.done().bit(true).err().bit(true).clkt().bit(true));
}
struct OnCancel {
regs: *const bsc0::RegisterBlock,
armed: bool,
}
impl Drop for OnCancel {
fn drop(&mut self) {
if self.armed {
abandon(unsafe { &*self.regs });
}
}
}
impl<I: Deref<Target = bsc0::RegisterBlock>> I2c<'_, I> {
fn waker_slot(&self) -> usize {
usize::from(ptr::eq(ptr::from_ref(&*self.bsc), BSC1::PTR))
}
fn unpark(&self) {
mask(&self.bsc);
critical_section::with(|cs| {
*WAKERS[self.waker_slot()].borrow_ref_mut(cs) = None;
});
}
fn park(&self, waker: &Waker, reading: bool) -> bool {
critical_section::with(|cs| {
*WAKERS[self.waker_slot()].borrow_ref_mut(cs) = Some(waker.clone());
});
self.bsc.c().modify(|_, w| {
w.intd().set_bit();
if reading {
w.intr().set_bit()
} else {
w.intt().set_bit()
}
});
let status = self.bsc.s().read();
let ready = status.done().bit_is_set()
|| status.err().bit_is_set()
|| status.clkt().bit_is_set()
|| if reading {
status.rxd().bit_is_set()
} else {
status.txd().bit_is_set()
};
if ready {
mask(&self.bsc);
}
ready
}
async fn write_one_async(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error> {
if bytes.is_empty() {
return Err(Error::ZeroLengthUnsupported);
}
let mut cancel = OnCancel {
regs: ptr::from_ref(&*self.bsc),
armed: true,
};
self.one_shot(address, false, bytes.len());
let deadline = self.timer.now_micros() + Self::timeout_us(bytes.len());
let mut sent = 0;
let outcome = poll_fn(|cx| {
loop {
let status = self.bsc.s().read();
if status.err().bit_is_set() {
self.unpark();
self.clear_status();
return Poll::Ready(Err(Error::NoAcknowledge));
}
if status.clkt().bit_is_set() {
self.unpark();
self.abandon();
return Poll::Ready(Err(Error::Timeout));
}
if sent < bytes.len() && status.txd().bit_is_set() {
unsafe {
self.bsc.fifo().write(|w| w.data().bits(bytes[sent]));
}
sent += 1;
continue;
}
if status.done().bit_is_set() {
self.unpark();
self.clear_status();
return Poll::Ready(Ok(()));
}
if self.timer.now_micros() > deadline {
self.unpark();
self.abandon();
return Poll::Ready(Err(Error::Timeout));
}
if !self.park(cx.waker(), false) {
return Poll::Pending;
}
}
})
.await;
cancel.armed = false;
outcome
}
async fn read_one_async(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
if buffer.is_empty() {
return Err(Error::ZeroLengthUnsupported);
}
let mut cancel = OnCancel {
regs: ptr::from_ref(&*self.bsc),
armed: true,
};
self.one_shot(address, true, buffer.len());
let deadline = self.timer.now_micros() + Self::timeout_us(buffer.len());
let mut received = 0;
let outcome = poll_fn(|cx| loop {
let status = self.bsc.s().read();
if status.err().bit_is_set() {
self.unpark();
self.clear_status();
return Poll::Ready(Err(Error::NoAcknowledge));
}
if status.clkt().bit_is_set() {
self.unpark();
self.abandon();
return Poll::Ready(Err(Error::Timeout));
}
if received < buffer.len() && status.rxd().bit_is_set() {
buffer[received] = self.bsc.fifo().read().data().bits();
received += 1;
continue;
}
if status.done().bit_is_set() && received >= buffer.len() {
self.unpark();
self.clear_status();
return Poll::Ready(Ok(()));
}
if self.timer.now_micros() > deadline {
let complete = status.done().bit_is_set();
self.unpark();
self.abandon();
return Poll::Ready(Err(if complete {
Error::Incomplete {
received,
requested: buffer.len(),
}
} else {
Error::Timeout
}));
}
if !self.park(cx.waker(), true) {
return Poll::Pending;
}
})
.await;
cancel.armed = false;
outcome
}
}
impl<I: Deref<Target = bsc0::RegisterBlock>> embedded_hal_async::i2c::I2c for I2c<'_, I> {
async fn transaction(
&mut self,
address: u8,
operations: &mut [embedded_hal::i2c::Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
embedded_hal::i2c::Operation::Read(buffer) => {
self.read_one_async(address, buffer).await?
}
embedded_hal::i2c::Operation::Write(bytes) => {
self.write_one_async(address, bytes).await?
}
}
}
Ok(())
}
}