1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use super::*;
6use crate::Platform;
7use fuchsia_async as fasync;
8//use futures::channel::mpsc as fmpsc;
9use std::task::{Context, Poll};
1011// Number of entries in the timer wakeup buffer.
12// This value was chosen somewhat arbitrarily, with the only
13// requirement being that it be larger than what should happen
14// during normal operation.
15const TIMER_BUFFER_LEN: usize = 20;
1617pub(crate) struct AlarmInstance {
18pub task_alarm: Cell<Option<fasync::Task<()>>>,
19pub timer_sender: fmpsc::Sender<usize>,
20}
2122impl AlarmInstance {
23pub(crate) fn new() -> (AlarmInstance, fmpsc::Receiver<usize>) {
24let (timer_sender, timer_receiver) = fmpsc::channel(TIMER_BUFFER_LEN);
2526 (AlarmInstance { task_alarm: Cell::new(None), timer_sender }, timer_receiver)
27 }
2829fn on_alarm_milli_get_now(&self) -> u32 {
30#[no_mangle]
31unsafe extern "C" fn otPlatAlarmMilliGetNow() -> u32 {
32// SAFETY: Must only be called from OpenThread thread,
33PlatformBacking::as_ref().alarm.on_alarm_milli_get_now()
34 }
35 (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_millis() as u32
36 }
3738fn on_time_get(&self) -> u64 {
39#[no_mangle]
40unsafe extern "C" fn otPlatTimeGet() -> u64 {
41// SAFETY: Must only be called from OpenThread thread,
42PlatformBacking::as_ref().alarm.on_time_get()
43 }
44 (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_micros() as u64
45 }
4647fn on_alarm_milli_start_at(&self, instance: Option<&ot::Instance>, t0: u32, dt: u32) {
48#[no_mangle]
49unsafe extern "C" fn otPlatAlarmMilliStartAt(
50 instance: *mut otsys::otInstance,
51 t0: u32,
52 dt: u32,
53 ) {
54 AlarmInstance::on_alarm_milli_start_at(
55// SAFETY: Must only be called from OpenThread thread,
56&PlatformBacking::as_ref().alarm,
57// SAFETY: `instance` must be a pointer to a valid `otInstance`
58ot::Instance::ref_from_ot_ptr(instance),
59 t0,
60 dt,
61 )
62 }
6364trace!(
65 tag = "alarm";
66"on_alarm_milli_start_at: scheduling alarm for {:?}ms after {:?}",
67 dt,
68 t0
69 );
7071let ot_instance_ptrval =
72 instance.map(ot::Boxable::as_ot_ptr).map(|x| x as usize).unwrap_or(0usize);
73let mut timer_sender = self.timer_sender.clone();
7475let future = async move {
76let now_in_millis =
77 (zx::MonotonicInstant::get() - zx::MonotonicInstant::ZERO).into_millis() as u32;
78let offset = ((now_in_millis - t0) as i32).min(0) as u32;
79let duration = if offset <= dt {
80 Duration::from_millis((dt - offset) as u64)
81 } else {
82 Duration::ZERO
83 };
84trace!(
85 tag = "alarm";
86"on_alarm_milli_start_at: helper task now waiting {:?}",
87 duration
88 );
89 fasync::Timer::new(duration).await;
90trace!(tag="alarm"; "on_alarm_milli_start_at: helper task finished waiting, now sending ot_instance_ptrval");
91 timer_sender.send(ot_instance_ptrval).await.unwrap();
92 };
9394// Make and spawn a helper task that waits for the duration
95 // and then puts the pointer value into the timer sender channel.
96 // The receiver end of the channel is being serviced by Platform::process_poll,
97 // which makes sure that the timer callback gets fired on the main
98 // thread. The previous alarm task, if any, is cancelled.
99self.task_alarm.set(Some(fasync::Task::spawn(future)));
100 }
101102fn on_alarm_milli_stop(&self, _instance: Option<&ot::Instance>) {
103#[no_mangle]
104unsafe extern "C" fn otPlatAlarmMilliStop(instance: *mut otsys::otInstance) {
105 AlarmInstance::on_alarm_milli_stop(
106// SAFETY: Must only be called from OpenThread thread,
107&PlatformBacking::as_ref().alarm,
108// SAFETY: `instance` must be a pointer to a valid `otInstance`
109ot::Instance::ref_from_ot_ptr(instance),
110 )
111 }
112113if self.task_alarm.take().is_some() {
114trace!(tag = "alarm"; "on_alarm_milli_stop: Alarm cancelled");
115 }
116 }
117118fn on_alarm_fired(&self, instance: &ot::Instance, value: usize) {
119trace!(tag = "alarm"; "on_alarm_fired");
120121let instance_ptr = instance.as_ot_ptr();
122assert_eq!(instance_ptr as usize, value, "Got wrong pointer from timer receiver");
123124// SAFETY: Must be called with a valid pointer to otInstance,
125 // must also only be called from the main OpenThread thread,
126 // which is a guarantee of this method.
127unsafe {
128if otsys::otPlatDiagModeGet() {
129 otsys::otPlatDiagAlarmFired(instance_ptr);
130 }
131132 otsys::otPlatAlarmMilliFired(instance_ptr);
133 }
134 }
135}
136137impl Platform {
138pub(crate) fn process_poll_alarm(&mut self, instance: &ot::Instance, cx: &mut Context<'_>) {
139while let Poll::Ready(Some(value)) = self.timer_receiver.poll_next_unpin(cx) {
140// SAFETY: Guaranteed to only be called from the OpenThread thread.
141unsafe { PlatformBacking::as_ref() }.alarm.on_alarm_fired(instance, value);
142 }
143 }
144}