1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
//! Hardware-independent kernel interface for deferred calls
//!
//! This allows any struct in the kernel which implements
//! [DynamicDeferredCallClient](crate::dynamic_deferred_call::DynamicDeferredCallClient)
//! to set and receive deferred calls.
//!
//! These can be used to implement long-running in-kernel algorithms
//! or software devices that are supposed to work like hardware devices.
//! Essentially, this allows the chip to handle more important interrupts,
//! and lets a kernel component return the function call stack up to the scheduler,
//! automatically being called again.
//!
//! Usage
//! -----
//!
//! The `dynamic_deferred_call_clients` array size determines how many
//! [DeferredCallHandle](crate::dynamic_deferred_call::DeferredCallHandle)s
//! may be registered with the instance.
//! When no more slots are available,
//! `dynamic_deferred_call.register(some_client)` will return `None`.
//!
//! ```
//! # use core::cell::Cell;
//! # use kernel::utilities::cells::OptionalCell;
//! # use kernel::static_init;
//! use kernel::dynamic_deferred_call::{
//!     DynamicDeferredCall,
//!     DynamicDeferredCallClient,
//!     DynamicDeferredCallClientState,
//! };
//!
//! let dynamic_deferred_call_clients = unsafe { static_init!(
//!     [DynamicDeferredCallClientState; 2],
//!     Default::default()
//! ) };
//! let dynamic_deferred_call = unsafe { static_init!(
//!     DynamicDeferredCall,
//!     DynamicDeferredCall::new(dynamic_deferred_call_clients)
//! ) };
//! assert!(unsafe { DynamicDeferredCall::set_global_instance(dynamic_deferred_call) } == true);
//!
//! # struct SomeCapsule;
//! # impl SomeCapsule {
//! #     pub fn new(_ddc: &'static DynamicDeferredCall) -> Self { SomeCapsule }
//! #     pub fn set_deferred_call_handle(
//! #         &self,
//! #         _handle: kernel::dynamic_deferred_call::DeferredCallHandle,
//! #     ) { }
//! # }
//! # impl DynamicDeferredCallClient for SomeCapsule {
//! #     fn call(
//! #         &self,
//! #         _handle: kernel::dynamic_deferred_call::DeferredCallHandle,
//! #     ) { }
//! # }
//! #
//! // Here you can register custom capsules, etc.
//! // This could look like:
//! let some_capsule = unsafe { static_init!(
//!     SomeCapsule,
//!     SomeCapsule::new(dynamic_deferred_call)
//! ) };
//! some_capsule.set_deferred_call_handle(
//!     dynamic_deferred_call.register(some_capsule).unwrap() // Unwrap fail = no deferred call slot available
//! );
//! ```

use core::cell::Cell;

use crate::utilities::cells::OptionalCell;

/// Kernel-global dynamic deferred call instance
///
/// This gets called by the kernel scheduler automatically and is accessible
/// through `unsafe` static functions on the `DynamicDeferredCall` struct
static mut DYNAMIC_DEFERRED_CALL: Option<&'static DynamicDeferredCall> = None;

/// Internal per-client state tracking for the [DynamicDeferredCall]
pub struct DynamicDeferredCallClientState {
    scheduled: Cell<bool>,
    client: OptionalCell<&'static dyn DynamicDeferredCallClient>,
}
impl Default for DynamicDeferredCallClientState {
    fn default() -> DynamicDeferredCallClientState {
        DynamicDeferredCallClientState {
            scheduled: Cell::new(false),
            client: OptionalCell::empty(),
        }
    }
}

/// Dynamic deferred call
///
/// This struct manages and calls dynamically (at runtime) registered
/// deferred calls from capsules and other kernel structures.
///
/// It has a fixed number of possible clients, which
/// is determined by the `clients`-array passed in with the constructor.
pub struct DynamicDeferredCall {
    client_states: &'static [DynamicDeferredCallClientState],
    handle_counter: Cell<usize>,
    call_pending: Cell<bool>,
}

impl DynamicDeferredCall {
    /// Construct a new dynamic deferred call implementation
    ///
    /// This needs to be registered with the `set_global_instance` function immediately
    /// afterwards, and should not be changed anymore. Only the globally registered
    /// instance will receive calls from the kernel scheduler.
    ///
    /// The `clients` array can be initialized using the implementation of [Default]
    /// for the [DynamicDeferredCallClientState].
    pub fn new(client_states: &'static [DynamicDeferredCallClientState]) -> DynamicDeferredCall {
        DynamicDeferredCall {
            client_states,
            handle_counter: Cell::new(0),
            call_pending: Cell::new(false),
        }
    }

    /// Sets a global [DynamicDeferredCall] instance
    ///
    /// This is required before any deferred calls can be retrieved.
    /// It may be called only once. Returns `true` if the global instance
    /// was successfully registered.
    pub unsafe fn set_global_instance(ddc: &'static DynamicDeferredCall) -> bool {
        // If the returned reference is identical to the instance argument,
        // it is set in the option. Otherwise, a different instance is
        // already registered and will not be replaced.
        (*DYNAMIC_DEFERRED_CALL.get_or_insert(ddc)) as *const _ == ddc as *const _
    }

