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}