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}