    /// Call the globally registered instance
    ///
    /// Returns `true` if a global instance was registered and has been called.
    pub unsafe fn call_global_instance() -> bool {
        DYNAMIC_DEFERRED_CALL.map(|ddc| ddc.call()).is_some()
    }

    /// Call the globally registered instance while the supplied predicate
    /// returns `true`.
    ///
    /// Returns `true` if a global instance was registered and has been called.
    pub unsafe fn call_global_instance_while<F: Fn() -> bool>(f: F) -> bool {
        DYNAMIC_DEFERRED_CALL
            .map(move |ddc| ddc.call_while(f))
            .is_some()
    }

    /// Check if one or more dynamic deferred calls are pending in the
    /// globally registered instance
    ///
    /// Returns `None` if no global instance has been registered, or `Some(true)`
    /// if the registered instance has one or more pending deferred calls.
    pub unsafe fn global_instance_calls_pending() -> Option<bool> {
        DYNAMIC_DEFERRED_CALL.map(|ddc| ddc.has_pending())
    }

    /// Schedule a deferred call to be called
    ///
    /// The handle addresses the client that will be called.
    ///
    /// If no client for the handle is found (it was unregistered), this
    /// returns `None`. If a call is already scheduled, it returns
    /// `Some(false)`.
    pub fn set(&self, handle: DeferredCallHandle) -> Option<bool> {
        let DeferredCallHandle(client_pos) = handle;
        let client_state = &self.client_states[client_pos];

        if let (call_set, true) = (&client_state.scheduled, client_state.client.is_some()) {
            if call_set.get() {
                // Already set
                Some(false)
            } else {
                call_set.set(true);
                self.call_pending.set(true);
                Some(true)
            }
        } else {
            None
        }
    }

    /// Register a new client
    ///
    /// On success, a `Some(handle)` will be returned. This handle is later
    /// required to schedule a deferred call.
    ///
    /// A given [`DynamicDeferredCallClient`] reference (client) can be
    /// registered multiple times and will receive a different handle each
    /// time. This mechanism is useful to distinguish between deferred calls
    /// scheduled by the same client, but to be handled differently. Each issued
    /// handle will occupy one [`DynamicDeferredCallClientState`] in the
    /// [`DynamicDeferredCall`]. Clients can utilize the passed
    /// [`DeferredCallHandle`] to distinguish between scheduled deferred calls.
    pub fn register(
        &self,
        ddc_client: &'static dyn DynamicDeferredCallClient,
    ) -> Option<DeferredCallHandle> {
        let current_counter = self.handle_counter.get();

        if current_counter < self.client_states.len() {
            let client_state = &self.client_states[current_counter];
            client_state.scheduled.set(false);
            client_state.client.set(ddc_client);

            self.handle_counter.set(current_counter + 1);

            Some(DeferredCallHandle(current_counter))
        } else {
            None
        }
    }

    /// Check if one or more deferred calls are pending
    ///
    /// Returns `true` if one or more deferred calls are pending.
    pub fn has_pending(&self) -> bool {
        self.call_pending.get()
    }

    /// Call all registered and to-be-scheduled deferred calls
    ///
    /// It may be called without holding the `DynamicDeferredCall` reference through
    /// `call_global_instance`.
    pub(self) fn call(&self) {
        self.call_while(|| true)
    }

    /// Call all registered and to-be-scheduled deferred calls while the supplied
    /// predicate returns `true`.
    ///
    /// It may be called without holding the `DynamicDeferredCall` reference through
    /// `call_global_instance_while`.
    pub(self) fn call_while<F: Fn() -> bool>(&self, f: F) {
        if self.call_pending.get() {
            for (i, client_state) in self.client_states.iter().enumerate() {
                if !f() {
                    break;
                }
                if client_state.scheduled.get() {
                    client_state.client.map(|client| {
                        client_state.scheduled.set(false);
                        client.call(DeferredCallHandle(i));
                    });
                }
            }

            // Recompute call_pending here, as some deferred calls may have been skipped due to the
            // `f` predicate becoming false.
            self.call_pending.set(
                self.client_states
                    .iter()
                    .any(|client_state| client_state.scheduled.get()),
            );
        }
    }
}

/// Client for the
/// [DynamicDeferredCall](crate::dynamic_deferred_call::DynamicDeferredCall)
///
/// This trait needs to be implemented for some struct to receive
/// deferred calls from a `DynamicDeferredCall`.
pub trait DynamicDeferredCallClient {
    fn call(&self, handle: DeferredCallHandle);
}

/// Unique identifier for a deferred call registered with a
/// [DynamicDeferredCall](crate::dynamic_deferred_call::DynamicDeferredCall)
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct DeferredCallHandle(usize);