riot_wrappers/ztimer/
periodic.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! # [Periodic ZTimer API](https://doc.riot-os.org/ztimer_2periodic_8h.html)

use core::marker::PhantomPinned;
use core::mem::MaybeUninit;
use core::pin::Pin;

/// Return value of a periodic callback
#[derive(Copy, Clone, Debug)]
pub enum Behavior {
    // The explicit values should make the into functions trivial
    /// Invoke the callback on the next cycle
    KeepGoing = riot_sys::ZTIMER_PERIODIC_KEEP_GOING as isize,
    /// Stop invoking the callback
    Abort = (riot_sys::ZTIMER_PERIODIC_KEEP_GOING as isize) ^ 1,
}

impl Into<riot_sys::libc::c_int> for Behavior {
    fn into(self) -> riot_sys::libc::c_int {
        match self {
            Behavior::KeepGoing => riot_sys::ZTIMER_PERIODIC_KEEP_GOING as _,
            // "any other value"
            Behavior::Abort => (riot_sys::ZTIMER_PERIODIC_KEEP_GOING as riot_sys::libc::c_int) ^ 1,
        }
    }
}

impl Into<bool> for Behavior {
    fn into(self) -> bool {
        match self {
            Behavior::KeepGoing => true,
            Behavior::Abort => false,
        }
    }
}

/// Callback for a periodic timer
///
/// This is implemented as a trait (rather than Timer taking a callback directly) as to allow
/// interaction with the handler in a critical section in [Timer::alter].
pub trait Handler: Send {
    fn trigger(&mut self) -> Behavior;
}

/// A periodic timer
///
/// This periodic timer is built on a [clock](super::Clock) and configured with a frequency and
/// tick handler.
///
/// It contains the handler and a `ztimer_periodic_t` C struct that then contains the actual timer
/// as well as a reference to the clock. Being self-referential by nature, it is mainly used in
/// pinned form. It can be started and stopped, and stops automatically when dropped.
pub struct Timer<H: Handler, const HZ: u32> {
    // When pinned, this must note move.
    timer: riot_sys::ztimer_periodic_t,
    // When pinned, a reference to this is held in the timer, but the handler itself can be swapped
    // around when the timer is not running or paused.
    //
    // (In a sense, we're treating this place in the struct like a bare_metal::Mutex, but as
    // ZTimer's behavior of turning off all interrupts during execution is not made explicit
    // anywhere by manifesting a CriticalSection, it's just done unsafely here right away -- and
    // anyhow would need additional trickery to get a &mut out of it).

    // FIXME: Should this be inside ... something (UnsafeCell is insufficient, as a &mut to it
    // still implies exclusive access) that disallows assumptions on exclusivity?
    handler: H,
    // From the .start(), timer has an internal reference to the handler
    _phantom: PhantomPinned,
}

impl<H: Handler, const HZ: u32> Timer<H, HZ> {
    pub fn new(clock: super::Clock<HZ>, handler: H, ticks: super::Ticks<HZ>) -> Self {
        let mut timer = MaybeUninit::uninit();

        // Leaving the arg blank for the moment, to be set later when we have a Pin<&mut self>
        //
        // The type is self-referential (.timer.arg is the whole thing again), a property which is
        // restored at start when pinned.
        let timer = unsafe {
            riot_sys::ztimer_periodic_init(
                clock.0,
                timer.as_mut_ptr(),
                Some(Self::callback),
                core::ptr::null_mut(),
                ticks.0,
            );
            timer.assume_init()
        };

        Timer {
            timer,
            handler,
            _phantom: PhantomPinned,
        }
    }

    fn restore_internal_references(&mut self) {
        self.timer.arg = &mut self.handler as *mut _ as *mut _;
        self.timer.timer.arg = &mut self.timer as *mut _ as *mut _;
    }

    pub fn stop(&mut self) {
        unsafe {
            riot_sys::ztimer_periodic_stop(&mut self.timer);
        }
    }

    extern "C" fn callback(arg: *mut riot_sys::libc::c_void) -> bool {
        let handler = unsafe { &mut *(arg as *mut H) };
        handler.trigger().into()
    }

    // Put on hold not only because I can't move the fields out due to the presence of a Drop
    // implementation, but also because how would one get an owned self after the type was pinned?
    //     /// Stop the periodic timer, and return the handler that was in the timer
    //     pub fn to_parts(mut self) -> H {
    //         self.stop();
    //         return self.handler;
    //     }

    /// Obtain a mutable reference to the handler.
    ///
    /// This can be used, for example, to feed data into a handler that is sent out whenever the
    /// timer triggers.
    ///
    /// This is relatively invasive to the system as it creates a critical section (ie. possibly
    /// delaying the execution of the next timer, or even other interrupts). In many cases, the
    /// preferable way to send data to the timer is to use a lock-free data structure.
    // This needs to take a &mut self to avoid nesting, otherwise two code paths could do nested
    // .alter().
    pub fn alter<R, F: FnOnce(&mut H) -> R>(self: &mut Pin<&mut Self>, f: F) -> R {
        crate::interrupt::free(|_| {
            // unsafe: Only accessing handler
            let s = unsafe { Pin::into_inner_unchecked(self.as_mut()) };
            f(&mut s.handler)
        })
    }
}

impl<H: Handler + 'static, const HZ: u32> Timer<H, HZ> {
    #[doc(alias = "ztimer_periodic_start")]
    /// Start the timer, calling the handler at every interval.
    ///
    /// This requires a `Handler + 'static` because it relies on the timer's drop to stop the
    /// process, and only a static handler can still safely be called if that drop never happens.
    ///
    /// (For non-static handlers, a scoped version might be introduced later).
    pub fn start(self: &mut Pin<&mut Self>) {
        unsafe {
            // unsafe: Nothing moved around with these references
            let s = Pin::into_inner_unchecked(self.as_mut());
            s.restore_internal_references();
            // unsafe: C API
            riot_sys::ztimer_periodic_start(&mut s.timer);
        }
    }
}

impl<H: Handler, const HZ: u32> Drop for Timer<H, HZ> {
    fn drop(&mut self) {
        self.stop();
        // and then drop the fields
    }
}