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
//! Deferred call mechanism.
//!
//! This is a tool to allow chip peripherals to schedule "interrupts"
//! in the chip scheduler if the hardware doesn't support interrupts where
//! they are needed.

use core::cell::UnsafeCell;
use core::convert::Into;
use core::convert::TryFrom;
use core::convert::TryInto;
use core::intrinsics;
use core::marker::Copy;
use core::marker::Sync;

/// AtomicUsize with no CAS operations that works on targets that have "no atomic
/// support" according to their specification. This makes it work on thumbv6
/// platforms.
///
/// Borrowed from https://github.com/japaric/heapless/blob/master/src/ring_buffer/mod.rs
/// See: https://github.com/japaric/heapless/commit/37c8b5b63780ed8811173dc1ec8859cd99efa9ad
struct AtomicUsize {
    v: UnsafeCell<usize>,
}

impl AtomicUsize {
    pub(crate) const fn new(v: usize) -> AtomicUsize {
        AtomicUsize {
            v: UnsafeCell::new(v),
        }
    }

    pub(crate) fn load_relaxed(&self) -> usize {
        unsafe { intrinsics::atomic_load_relaxed(self.v.get()) }
    }

    pub(crate) fn store_relaxed(&self, val: usize) {
        unsafe { intrinsics::atomic_store_relaxed(self.v.get(), val) }
    }

    pub(crate) fn fetch_or_relaxed(&self, val: usize) {
        unsafe { intrinsics::atomic_store_relaxed(self.v.get(), self.load_relaxed() | val) }
    }
}

unsafe impl Sync for AtomicUsize {}

static DEFERRED_CALL: AtomicUsize = AtomicUsize::new(0);

/// Are there any pending `DeferredCall`s?
pub fn has_tasks() -> bool {
    DEFERRED_CALL.load_relaxed() != 0
}

/// Represents a way to generate an asynchronous call without a hardware
/// interrupt. Supports up to 32 possible deferrable tasks.
pub struct DeferredCall<T>(T);

impl<T: Into<usize> + TryFrom<usize> + Copy> DeferredCall<T> {
    /// Creates a new DeferredCall
    ///
    /// Only create one per task, preferably in the module that it will be used
    /// in.
    pub const unsafe fn new(task: T) -> Self {
        DeferredCall(task)
    }

    /// Set the `DeferredCall` as pending
    pub fn set(&self) {
        DEFERRED_CALL.fetch_or_relaxed(1 << self.0.into() as usize);
    }

    /// Gets and clears the next pending `DeferredCall`
    pub fn next_pending() -> Option<T> {
        let val = DEFERRED_CALL.load_relaxed();
        if val == 0 {
            None
        } else {
            let bit = val.trailing_zeros() as usize;
            let new_val = val & !(1 << bit);
            DEFERRED_CALL.store_relaxed(new_val);
            bit.try_into().ok()
        }
    }
}