esp_wifi/preempt_builtin/timer/
mod.rs

1use esp_hal::{
2    interrupt::{InterruptHandler, Priority},
3    sync::Locked,
4    time::Rate,
5    trapframe::TrapFrame,
6};
7
8#[cfg_attr(xtensa, path = "xtensa.rs")]
9#[cfg_attr(riscv, path = "riscv.rs")]
10mod arch_specific;
11
12pub(crate) use arch_specific::*;
13
14use crate::TimeBase;
15
16/// The timer responsible for time slicing.
17const TIMESLICE_FREQUENCY: Rate = Rate::from_hz(crate::CONFIG.tick_rate_hz);
18
19pub(crate) static TIMER: Locked<Option<TimeBase>> = Locked::new(None);
20
21pub(crate) fn setup_timebase(mut timer: TimeBase) {
22    // The timer needs to tick at Priority 1 to prevent accidentally interrupting
23    // priority 1 limited locks.
24    let cb: extern "C" fn() = unsafe { core::mem::transmute(timer_tick_handler as *const ()) };
25    let handler = InterruptHandler::new(cb, Priority::Priority1);
26
27    timer.set_interrupt_handler(handler);
28    unwrap!(timer.start(TIMESLICE_FREQUENCY.as_duration()));
29    TIMER.with(|t| {
30        timer.enable_interrupt(true);
31        t.replace(timer);
32    });
33}
34
35pub(crate) fn clear_timer_interrupt() {
36    TIMER.with(|timer| {
37        unwrap!(timer.as_mut()).clear_interrupt();
38    });
39}
40
41pub(crate) fn disable_timebase() {
42    TIMER.with(|timer| {
43        let mut timer = unwrap!(timer.take());
44        timer.enable_interrupt(false);
45        unwrap!(timer.cancel());
46    });
47}
48
49extern "C" fn timer_tick_handler(_context: &mut TrapFrame) {
50    clear_timer_interrupt();
51
52    // `task_switch` must be called on a single interrupt priority level only.
53    // Because on ESP32 the software interrupt is triggered at priority 3 but
54    // the timer interrupt is triggered at priority 1, we need to trigger the
55    // software interrupt manually.
56    cfg_if::cfg_if! {
57        if #[cfg(esp32)] {
58            yield_task();
59        } else {
60            super::task_switch(_context);
61        }
62    }
63}