From 4a27d276e7b484f0b4a20e4ac74217eee63249a7 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sun, 14 Jan 2024 06:56:16 +0000 Subject: [PATCH] Leave out only one support for embassy-time --- Cargo.toml | 6 - src/lib.rs | 3 +- src/timer.rs | 470 ++++++++++----------------------------------------- 3 files changed, 93 insertions(+), 386 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8306a578412..f1a9d50935d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,11 +27,8 @@ std = ["alloc", "log/std", "esp-idf-hal/std", "embedded-svc/std"] alloc = ["esp-idf-hal/alloc", "embedded-svc/alloc", "uncased/alloc"] nightly = ["embedded-svc/nightly", "esp-idf-hal/nightly"] experimental = ["embedded-svc/experimental"] -embassy-time-driver = ["dep:embassy-time-driver"] -embassy-time-queue-driver = ["dep:embassy-time-queue-driver", "embassy-time", "embassy-sync"] # Propagated esp-idf-hal features -embassy-sync = ["esp-idf-hal/embassy-sync", "dep:embassy-sync"] critical-section = ["esp-idf-hal/critical-section"] wake-from-isr = ["esp-idf-hal/wake-from-isr"] @@ -51,10 +48,7 @@ log = { version = "0.4", default-features = false } uncased = { version = "0.9.7", default-features = false } embedded-svc = { version = "0.26.4", default-features = false, features = ["asyncify"] } esp-idf-hal = { version = "0.42.5", default-features = false, features = ["esp-idf-sys"] } -embassy-sync = { version = "0.5", optional = true } embassy-time-driver = { version = "0.1", optional = true, features = ["tick-hz-1_000_000"] } -embassy-time-queue-driver = { version = "0.1", optional = true } -embassy-time = { version = "0.3", optional = true } embassy-futures = "0.1" [build-dependencies] diff --git a/src/lib.rs b/src/lib.rs index 5a5955a2ce8..e81ba72c126 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,7 @@ //! //! - `std`: Enable the use of std. Enabled by default. //! - `experimental`: Enable the use of experimental features. -//! - `embassy-time-driver` -//! - `embassy-time-queue-queue` +//! - `embassy-time-driver`: Implement an embassy time driver. #![cfg_attr(not(feature = "std"), no_std)] #![allow(async_fn_in_trait)] //#![allow(unknown_lints)] diff --git a/src/timer.rs b/src/timer.rs index c4a0f1e705d..aa3225d7f72 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -348,425 +348,139 @@ mod asyncify { } } -pub mod embassy_time { - #[cfg(any(feature = "embassy-time-driver", feature = "embassy-time-queue-driver"))] - pub mod driver { - use core::cell::UnsafeCell; +#[cfg(feature = "embassy-time-driver")] +pub mod embassy_time_driver { + use core::cell::UnsafeCell; - use heapless::Vec; + use heapless::Vec; - use ::embassy_time_driver::{AlarmHandle, Driver}; + use ::embassy_time_driver::{AlarmHandle, Driver}; - use crate::hal::task::CriticalSection; + use crate::hal::task::CriticalSection; - use crate::sys::*; + use crate::sys::*; - use crate::timer::*; + use crate::timer::*; - struct Alarm { - timer: Option>, - #[allow(clippy::type_complexity)] - callback: Option<(fn(*mut ()), *mut ())>, - } - - struct EspDriver { - alarms: UnsafeCell>, - cs: CriticalSection, - } - - impl EspDriver { - const fn new() -> Self { - Self { - alarms: UnsafeCell::new(Vec::new()), - cs: CriticalSection::new(), - } - } - - fn call(&self, id: u8) { - let callback = { - let _guard = self.cs.enter(); - - let alarm = self.alarm(id); - - alarm.callback - }; + struct Alarm { + timer: Option>, + #[allow(clippy::type_complexity)] + callback: Option<(fn(*mut ()), *mut ())>, + } - if let Some((func, arg)) = callback { - func(arg) - } - } + struct EspDriver { + alarms: UnsafeCell>, + cs: CriticalSection, + } - #[allow(clippy::mut_from_ref)] - fn alarm(&self, id: u8) -> &mut Alarm { - &mut unsafe { self.alarms.get().as_mut() }.unwrap()[id as usize] + impl EspDriver { + const fn new() -> Self { + Self { + alarms: UnsafeCell::new(Vec::new()), + cs: CriticalSection::new(), } } - unsafe impl Send for EspDriver {} - unsafe impl Sync for EspDriver {} - - impl Driver for EspDriver { - fn now(&self) -> u64 { - unsafe { esp_timer_get_time() as _ } - } - - unsafe fn allocate_alarm(&self) -> Option { - let id = { - let _guard = self.cs.enter(); - - let id = self.alarms.get().as_mut().unwrap().len(); - - if id < MAX_ALARMS { - self.alarms - .get() - .as_mut() - .unwrap() - .push(Alarm { - timer: None, - callback: None, - }) - .unwrap_or_else(|_| unreachable!()); - - id as u8 - } else { - return None; - } - }; - - let service = EspTimerService::::new().unwrap(); - - // Driver is always statically allocated, so this is safe - let static_self: &'static Self = core::mem::transmute(self); - - self.alarm(id).timer = Some(service.timer(move || static_self.call(id)).unwrap()); - - Some(AlarmHandle::new(id)) - } - - fn set_alarm_callback(&self, handle: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + fn call(&self, id: u8) { + let callback = { let _guard = self.cs.enter(); - let alarm = self.alarm(handle.id()); + let alarm = self.alarm(id); - alarm.callback = Some((callback, ctx)); - } - - fn set_alarm(&self, handle: AlarmHandle, timestamp: u64) -> bool { - let alarm = self.alarm(handle.id()); + alarm.callback + }; - let now = self.now(); - - if now < timestamp { - alarm - .timer - .as_mut() - .unwrap() - .after(Duration::from_micros(timestamp - now)) - .unwrap(); - true - } else { - false - } + if let Some((func, arg)) = callback { + func(arg) } } - pub type LinkWorkaround = [*mut (); 4]; - - static mut __INTERNAL_REFERENCE: LinkWorkaround = [ - _embassy_time_now as *mut _, - _embassy_time_allocate_alarm as *mut _, - _embassy_time_set_alarm_callback as *mut _, - _embassy_time_set_alarm as *mut _, - ]; - - pub fn link() -> LinkWorkaround { - unsafe { __INTERNAL_REFERENCE } + #[allow(clippy::mut_from_ref)] + fn alarm(&self, id: u8) -> &mut Alarm { + &mut unsafe { self.alarms.get().as_mut() }.unwrap()[id as usize] } - - ::embassy_time_driver::time_driver_impl!(static DRIVER: EspDriver = EspDriver::new()); } - #[cfg(feature = "embassy-time-queue-driver")] - pub mod queue { - #[cfg(esp_idf_esp_timer_supports_isr_dispatch_method)] - use crate::hal::interrupt::embassy_sync::IsrRawMutex as RawMutexImpl; - - #[cfg(not(esp_idf_esp_timer_supports_isr_dispatch_method))] - use crate::hal::task::embassy_sync::EspRawMutex as RawMutexImpl; - - use crate::sys::*; - - use generic_queue::*; + unsafe impl Send for EspDriver {} + unsafe impl Sync for EspDriver {} - struct AlarmImpl(esp_timer_handle_t); + impl Driver for EspDriver { + fn now(&self) -> u64 { + unsafe { esp_timer_get_time() as _ } + } - impl AlarmImpl { - unsafe extern "C" fn handle_isr(alarm_context: *mut core::ffi::c_void) { - let alarm_context = (alarm_context as *const AlarmContext).as_ref().unwrap(); + unsafe fn allocate_alarm(&self) -> Option { + let id = { + let _guard = self.cs.enter(); - if crate::hal::interrupt::active() { - #[cfg(esp_idf_esp_timer_supports_isr_dispatch_method)] - { - let signaled = crate::hal::interrupt::with_isr_yield_signal(move || { - (alarm_context.callback)(alarm_context.ctx); - }); + let id = self.alarms.get().as_mut().unwrap().len(); - if signaled { - crate::sys::esp_timer_isr_dispatch_need_yield(); - } - } + if id < MAX_ALARMS { + self.alarms + .get() + .as_mut() + .unwrap() + .push(Alarm { + timer: None, + callback: None, + }) + .unwrap_or_else(|_| unreachable!()); - #[cfg(not(esp_idf_esp_timer_supports_isr_dispatch_method))] - unreachable!(); + id as u8 } else { - (alarm_context.callback)(alarm_context.ctx); + return None; } - } - } + }; - unsafe impl Send for AlarmImpl {} - - impl Alarm for AlarmImpl { - fn new(context: &AlarmContext) -> Self { - #[cfg(esp_idf_esp_timer_supports_isr_dispatch_method)] - let dispatch_method = esp_timer_dispatch_t_ESP_TIMER_ISR; - - #[cfg(not(esp_idf_esp_timer_supports_isr_dispatch_method))] - let dispatch_method = esp_timer_dispatch_t_ESP_TIMER_TASK; - - let mut handle: esp_timer_handle_t = core::ptr::null_mut(); - - esp!(unsafe { - esp_timer_create( - &esp_timer_create_args_t { - callback: Some(AlarmImpl::handle_isr), - name: b"embassy-time-queue\0" as *const _ as *const _, - arg: context as *const _ as *mut _, - dispatch_method, - skip_unhandled_events: false, - }, - &mut handle as *mut _, - ) - }) - .unwrap(); - - Self(handle) - } + let service = EspTimerService::::new().unwrap(); - fn schedule(&mut self, timestamp: u64) { - let now = unsafe { esp_timer_get_time() as _ }; - let after = if timestamp <= now { 0 } else { timestamp - now }; + // Driver is always statically allocated, so this is safe + let static_self: &'static Self = core::mem::transmute(self); - unsafe { - esp_timer_stop(self.0); - } + self.alarm(id).timer = Some(service.timer(move || static_self.call(id)).unwrap()); - if timestamp < u64::MAX { - esp!(unsafe { esp_timer_start_once(self.0, after as _) }).unwrap(); - } - } + Some(AlarmHandle::new(id)) } - pub type LinkWorkaround = [*mut (); 1]; + fn set_alarm_callback(&self, handle: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + let _guard = self.cs.enter(); - static mut __INTERNAL_REFERENCE: LinkWorkaround = [_embassy_time_schedule_wake as *mut _]; + let alarm = self.alarm(handle.id()); - pub fn link() -> (LinkWorkaround, super::driver::LinkWorkaround) { - (unsafe { __INTERNAL_REFERENCE }, super::driver::link()) + alarm.callback = Some((callback, ctx)); } - ::embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new()); - - mod generic_queue { - use core::cell::RefCell; - use core::cmp::Ordering; - use core::task::Waker; - - use embassy_sync::blocking_mutex::{raw::RawMutex, Mutex}; - - use embassy_time::Instant; - use embassy_time_queue_driver::TimerQueue; - - use heapless::sorted_linked_list::{LinkedIndexU8, Min, SortedLinkedList}; - - #[derive(Debug)] - struct Timer { - at: Instant, - waker: Waker, + fn set_alarm(&self, handle: AlarmHandle, timestamp: u64) -> bool { + let alarm = self.alarm(handle.id()); + + let now = self.now(); + + if now < timestamp { + alarm + .timer + .as_mut() + .unwrap() + .after(Duration::from_micros(timestamp - now)) + .unwrap(); + true + } else { + false } + } + } - impl PartialEq for Timer { - fn eq(&self, other: &Self) -> bool { - self.at == other.at - } - } - - impl Eq for Timer {} - - impl PartialOrd for Timer { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl Ord for Timer { - fn cmp(&self, other: &Self) -> Ordering { - self.at.cmp(&other.at) - } - } - - pub struct AlarmContext { - pub callback: fn(*mut ()), - pub ctx: *mut (), - } - - impl AlarmContext { - const fn new() -> Self { - Self { - callback: Self::noop, - ctx: core::ptr::null_mut(), - } - } - - fn set(&mut self, callback: fn(*mut ()), ctx: *mut ()) { - self.callback = callback; - self.ctx = ctx; - } - - fn noop(_ctx: *mut ()) {} - } - - unsafe impl Send for AlarmContext {} - - pub trait Alarm { - fn new(context: &AlarmContext) -> Self; - fn schedule(&mut self, timestamp: u64); - } - - struct InnerQueue { - queue: SortedLinkedList, - alarm: Option, - alarm_context: AlarmContext, - alarm_at: Instant, - } - - impl InnerQueue { - const fn new() -> Self { - Self { - queue: SortedLinkedList::new_u8(), - alarm: None, - alarm_context: AlarmContext::new(), - alarm_at: Instant::MAX, - } - } - - fn schedule_wake(&mut self, at: Instant, waker: &Waker) { - self.initialize(); - - self.queue - .find_mut(|timer| timer.waker.will_wake(waker)) - .map(|mut timer| { - timer.at = at; - timer.finish(); - }) - .unwrap_or_else(|| { - let mut timer = Timer { - waker: waker.clone(), - at, - }; - - loop { - match self.queue.push(timer) { - Ok(()) => break, - Err(e) => timer = e, - } - - self.queue.pop().unwrap().waker.wake(); - } - }); - - // Don't wait for the alarm callback to trigger and directly - // dispatch all timers that are already due - // - // Then update the alarm if necessary - self.dispatch(); - } - - fn dispatch(&mut self) { - let now = Instant::now(); - - while self.queue.peek().filter(|timer| timer.at <= now).is_some() { - self.queue.pop().unwrap().waker.wake(); - } - - self.update_alarm(); - } - - fn update_alarm(&mut self) { - if let Some(timer) = self.queue.peek() { - let new_at = timer.at; - - if self.alarm_at != new_at { - self.alarm_at = new_at; - self.alarm.as_mut().unwrap().schedule(new_at.as_ticks()); - } - } else { - self.alarm_at = Instant::MAX; - self.alarm - .as_mut() - .unwrap() - .schedule(Instant::MAX.as_ticks()); - } - } - - fn handle_alarm(&mut self) { - self.alarm_at = Instant::MAX; - - self.dispatch(); - } - - fn initialize(&mut self) { - if self.alarm.is_none() { - self.alarm = Some(A::new(&self.alarm_context)); - } - } - } - - pub struct Queue { - inner: Mutex>>, - } - - impl Queue { - pub const fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(InnerQueue::new())), - } - } - - fn schedule_wake(&'static self, at: Instant, waker: &Waker) { - self.inner.lock(|inner| { - let mut inner = inner.borrow_mut(); - inner - .alarm_context - .set(Self::handle_alarm_callback, self as *const _ as _); - inner.schedule_wake(at, waker); - }); - } - - fn handle_alarm(&self) { - self.inner.lock(|inner| inner.borrow_mut().handle_alarm()); - } + pub type LinkWorkaround = [*mut (); 4]; - fn handle_alarm_callback(ctx: *mut ()) { - unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); - } - } + static mut __INTERNAL_REFERENCE: LinkWorkaround = [ + _embassy_time_now as *mut _, + _embassy_time_allocate_alarm as *mut _, + _embassy_time_set_alarm_callback as *mut _, + _embassy_time_set_alarm as *mut _, + ]; - impl TimerQueue for Queue { - fn schedule_wake(&'static self, at: u64, waker: &Waker) { - Queue::schedule_wake(self, Instant::from_ticks(at), waker); - } - } - } + pub fn link() -> LinkWorkaround { + unsafe { __INTERNAL_REFERENCE } } + + ::embassy_time_driver::time_driver_impl!(static DRIVER: EspDriver = EspDriver::new()); }