use core::future::poll_fn;
use core::marker::PhantomData;
use core::sync::atomic::{Ordering, compiler_fence};
use core::task::Poll;
#[cfg(all(any(stm32wb, stm32wl5x), feature = "low-power"))]
use critical_section::CriticalSection;
use embassy_hal_internal::PeripheralType;
use embassy_sync::waitqueue::AtomicWaker;
use crate::Peri;
use crate::cpu::CoreId;
use crate::rcc::{self, RccPeripheral};
use crate::{interrupt, pac};
#[derive(Debug)]
pub enum HsemError {
LockFailed,
}
#[cfg(all(not(all(stm32wb, feature = "low-power")), not(all(stm32wl5x, feature = "low-power"))))]
const PUB_CHANNELS: usize = 6;
#[cfg(all(stm32wl5x, feature = "low-power"))]
const PUB_CHANNELS: usize = 5;
#[cfg(all(stm32wb, feature = "low-power"))]
const PUB_CHANNELS: usize = 4;
pub struct HardwareSemaphoreInterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for HardwareSemaphoreInterruptHandler<T> {
unsafe fn on_interrupt() {
let core_id = CoreId::current();
let isr = T::regs().isr(core_id.to_index()).read();
for number in 0..5 {
if isr.isf(number as usize) {
T::regs()
.icr(core_id.to_index())
.write(|w| w.set_isc(number as usize, true));
T::state().waker_for(number).wake();
}
}
}
}
pub struct HardwareSemaphoreMutex<'a, T: Instance> {
index: u8,
process_id: u8,
_lifetime: PhantomData<&'a mut T>,
}
impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> {
fn drop(&mut self) {
let core_id = CoreId::current();
T::regs()
.icr(core_id.to_index())
.write(|w| w.set_isc(self.index as usize, true));
critical_section::with(|_| {
T::regs()
.ier(core_id.to_index())
.modify(|w| w.set_ise(self.index as usize, false));
});
HardwareSemaphoreChannel::<'a, T> {
index: self.index,
_lifetime: PhantomData,
}
.unlock(self.process_id);
}
}
pub struct HardwareSemaphoreChannel<'a, T: Instance> {
index: u8,
_lifetime: PhantomData<&'a mut T>,
}
impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> {
pub(crate) const fn new(number: u8) -> Self {
core::assert!(number > 0 && number <= 6);
Self {
index: number - 1,
_lifetime: PhantomData,
}
}
pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> {
let _scoped_wake_guard = T::RCC_INFO.wake_guard();
let core_id = CoreId::current();
poll_fn(|cx| {
T::state().waker_for(self.index).register(cx.waker());
compiler_fence(Ordering::SeqCst);
critical_section::with(|_| {
T::regs()
.ier(core_id.to_index())
.modify(|w| w.set_ise(self.index as usize, true));
});
match self.try_lock(process_id) {
Some(mutex) => Poll::Ready(mutex),
None => Poll::Pending,
}
})
.await
}
pub fn blocking_lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> {
loop {
if let Some(lock) = self.try_lock(process_id) {
return lock;
}
}
}
pub fn try_lock(&mut self, process_id: u8) -> Option<HardwareSemaphoreMutex<'a, T>> {
if self.two_step_lock(process_id).is_ok() {
Some(HardwareSemaphoreMutex {
index: self.index,
process_id: process_id,
_lifetime: PhantomData,
})
} else {
None
}
}
pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> {
T::regs().r(self.index as usize).write(|w| {
w.set_procid(process_id);
w.set_coreid(CoreId::current() as u8);
w.set_lock(true);
});
let reg = T::regs().r(self.index as usize).read();
match (
reg.lock(),
reg.coreid() == CoreId::current() as u8,
reg.procid() == process_id,
) {
(true, true, true) => Ok(()),
_ => Err(HsemError::LockFailed),
}
}
pub fn one_step_lock(&mut self) -> Result<(), HsemError> {
let reg = T::regs().rlr(self.index as usize).read();
match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) {
(false, true, 0) => Ok(()),
_ => Err(HsemError::LockFailed),
}
}
pub fn unlock(&mut self, process_id: u8) {
T::regs().r(self.index as usize).write(|w| {
w.set_procid(process_id);
w.set_coreid(CoreId::current() as u8);
w.set_lock(false);
});
}
pub const fn channel(&self) -> u8 {
self.index + 1
}
}
pub struct HardwareSemaphore<T: Instance> {
_type: PhantomData<T>,
}
impl<T: Instance> HardwareSemaphore<T> {
pub fn new<'d>(
_peripheral: Peri<'d, T>,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, HardwareSemaphoreInterruptHandler<T>> + 'd,
) -> Self {
rcc::enable_and_reset_without_stop::<T>();
HardwareSemaphore { _type: PhantomData }
}
pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> {
#[cfg(all(stm32wb, feature = "low-power"))]
core::assert!(number != 3 && number != 4);
#[cfg(all(stm32wl5x, feature = "low-power"))]
core::assert!(number != 3);
HardwareSemaphoreChannel::new(number)
}
pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] {
[
HardwareSemaphoreChannel::new(1),
HardwareSemaphoreChannel::new(2),
#[cfg(not(all(any(stm32wb, stm32wl5x), feature = "low-power")))]
HardwareSemaphoreChannel::new(3),
#[cfg(not(all(stm32wb, feature = "low-power")))]
HardwareSemaphoreChannel::new(4),
HardwareSemaphoreChannel::new(5),
HardwareSemaphoreChannel::new(6),
]
}
pub fn unlock_all(&mut self, key: u16, core_id: u8) {
T::regs().cr().write(|w| {
w.set_key(key);
w.set_coreid(core_id);
});
}
pub fn set_clear_key(&mut self, key: u16) {
T::regs().keyr().modify(|w| w.set_key(key));
}
pub fn get_clear_key(&mut self) -> u16 {
T::regs().keyr().read().key()
}
}
#[cfg(all(any(stm32wb, stm32wl5x), feature = "low-power"))]
pub(crate) fn init_hsem(cs: CriticalSection) {
rcc::enable_and_reset_with_cs::<crate::peripherals::HSEM>(cs);
}
#[cfg(any(all(stm32wb, feature = "low-power"), stm32wl5x))]
pub(crate) const fn get_hsem<'a>(index: usize) -> HardwareSemaphoreChannel<'a, crate::peripherals::HSEM> {
match index {
#[cfg(any(stm32wb, stm32wl5x))]
3 => HardwareSemaphoreChannel::new(3),
#[cfg(stm32wb)]
4 => HardwareSemaphoreChannel::new(4),
_ => core::unreachable!(),
}
}
struct State {
wakers: [AtomicWaker; 6],
}
impl State {
const fn new() -> Self {
Self {
wakers: [const { AtomicWaker::new() }; 6],
}
}
const fn waker_for(&self, index: u8) -> &AtomicWaker {
&self.wakers[index as usize]
}
}
trait SealedInstance {
fn regs() -> pac::hsem::Hsem;
fn state() -> &'static State;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {
type Interrupt: interrupt::typelevel::Interrupt;
}
impl SealedInstance for crate::peripherals::HSEM {
fn regs() -> crate::pac::hsem::Hsem {
crate::pac::HSEM
}
fn state() -> &'static State {
static STATE: State = State::new();
&STATE
}
}
foreach_interrupt!(
($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => {
impl Instance for crate::peripherals::$inst {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
);