capsules_core/
alarm.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Tock syscall driver capsule for Alarms, which issue callbacks when
6//! a point in time has been reached.
7
8use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
9use kernel::hil::time::{self, Alarm, Ticks};
10use kernel::syscall::{CommandReturn, SyscallDriver};
11use kernel::{ErrorCode, ProcessId};
12
13/// Syscall driver number.
14use crate::driver;
15pub const DRIVER_NUM: usize = driver::NUM::Alarm as usize;
16
17#[derive(Copy, Clone, Debug)]
18struct Expiration<T: Ticks> {
19    reference: T,
20    dt: T,
21}
22
23#[derive(Copy, Clone)]
24pub struct AlarmData<T: Ticks> {
25    expiration: Option<Expiration<T>>,
26}
27
28const ALARM_CALLBACK_NUM: usize = 0;
29const NUM_UPCALLS: u8 = 1;
30
31impl<T: Ticks> Default for AlarmData<T> {
32    fn default() -> AlarmData<T> {
33        AlarmData { expiration: None }
34    }
35}
36
37pub struct AlarmDriver<'a, A: Alarm<'a>> {
38    alarm: &'a A,
39    app_alarms:
40        Grant<AlarmData<A::Ticks>, UpcallCount<NUM_UPCALLS>, AllowRoCount<0>, AllowRwCount<0>>,
41}
42
43impl<'a, A: Alarm<'a>> AlarmDriver<'a, A> {
44    pub const fn new(
45        alarm: &'a A,
46        grant: Grant<
47            AlarmData<A::Ticks>,
48            UpcallCount<NUM_UPCALLS>,
49            AllowRoCount<0>,
50            AllowRwCount<0>,
51        >,
52    ) -> AlarmDriver<'a, A> {
53        AlarmDriver {
54            alarm,
55            app_alarms: grant,
56        }
57    }
58
59    /// Finds the earliest non-expired alarm from an iterator of
60    /// [`Expiration`]s.
61    ///
62    /// This function iterates through a collection of alarms and identifies
63    /// the one that will fire next.
64    ///
65    /// # Parameters
66    ///
67    /// - `now`: The current time, used to determine if an alarm has already
68    ///   expired.
69    /// - `expirations`: An iterator of tuples, where each tuple contains:
70    ///   - [`Expiration`]: The alarm data.
71    ///   - `UD`: User-defined data associated with the alarm. This is used in
72    ///     callbacks and return values to identify a particular alarm instance.
73    ///   - `F`: A callback function that is invoked if the alarm has already
74    ///     expired. The callback receives the [`Expiration`] and a reference to
75    ///     its associated `UD`.
76    ///
77    /// # Behavior
78    ///
79    /// An [`Expiration`] `exp` is considered *not* expired if the current time
80    /// `now` is within the range `[exp.reference, exp.reference + exp.dt)`
81    /// using a wrapping-add.
82    ///
83    /// For each expired alarm, its associated callback `F` is called. If the
84    /// callback returns `Some(R)`, iteration stops immediately, and this
85    /// function returns `Err((exp, ud, R))`. This allows for early exit, and
86    /// will stop consuming the `expirations` iterator beyond this point.
87    ///
88    /// # Return Value
89    ///
90    /// - `Ok(Some((exp, ud)))`: If one or more non-expired alarms are found,
91    ///   this returns the [`Expiration`] and user data for the one that will
92    ///   fire earliest, relative to `now`.
93    /// - `Ok(None)`: If the `expirations` iterator is empty or all alarms have
94    ///   already expired.
95    /// - `Err((exp, ud, R))`: If a callback for an expired alarm signals to
96    ///   stop iteration.
97    fn earliest_alarm<UD, R, F: FnOnce(Expiration<A::Ticks>, &UD) -> Option<R>>(
98        now: A::Ticks,
99        expirations: impl Iterator<Item = (Expiration<A::Ticks>, UD, F)>,
100    ) -> Result<Option<(Expiration<A::Ticks>, UD)>, (Expiration<A::Ticks>, UD, R)> {
101        let mut earliest: Option<(Expiration<A::Ticks>, UD)> = None;
102
103        for (exp, ud, expired_handler) in expirations {
104            let Expiration {
105                reference: exp_ref,
106                dt: exp_dt,
107            } = exp;
108
109            // Calculate the absolute time at which this alarm is set to fire.
110            let exp_end = exp_ref.wrapping_add(exp_dt);
111
112            // An alarm is considered expired if `now` is not within the
113            // interval `[reference; reference + dt)`. If it has expired, we
114            // call its handler.
115            if !now.within_range(exp_ref, exp_end) {
116                if let Some(retval) = expired_handler(exp, &ud) {
117                    // The handler requested to stop iteration, so we exit
118                    // early.
119                    return Err((exp, ud, retval));
120                }
121
122                // We continue iteration, but skip to the next alarm since this
123                // one has expired. Expired alarms are not a candidate for the
124                // "earliest", non-expired alarm returned by this function.
125                continue;
126            }
127
128            // `exp` has not yet expired. Now, check if it is earlier than the
129            // current earliest known alarm:
130            match &earliest {
131                None => {
132                    // This is the first non-expired alarm we've found.
133                    earliest = Some((exp, ud));
134                }
135                Some((
136                    Expiration {
137                        reference: earliest_ref,
138                        dt: earliest_dt,
139                    },
140                    _,
141                )) => {
142                    // We already have a candidate for the earliest alarm. To
143                    // compare, we calculate the remaining time for both alarms
144                    // relative to `now`. The one with less time remaining is
145                    // earlier.
146                    let earliest_end = earliest_ref.wrapping_add(*earliest_dt);
147
148                    let exp_remain = exp_end.wrapping_sub(now);
149                    let earliest_remain = earliest_end.wrapping_sub(now);
150
151                    if exp_remain < earliest_remain {
152                        // The current alarm `exp` will fire sooner than our
153                        // previous `earliest`.
154                        earliest = Some((exp, ud));
155                    }
156                }
157            }
158        }
159
160        // We have iterated over all alarms. Return the earliest one found.
161        Ok(earliest)
162    }
163
164    /// Re-arm the timer. This must be called in response to the underlying
165    /// timer firing, or the set of [`Expiration`]s changing. This will iterate
166    /// over all [`Expiration`]s and
167    ///
168    /// - invoke upcalls for all expired app alarms, resetting them afterwards,
169    /// - re-arming the alarm for the next earliest [`Expiration`], or
170    /// - disarming the alarm if no unexpired [`Expiration`] is found.
171    fn process_rearm_or_callback(&self) {
172        // Ask the clock about a current reference once. This can incur a
173        // volatile read, and this may not be optimized if done in a loop:
174        let now = self.alarm.now();
175
176        let expired_handler = |expired: Expiration<A::Ticks>, process_id: &ProcessId| {
177            // This closure is run on every expired alarm, _after_ the `enter()`
178            // closure on the Grant iterator has returned. We are thus not
179            // risking reentrancy here.
180
181            // Enter the app's grant again:
182            let _ = self.app_alarms.enter(*process_id, |alarm_state, upcalls| {
183                // Reset this app's alarm:
184                alarm_state.expiration = None;
185
186                // Deliver the upcall:
187                let _ = upcalls.schedule_upcall(
188                    ALARM_CALLBACK_NUM,
189                    (
190                        now.into_u32_left_justified() as usize,
191                        expired
192                            .reference
193                            .wrapping_add(expired.dt)
194                            .into_u32_left_justified() as usize,
195                        0,
196                    ),
197                );
198            });
199
200            // Proceed iteration across expirations:
201            None::<()>
202        };
203
204        // Compute the earliest alarm, and invoke the `expired_handler` for
205        // every expired alarm. This will issue a callback and reset the alarms
206        // respectively.
207        let res = Self::earliest_alarm(
208            now,
209            // Pass an interator of all non-None expirations:
210            self.app_alarms.iter().filter_map(|app| {
211                let process_id = app.processid();
212                app.enter(|alarm_state, _upcalls| {
213                    if let Some(exp) = alarm_state.expiration {
214                        Some((exp, process_id, expired_handler))
215                    } else {
216                        None
217                    }
218                })
219            }),
220        );
221
222        // Arm or disarm the alarm accordingly:
223        match res {
224            // No pending alarm, disarm:
225            Ok(None) => {
226                let _ = self.alarm.disarm();
227            }
228
229            // A future, non-expired alarm should fire:
230            Ok(Some((Expiration { reference, dt }, _))) => {
231                self.alarm.set_alarm(reference, dt);
232            }
233
234            // The expired closure has requested to stop iteration. This should
235            // be unreachable, and hence we panic:
236            Err((_, _, ())) => {
237                unreachable!();
238            }
239        }
240    }
241
242    fn rearm_u32_left_justified_expiration(
243        now: A::Ticks,
244        reference_u32: Option<u32>,
245        dt_u32: u32,
246        expiration: &mut Option<Expiration<A::Ticks>>,
247    ) -> u32 {
248        let reference_unshifted = reference_u32.map(|ref_u32| ref_u32 >> A::Ticks::u32_padding());
249
250        // If the underlying timer is less than 32-bit wide, userspace is able
251        // to provide a finer `reference` and `dt` resolution than we can
252        // possibly represent in the kernel.
253        //
254        // We do not want to switch back to userspace *before* the timer
255        // fires. As such, when userspace gives us reference and ticks values
256        // with a precision unrepresentible using our Ticks object, we round
257        // `reference` down, and `dt` up (ensuring that the timer cannot fire
258        // earlier than requested).
259        let dt_unshifted = if let Some(reference_u32) = reference_u32 {
260            // Computing unshifted dt for a userspace alarm can
261            // underestimate dt in some cases where both reference and
262            // dt had low-order bits that are rounded off by
263            // unshifting. To ensure `dt` results in an actual
264            // expiration that is at least as long as the expected
265            // expiration in user space, compute unshifted dt from an
266            // unshifted expiration.
267            let expiration_shifted = reference_u32.wrapping_add(dt_u32);
268            let expiration_unshifted =
269                if expiration_shifted & ((1 << A::Ticks::u32_padding()) - 1) != 0 {
270                    // By right-shifting, we would decrease the requested dt value,
271                    // firing _before_ the time requested by userspace. Add one to
272                    // compensate this:
273                    (expiration_shifted >> A::Ticks::u32_padding()) + 1
274                } else {
275                    expiration_shifted >> A::Ticks::u32_padding()
276                };
277
278            expiration_unshifted.wrapping_sub(reference_u32 >> A::Ticks::u32_padding())
279        } else if dt_u32 & ((1 << A::Ticks::u32_padding()) - 1) != 0 {
280            // By right-shifting, we would decrease the requested dt value,
281            // firing _before_ the time requested by userspace. Add one to
282            // compensate this:
283            (dt_u32 >> A::Ticks::u32_padding()) + 1
284        } else {
285            // dt does not need to be shifted *or* contains no lower bits
286            // unrepresentable in the kernel:
287            dt_u32 >> A::Ticks::u32_padding()
288        };
289
290        // For timers less than 32-bit wide, we do not have to handle a
291        // `reference + dt` overflow specially. This is because those timers are
292        // conveyed to us left-justified, and as such userspace would already
293        // have to take care of such overflow.
294        //
295        // However, we *may* need to handle overflow when the timer is *wider*
296        // than 32 bit. In this case, if `reference + dt` were to overflow, we
297        // need to rebase our reference on the full-width `now` time.
298        //
299        // If userspace didn't give us a reference, we can skip all of this and
300        // simply set the unshifted dt.
301        let new_exp = match (reference_unshifted, A::Ticks::width() > 32) {
302            (Some(userspace_reference_unshifted), true) => {
303                // We have a userspace reference and timer is wider than 32 bit.
304                //
305                // In this case, we need to check whether the lower 32 bits of the
306                // timer `reference` have already wrapped, compared to the reference
307                // provided by userspace:
308                if now.into_u32() < userspace_reference_unshifted {
309                    // The lower 32-bit of reference are smaller than the userspace
310                    // reference. This means that the full-width timer has had an
311                    // increment in the upper bits. We thus set the full-width
312                    // reference to the combination of the current upper timer bits
313                    // *minus 1*, concatenated to the user-space provided bits.
314                    //
315                    // Because we don't know the integer type of the Ticks object
316                    // (just that it's larger than a u32), we:
317                    //
318                    // 1. subtract a full `u32::MAX + 1` to incur a downward wrap,
319                    //    effectively subtracting `1` from the upper part,
320                    // 2. subtract the lower `u32` bits from this value, setting
321                    //    those bits to zero,
322                    // 3. adding back the userspace-provided reference.
323
324                    // Build 1 << 32:
325                    let bit33 = A::Ticks::from(0xffffffff).wrapping_add(A::Ticks::from(0x1));
326
327                    // Perform step 1, subtracting 1 << 32:
328                    let sub_1_upper = now.wrapping_sub(bit33);
329
330                    // Perform step 2, setting first 32 bit to zero:
331                    let sub_lower =
332                        sub_1_upper.wrapping_sub(A::Ticks::from(sub_1_upper.into_u32()));
333
334                    // Perform step 3, add back the userspace-provided reference:
335                    let rebased_reference =
336                        sub_lower.wrapping_add(A::Ticks::from(userspace_reference_unshifted));
337
338                    // Finally, return the new expiration. We don't have to do
339                    // anything special for `dt`, as it's relative:
340                    Expiration {
341                        reference: rebased_reference,
342                        dt: A::Ticks::from(dt_unshifted),
343                    }
344                } else {
345                    // The lower 32-bit of reference are equal to or larger than the
346                    // userspace reference. Thus we can rebase the reference,
347                    // touching only the lower 32 bit, by:
348                    //
349                    // 1. subtract the lower `u32` bits from this value, setting
350                    //    those bits to zero,
351                    // 2. adding back the userspace-provided reference.
352
353                    // Perform step 1, setting first 32 bit to zero:
354                    let sub_lower = now.wrapping_sub(A::Ticks::from(now.into_u32()));
355
356                    // Perform step 2, add back the userspace-provided reference:
357                    let rebased_reference =
358                        sub_lower.wrapping_add(A::Ticks::from(userspace_reference_unshifted));
359
360                    // Finally, return the new expiration. We don't have to do
361                    // anything special for `dt`, as it's relative:
362                    Expiration {
363                        reference: rebased_reference,
364                        dt: A::Ticks::from(dt_unshifted),
365                    }
366                }
367            }
368
369            (Some(userspace_reference_unshifted), false) => {
370                // We have a userspace reference and timer is (less than) 32
371                // bit. Simply set to unshifted values:
372
373                Expiration {
374                    reference: A::Ticks::from(userspace_reference_unshifted),
375                    dt: A::Ticks::from(dt_unshifted),
376                }
377            }
378
379            (None, _) => {
380                // We have no userspace reference. Use `now` as a reference:
381                Expiration {
382                    reference: now,
383                    dt: A::Ticks::from(dt_unshifted),
384                }
385            }
386        };
387
388        // Store the new expiration. We already adjusted the armed count above:
389        *expiration = Some(new_exp);
390
391        // Return the time left-justified time at which the alarm will fire:
392        new_exp
393            .reference
394            .wrapping_add(new_exp.dt)
395            .into_u32_left_justified()
396    }
397}
398
399impl<'a, A: Alarm<'a>> SyscallDriver for AlarmDriver<'a, A> {
400    /// Setup and read the alarm.
401    ///
402    /// ### `command_num`
403    ///
404    /// - `0`: Driver existence check.
405    /// - `1`: Return the clock frequency in Hz.
406    /// - `2`: Read the current clock value
407    /// - `3`: Stop the alarm if it is outstanding
408    /// - `4`: Deprecated
409    /// - `5`: Set an alarm to fire at a given clock value `time` relative to `now`
410    /// - `6`: Set an alarm to fire at a given clock value `time` relative to a
411    ///   provided reference point.
412    fn command(
413        &self,
414        cmd_type: usize,
415        data: usize,
416        data2: usize,
417        caller_id: ProcessId,
418    ) -> CommandReturn {
419        // Returns the error code to return to the user and whether we need to
420        // reset which is the next active alarm. We _don't_ reset if
421        //   - we're disabling the underlying alarm anyway,
422        //   - the underlying alarm is currently disabled and we're enabling the first alarm, or
423        //   - on an error (i.e. no change to the alarms).
424        self.app_alarms
425            .enter(caller_id, |td, _upcalls| {
426                let now = self.alarm.now();
427
428                match cmd_type {
429                    // Driver check:
430                    //
431                    // Don't re-arm the timer:
432                    0 => (CommandReturn::success(), false),
433
434                    1 => {
435                        // Get clock frequency. We return a frequency scaled by
436                        // the amount of padding we add to the `ticks` value
437                        // returned in command 2 ("capture time"), such that
438                        // userspace knows when the timer will wrap and can
439                        // accurately determine the duration of a single tick.
440                        //
441                        // Don't re-arm the timer:
442                        let scaled_freq =
443                            <A::Ticks>::u32_left_justified_scale_freq::<A::Frequency>();
444                        (CommandReturn::success_u32(scaled_freq), false)
445                    }
446                    2 => {
447                        // Capture time. We pad the underlying timer's ticks to
448                        // wrap at exactly `(2 ** 32) - 1`. This predictable
449                        // wrapping value allows userspace to build long running
450                        // timers beyond `2 ** now.width()` ticks.
451                        //
452                        // Don't re-arm the timer:
453                        (
454                            CommandReturn::success_u32(now.into_u32_left_justified()),
455                            false,
456                        )
457                    }
458                    3 => {
459                        // Stop
460                        match td.expiration {
461                            None => {
462                                // Request to stop when already stopped. Don't
463                                // re-arm the timer:
464                                (CommandReturn::failure(ErrorCode::ALREADY), false)
465                            }
466                            Some(_old_expiraton) => {
467                                // Clear the expiration:
468                                td.expiration = None;
469
470                                // Ask for the timer to be re-armed. We can't do
471                                // this here, as it would re-enter the grant
472                                // region:
473                                (CommandReturn::success(), true)
474                            }
475                        }
476                    }
477                    4 => {
478                        // Deprecated in 2.0, used to be: set absolute expiration
479                        //
480                        // Don't re-arm the timer:
481                        (CommandReturn::failure(ErrorCode::NOSUPPORT), false)
482                    }
483                    5 => {
484                        // Set relative expiration.
485                        //
486                        // We provided userspace a potentially padded version of
487                        // our in-kernel Ticks object, and as such we have to
488                        // invert that operation through a right shift.
489                        //
490                        // Also, we need to keep track of the currently armed
491                        // timers.
492                        //
493                        // All of this is done in the following helper method:
494                        let new_exp_left_justified = Self::rearm_u32_left_justified_expiration(
495                            // Current time:
496                            now,
497                            // No userspace-provided reference:
498                            None,
499                            // Left-justified `dt` value:
500                            data as u32,
501                            // Reference to the `Option<Expiration>`, also used
502                            // to update the counter of armed alarms:
503                            &mut td.expiration,
504                        );
505
506                        // Report success, with the left-justified time at which
507                        // the alarm will fire. Also ask for the timer to be
508                        // re-armed. We can't do this here, as it would re-enter
509                        // the grant region:
510                        (CommandReturn::success_u32(new_exp_left_justified), true)
511                    }
512                    6 => {
513                        // Also, we need to keep track of the currently armed
514                        // timers.
515                        //
516                        // All of this is done in the following helper method:
517                        let new_exp_left_justified = Self::rearm_u32_left_justified_expiration(
518                            // Current time:
519                            now,
520                            // Left-justified userspace-provided reference:
521                            Some(data as u32),
522                            // Left-justified `dt` value:
523                            data2 as u32,
524                            // Reference to the `Option<Expiration>`, also used
525                            // to update the counter of armed alarms:
526                            &mut td.expiration,
527                        );
528
529                        // Report success, with the left-justified time at which
530                        // the alarm will fire. Also ask for the timer to be
531                        // re-armed. We can't do this here, as it would re-enter
532                        // the grant region:
533                        (CommandReturn::success_u32(new_exp_left_justified), true)
534                    }
535
536                    // Unknown command:
537                    //
538                    // Don't re-arm the timer:
539                    _ => (CommandReturn::failure(ErrorCode::NOSUPPORT), false),
540                }
541            })
542            .map_or_else(
543                |err| CommandReturn::failure(err.into()),
544                |(retval, rearm_timer)| {
545                    if rearm_timer {
546                        self.process_rearm_or_callback();
547                    }
548                    retval
549                },
550            )
551    }
552
553    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
554        self.app_alarms.enter(processid, |_, _| {})
555    }
556}
557
558impl<'a, A: Alarm<'a>> time::AlarmClient for AlarmDriver<'a, A> {
559    fn alarm(&self) {
560        self.process_rearm_or_callback();
561    }
562}
563
564#[cfg(test)]
565mod test {
566    use core::cell::Cell;
567    use core::marker::PhantomData;
568
569    use kernel::hil::time::{
570        Alarm, AlarmClient, Freq10MHz, Frequency, Ticks, Ticks24, Ticks32, Ticks64, Time,
571    };
572    use kernel::utilities::cells::OptionalCell;
573    use kernel::ErrorCode;
574
575    use super::{AlarmDriver, Expiration};
576
577    struct MockAlarm<'a, T: Ticks, F: Frequency> {
578        current_ticks: Cell<T>,
579        client: OptionalCell<&'a dyn AlarmClient>,
580        _frequency: PhantomData<F>,
581    }
582
583    impl<T: Ticks, F: Frequency> Time for MockAlarm<'_, T, F> {
584        type Frequency = F;
585        type Ticks = T;
586
587        fn now(&self) -> Self::Ticks {
588            self.current_ticks.get()
589        }
590    }
591
592    impl<'a, T: Ticks, F: Frequency> Alarm<'a> for MockAlarm<'a, T, F> {
593        fn set_alarm_client(&self, client: &'a dyn AlarmClient) {
594            self.client.set(client);
595        }
596
597        fn set_alarm(&self, _reference: Self::Ticks, _dt: Self::Ticks) {
598            unimplemented!()
599        }
600
601        fn get_alarm(&self) -> Self::Ticks {
602            unimplemented!()
603        }
604
605        fn disarm(&self) -> Result<(), ErrorCode> {
606            unimplemented!()
607        }
608
609        fn is_armed(&self) -> bool {
610            unimplemented!()
611        }
612
613        fn minimum_dt(&self) -> Self::Ticks {
614            unimplemented!()
615        }
616    }
617
618    #[test]
619    fn test_earliest_alarm_no_alarms() {
620        assert!(
621            AlarmDriver::<MockAlarm<Ticks32, Freq10MHz>>::earliest_alarm(
622                // Now:
623                Ticks32::from(42_u32),
624                // Expirations:
625                <[(
626                    Expiration<kernel::hil::time::Ticks32>,
627                    (),
628                    fn(_, &()) -> Option<()>
629                ); 0] as IntoIterator>::into_iter([])
630            )
631            .unwrap()
632            .is_none()
633        )
634    }
635
636    #[test]
637    fn test_earliest_alarm_multiple_unexpired() {
638        // Should never be called:
639        let exp_handler = |exp, id: &usize| -> Option<()> {
640            panic!("Alarm should not be expired: {:?}, id: {}", exp, id)
641        };
642
643        let (earliest, id) = AlarmDriver::<MockAlarm<Ticks32, Freq10MHz>>::earliest_alarm(
644            // Now:
645            42_u32.into(),
646            // Expirations:
647            [
648                (
649                    // Will expire at 52:
650                    Expiration {
651                        reference: 42_u32.into(),
652                        dt: 10_u32.into(),
653                    },
654                    0,
655                    exp_handler,
656                ),
657                (
658                    // Will expire at exactly 43:
659                    Expiration {
660                        reference: u32::MAX.into(),
661                        dt: 44_u32.into(),
662                    },
663                    1,
664                    exp_handler,
665                ),
666                (
667                    // Will expire at 44:
668                    Expiration {
669                        reference: 10_u32.into(),
670                        dt: 34_u32.into(),
671                    },
672                    2,
673                    exp_handler,
674                ),
675            ]
676            .into_iter(),
677        )
678        .unwrap()
679        .unwrap();
680
681        assert_eq!(id, 1);
682        assert_eq!(earliest.reference.into_u32(), u32::MAX);
683        assert_eq!(earliest.dt.into_u32(), 44);
684    }
685
686    #[test]
687    fn test_earliest_alarm_multiple_expired() {
688        let exp_list: [Cell<bool>; 7] = Default::default();
689
690        let exp_handler = |_exp, id: &usize| -> Option<()> {
691            exp_list[*id].set(true);
692
693            // Don't stop iterating on the first expired alarm:
694            None
695        };
696
697        let (earliest, id) = AlarmDriver::<MockAlarm<Ticks32, Freq10MHz>>::earliest_alarm(
698            // Now:
699            42_u32.into(),
700            // Expirations:
701            [
702                (
703                    // Has expired at 42 (current cycle), should fire!
704                    Expiration {
705                        reference: 41_u32.into(),
706                        dt: 1_u32.into(),
707                    },
708                    0,
709                    &exp_handler,
710                ),
711                (
712                    // Will expire at 52, should not fire.
713                    Expiration {
714                        reference: 42_u32.into(),
715                        dt: 10_u32.into(),
716                    },
717                    1,
718                    &exp_handler,
719                ),
720                (
721                    // Will expire at exactly 43, should not fire.
722                    Expiration {
723                        reference: u32::MAX.into(),
724                        dt: 44_u32.into(),
725                    },
726                    2,
727                    &exp_handler,
728                ),
729                (
730                    // Reference is current time, expiration in the future,
731                    // should not fire:
732                    Expiration {
733                        reference: 42_u32.into(),
734                        dt: 1_u32.into(),
735                    },
736                    3,
737                    &exp_handler,
738                ),
739                (
740                    // Reference is 43 (current time + 1), interpreted as "in
741                    // the past", should fire:
742                    Expiration {
743                        reference: 43_u32.into(),
744                        dt: 1_u32.into(),
745                    },
746                    4,
747                    &exp_handler,
748                ),
749                (
750                    // Reference is 0, end is at 1, in the past, should fire:
751                    Expiration {
752                        reference: 0_u32.into(),
753                        dt: 1_u32.into(),
754                    },
755                    5,
756                    &exp_handler,
757                ),
758                (
759                    // Reference is u32::MAX, end is at 0, in the past, should fire:
760                    Expiration {
761                        reference: u32::MAX.into(),
762                        dt: 1_u32.into(),
763                    },
764                    6,
765                    &exp_handler,
766                ),
767            ]
768            .into_iter(),
769        )
770        .unwrap()
771        .unwrap();
772
773        // A compliant implementation could also return ID 3, which expires at
774        // the same time as the `Expiration` with ID 2.
775        assert_eq!(id, 2);
776        assert_eq!(earliest.reference.into_u32(), u32::MAX);
777        assert_eq!(earliest.dt.into_u32(), 44);
778
779        let mut bool_exp_list: [bool; 7] = [false; 7];
780        exp_list
781            .into_iter()
782            .zip(bool_exp_list.iter_mut())
783            .for_each(|(src, dst)| *dst = src.get());
784
785        assert_eq!(bool_exp_list, [true, false, false, false, true, true, true]);
786    }
787
788    #[test]
789    fn test_earliest_alarm_expired_stop() {
790        let exp_list: [Cell<bool>; 4] = Default::default();
791
792        let exp_handler = |_exp, id: &usize| -> Option<&'static str> {
793            exp_list[*id].set(true);
794
795            // Stop iterating on id == 3
796            if *id == 3 {
797                Some("stopped")
798            } else {
799                None
800            }
801        };
802
803        let (expired, id, expired_ret) =
804            AlarmDriver::<MockAlarm<Ticks32, Freq10MHz>>::earliest_alarm(
805                // Now:
806                42_u32.into(),
807                // Expirations:
808                [
809                    (
810                        // Will expire at 52, should not fire.
811                        Expiration {
812                            reference: 42_u32.into(),
813                            dt: 10_u32.into(),
814                        },
815                        0,
816                        &exp_handler,
817                    ),
818                    (
819                        // Has expired at 42 (current cycle), should fire!
820                        Expiration {
821                            reference: 41_u32.into(),
822                            dt: 1_u32.into(),
823                        },
824                        1,
825                        &exp_handler,
826                    ),
827                    (
828                        // Will expire at exactly 43, should not fire.
829                        Expiration {
830                            reference: u32::MAX.into(),
831                            dt: 44_u32.into(),
832                        },
833                        2,
834                        &exp_handler,
835                    ),
836                    (
837                        // Reference is 0, end is at 1, in the past, should fire:
838                        Expiration {
839                            reference: 0_u32.into(),
840                            dt: 1_u32.into(),
841                        },
842                        3,
843                        &exp_handler,
844                    ),
845                ]
846                .into_iter(),
847            )
848            .err()
849            .unwrap();
850
851        assert_eq!(id, 3);
852        assert_eq!(expired.reference.into_u32(), 0);
853        assert_eq!(expired.dt.into_u32(), 1);
854        assert_eq!(expired_ret, "stopped");
855
856        let mut bool_exp_list: [bool; 4] = [false; 4];
857        exp_list
858            .into_iter()
859            .zip(bool_exp_list.iter_mut())
860            .for_each(|(src, dst)| *dst = src.get());
861
862        assert_eq!(bool_exp_list, [false, true, false, true,]);
863    }
864
865    #[test]
866    fn test_rearm_24bit_left_justified_noref_basic() {
867        let mut expiration = None;
868
869        assert_eq!(Ticks24::u32_padding(), 8);
870
871        let armed_time =
872            AlarmDriver::<MockAlarm<Ticks24, Freq10MHz>>::rearm_u32_left_justified_expiration(
873                // Current time:
874                Ticks24::from(1337_u32),
875                // No userspace-provided reference:
876                None,
877                // Left-justified `dt` value:
878                1234_u32 << Ticks24::u32_padding(),
879                // Reference to the `Option<Expiration>`, also used
880                // to update the counter of armed alarms:
881                &mut expiration,
882            );
883
884        let expiration = expiration.unwrap();
885
886        assert_eq!(armed_time, (1337 + 1234) << Ticks24::u32_padding());
887        assert_eq!(expiration.reference.into_u32(), 1337);
888        assert_eq!(expiration.dt.into_u32(), 1234);
889    }
890
891    #[test]
892    fn test_rearm_24bit_left_justified_noref_wrapping() {
893        let mut expiration = None;
894
895        let armed_time =
896            AlarmDriver::<MockAlarm<Ticks24, Freq10MHz>>::rearm_u32_left_justified_expiration(
897                // Current time:
898                Ticks24::from(1337_u32),
899                // No userspace-provided reference:
900                None,
901                // Left-justified `dt` value (in this case, with some
902                // irrepresentable precision)
903                u32::MAX - (42 << Ticks24::u32_padding()),
904                // Reference to the `Option<Expiration>`, also used
905                // to update the counter of armed alarms:
906                &mut expiration,
907            );
908
909        let expiration = expiration.unwrap();
910
911        // (1337 + ((0xffffffff - (42 << 8)) >> 8) + 1) % 0x01000000 = 1295
912        assert_eq!(armed_time, 1295 << Ticks24::u32_padding());
913        assert_eq!(expiration.reference.into_u32(), 1337);
914        assert_eq!(
915            expiration.dt.into_u32(),
916            // dt is rounded up to the next representable tick:
917            ((u32::MAX - (42 << Ticks24::u32_padding())) >> Ticks24::u32_padding()) + 1
918        );
919    }
920
921    #[test]
922    fn test_rearm_24bit_left_justified_ref_low_bits_basic() {
923        let mut expiration = None;
924
925        assert_eq!(Ticks24::u32_padding(), 8);
926
927        let armed_time =
928            AlarmDriver::<MockAlarm<Ticks24, Freq10MHz>>::rearm_u32_left_justified_expiration(
929                // Current time:
930                Ticks24::from(0_u32),
931                // Userspace-provided reference, below minimum precision of Ticks24, will be rounded down:
932                Some(1_u32),
933                // Left-justified `dt` value, below minimum precision of Ticks24, will be rounded up:
934                3_u32,
935                // Reference to the `Option<Expiration>`, also used
936                // to update the counter of armed alarms:
937                &mut expiration,
938            );
939
940        let expiration = expiration.unwrap();
941
942        // ((1 >> 8) + ((3 >> 8) + 1)  << 8) = 1
943        assert_eq!(armed_time, 1 << 8);
944        assert_eq!(expiration.reference.into_u32(), 0);
945        assert_eq!(expiration.dt.into_u32(), 1);
946    }
947
948    #[test]
949    fn test_rearm_24bit_left_justified_ref_low_bits_max_int() {
950        let mut expiration = None;
951
952        assert_eq!(Ticks24::u32_padding(), 8);
953
954        let armed_time =
955            AlarmDriver::<MockAlarm<Ticks24, Freq10MHz>>::rearm_u32_left_justified_expiration(
956                // Current time:
957                Ticks24::from(6_u32),
958                // Userspace-provided reference, including bits not representable in Ticks24:
959                // (5 << 8) - 43 = 1237
960                Some(Ticks24::from(5_u32).into_u32_left_justified() - 43),
961                // Left-justified `dt` value, including bits not representable in Ticks24:
962                // (2 << 8) - 43 = 469
963                Ticks24::from(2_u32).into_u32_left_justified() + 43,
964                // Reference to the `Option<Expiration>`, also used
965                // to update the counter of armed alarms:
966                &mut expiration,
967            );
968
969        let expiration = expiration.unwrap();
970
971        // When we naively round down reference `(1237 / 256 ~= 4.83 -> 4)` and
972        // round up dt `(469 / 256 ~= 1.83 -> 2)` we'd arm the alarm to
973        // `2 + 4 = 6`. However, when considering the full resolution
974        // `reference + dt` `(1237 + 256) / 256 ~= 6.67` we can see that arming
975        // to `6` will have the alarm fire too early. The alarm rearm code needs
976        // to compensate for the case that (reference + dt) overflow and generate
977        // a dt from that rounded value, in this case `7`.
978        assert_eq!(armed_time, 7 << 8);
979        assert_eq!(expiration.reference.into_u32(), 4);
980        assert_eq!(expiration.dt, Ticks24::from(3));
981    }
982
983    #[test]
984    fn test_rearm_32bit_left_justified_noref_basic() {
985        let mut expiration = Some(Expiration {
986            reference: 0_u32.into(),
987            dt: 1_u32.into(),
988        });
989
990        assert_eq!(Ticks32::u32_padding(), 0);
991
992        let armed_time =
993            AlarmDriver::<MockAlarm<Ticks32, Freq10MHz>>::rearm_u32_left_justified_expiration(
994                // Current time:
995                Ticks32::from(1337_u32),
996                // No userspace-provided reference:
997                None,
998                // Left-justified `dt` value, unshifted for 32 bit:
999                1234_u32,
1000                // Reference to the `Option<Expiration>`, also used
1001                // to update the counter of armed alarms:
1002                &mut expiration,
1003            );
1004
1005        let expiration = expiration.unwrap();
1006
1007        assert_eq!(armed_time, 1337 + 1234);
1008        assert_eq!(expiration.reference.into_u32(), 1337);
1009        assert_eq!(expiration.dt.into_u32(), 1234);
1010    }
1011
1012    #[test]
1013    fn test_rearm_32bit_left_justified_noref_wrapping() {
1014        let mut expiration = None;
1015
1016        let armed_time =
1017            AlarmDriver::<MockAlarm<Ticks32, Freq10MHz>>::rearm_u32_left_justified_expiration(
1018                // Current time:
1019                Ticks32::from(1337_u32),
1020                // No userspace-provided reference:
1021                None,
1022                // Left-justified `dt` value (in this case, with some
1023                // irrepresentable precision)
1024                u32::MAX - 42,
1025                // Reference to the `Option<Expiration>`, also used
1026                // to update the counter of armed alarms:
1027                &mut expiration,
1028            );
1029
1030        let expiration = expiration.unwrap();
1031
1032        // (1337 + (0xffffffff - 42)) % 0x100000000 = 1294
1033        assert_eq!(armed_time, 1294);
1034        assert_eq!(expiration.reference.into_u32(), 1337);
1035        assert_eq!(expiration.dt.into_u32(), u32::MAX - 42);
1036    }
1037
1038    #[test]
1039    fn test_rearm_64bit_left_justified_noref_wrapping() {
1040        let mut expiration = Some(Expiration {
1041            reference: 0_u32.into(),
1042            dt: 1_u32.into(),
1043        });
1044
1045        assert_eq!(Ticks64::u32_padding(), 0);
1046
1047        let armed_time =
1048            AlarmDriver::<MockAlarm<Ticks64, Freq10MHz>>::rearm_u32_left_justified_expiration(
1049                // Current time:
1050                Ticks64::from(0xDEADBEEFCAFE_u64),
1051                // No userspace-provided reference:
1052                None,
1053                // Left-justified `dt` value, unshifted for 32 bit:
1054                0xDEADC0DE_u32,
1055                // Reference to the `Option<Expiration>`, also used
1056                // to update the counter of armed alarms:
1057                &mut expiration,
1058            );
1059
1060        let expiration = expiration.unwrap();
1061
1062        assert_eq!(armed_time, 0x9D9D8BDC_u32);
1063        assert_eq!(expiration.reference.into_u64(), 0xDEADBEEFCAFE_u64);
1064        assert_eq!(expiration.dt.into_u64(), 0xDEADC0DE_u64);
1065    }
1066
1067    #[test]
1068    fn test_rearm_64bit_left_justified_refnowrap_dtnorwap() {
1069        let mut expiration = None;
1070
1071        // reference smaller than now & 0xffffffff, reference + dt don't wrap:
1072        let armed_time =
1073            AlarmDriver::<MockAlarm<Ticks64, Freq10MHz>>::rearm_u32_left_justified_expiration(
1074                // Current time:
1075                Ticks64::from(0xDEADBEEFCAFE_u64),
1076                // Userspace-provided reference, smaller than now and dt
1077                Some(0xBEEFC0DE_u32),
1078                // Left-justified `dt` value, unshifted for 32 bit:
1079                0x1BADB002_u32,
1080                // Reference to the `Option<Expiration>`, also used
1081                // to update the counter of armed alarms:
1082                &mut expiration,
1083            );
1084
1085        let expiration = expiration.unwrap();
1086
1087        assert_eq!(armed_time, 0xDA9D70E0_u32); // remains at 0xDEAD
1088        assert_eq!(expiration.reference.into_u64(), 0xDEADBEEFC0DE_u64);
1089        assert_eq!(expiration.dt.into_u64(), 0x1BADB002_u64);
1090    }
1091
1092    #[test]
1093    fn test_rearm_64bit_left_justified_refnowrwap_dtwrap() {
1094        let mut expiration = Some(Expiration {
1095            reference: 0_u32.into(),
1096            dt: 1_u32.into(),
1097        });
1098
1099        // reference smaller than now & 0xffffffff, reference + dt wrap:
1100        let armed_time =
1101            AlarmDriver::<MockAlarm<Ticks64, Freq10MHz>>::rearm_u32_left_justified_expiration(
1102                // Current time:
1103                Ticks64::from(0xDEADBEEFCAFE_u64),
1104                // Userspace-provided reference, smaller than lower 32-bit of now
1105                Some(0x8BADF00D_u32),
1106                // Left-justified `dt` value, unshifted for 32 bit:
1107                0xFEEDC0DE_u32,
1108                // Reference to the `Option<Expiration>`, also used
1109                // to update the counter of armed alarms:
1110                &mut expiration,
1111            );
1112
1113        let expiration = expiration.unwrap();
1114
1115        assert_eq!(armed_time, 0x8A9BB0EB_u32); // wraps t0 0x0xDEAE
1116        assert_eq!(expiration.reference.into_u64(), 0xDEAD8BADF00D_u64);
1117        assert_eq!(expiration.dt.into_u64(), 0xFEEDC0DE_u64);
1118    }
1119
1120    #[test]
1121    fn test_rearm_64bit_left_justified_refwrap_dtwrap() {
1122        let mut expiration = None;
1123
1124        // reference larger than now & 0xffffffff, reference + dt wrap:
1125        let armed_time =
1126            AlarmDriver::<MockAlarm<Ticks64, Freq10MHz>>::rearm_u32_left_justified_expiration(
1127                // Current time:
1128                Ticks64::from(0xDEADBEEFCAFE_u64),
1129                // Userspace-provided reference, larger than lower 32-bit of
1130                // now, meaning that it's already past:
1131                Some(0xCAFEB0BA_u32),
1132                // Left-justified `dt` value, unshifted for 32 bit:
1133                0xFEEDC0DE_u32,
1134                // Reference to the `Option<Expiration>`, also used
1135                // to update the counter of armed alarms:
1136                &mut expiration,
1137            );
1138
1139        let expiration = expiration.unwrap();
1140
1141        assert_eq!(armed_time, 0xC9EC7198_u32); // wraps to 0xDEAE
1142        assert_eq!(expiration.reference.into_u64(), 0xDEACCAFEB0BA_u64);
1143        assert_eq!(expiration.dt.into_u64(), 0xFEEDC0DE_u64);
1144    }
1145
1146    #[test]
1147    fn test_rearm_64bit_left_justified_refwrap_dtnowrap() {
1148        let mut expiration = Some(Expiration {
1149            reference: 0_u32.into(),
1150            dt: 1_u32.into(),
1151        });
1152
1153        // reference larger than now & 0xffffffff, reference + dt don't wrap
1154        let armed_time =
1155            AlarmDriver::<MockAlarm<Ticks64, Freq10MHz>>::rearm_u32_left_justified_expiration(
1156                // Current time:
1157                Ticks64::from(0xDEADBEEFCAFE_u64),
1158                // Userspace-provided reference, larger than lower 32-bit of now
1159                Some(0xCAFEB0BA_u32),
1160                // Left-justified `dt` value, unshifted for 32 bit:
1161                0x1BADB002_u32,
1162                // Reference to the `Option<Expiration>`, also used
1163                // to update the counter of armed alarms:
1164                &mut expiration,
1165            );
1166
1167        let expiration = expiration.unwrap();
1168
1169        assert_eq!(armed_time, 0xE6AC60BC_u32); // remains at 0xDEAD
1170        assert_eq!(expiration.reference.into_u64(), 0xDEACCAFEB0BA_u64);
1171        assert_eq!(expiration.dt.into_u64(), 0x1BADB002_u64);
1172    }
1173}