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