esp_wifi/
preempt.rs

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
//! This module allows hooking `esp-wifi` into an external scheduler, instead
//! of using the integrated one as provided by the `preempt` module.
//!
//! In order to hook up an external scheduler, enable the `preempt-extern`
//! feature, implement the `Scheduler` trait for a struct, and create the
//! necessary `extern` functions using the `scheduler_impl!()` macro.
//!
//! Example:
//!
//! ```ignore
//! use esp_wifi::preempt::Scheduler;
//!
//! struct MyScheduler {}
//!
//! impl Scheduler for MyScheduler {
//!     // impl goes here
//! }
//!
//! esp_wifi::scheduler_impl!(static SCHEDULER: MyScheduler = MyScheduler {});
//! ```
use core::ffi::c_void;

pub trait Scheduler: Send + Sync + 'static {
    /// This function is called by `esp-wifi` when starting up the WiFi stack.
    fn enable(&self);

    /// This function is called by `esp-wifi` when shutting down the WiFi stack.
    fn disable(&self);

    /// This function is called by threads and should switch to the next thread.
    fn yield_task(&self);

    /// This function is called by threads and should return an opaque handle
    /// for the calling thread. The same handle will be passed to
    /// `esp_wifi_preempt_schedule_task_deletion`.
    fn current_task(&self) -> *mut c_void;

    /// This function is used to create threads.
    /// It should allocate the stack.
    fn task_create(
        &self,
        task: extern "C" fn(*mut c_void),
        param: *mut c_void,
        task_stack_size: usize,
    ) -> *mut c_void;

    /// This function is called to let the scheduler know this thread is not
    /// needed anymore and should be deleted. After this function is called,
    /// the thread should not be scheduled anymore. The thread stack can be
    /// free'ed.
    fn schedule_task_deletion(&self, task_handle: *mut c_void);

    /// This function should return an opaque per-thread pointer to an
    /// usize-sized memory location, which will be used to store a pointer
    /// to a semaphore for this thread.
    fn current_task_thread_semaphore(&self) -> *mut c_void;
}

extern "Rust" {
    fn esp_wifi_preempt_enable();
    fn esp_wifi_preempt_disable();
    fn esp_wifi_preempt_yield_task();
    fn esp_wifi_preempt_current_task() -> *mut c_void;
    fn esp_wifi_preempt_task_create(
        task: extern "C" fn(*mut c_void),
        param: *mut c_void,
        task_stack_size: usize,
    ) -> *mut c_void;
    fn esp_wifi_preempt_schedule_task_deletion(task_handle: *mut c_void);
    fn esp_wifi_preempt_current_task_thread_semaphore() -> *mut c_void;
}

pub(crate) fn enable() {
    unsafe { esp_wifi_preempt_enable() }
}

pub(crate) fn disable() {
    unsafe { esp_wifi_preempt_disable() }
}

pub(crate) fn yield_task() {
    unsafe { esp_wifi_preempt_yield_task() }
}

pub(crate) fn current_task() -> *mut c_void {
    unsafe { esp_wifi_preempt_current_task() }
}

pub(crate) fn task_create(
    task: extern "C" fn(*mut c_void),
    param: *mut c_void,
    task_stack_size: usize,
) -> *mut c_void {
    unsafe { esp_wifi_preempt_task_create(task, param, task_stack_size) }
}

pub(crate) fn schedule_task_deletion(task_handle: *mut c_void) {
    unsafe { esp_wifi_preempt_schedule_task_deletion(task_handle) }
}

pub(crate) fn current_task_thread_semaphore() -> *mut c_void {
    unsafe { esp_wifi_preempt_current_task_thread_semaphore() }
}

/// Set the Scheduler implementation.
///
/// See the module documentation for an example.
#[macro_export]
macro_rules! scheduler_impl {
    (static $name:ident: $t: ty = $val:expr) => {
        static $name: $t = $val;

        #[no_mangle]
        fn esp_wifi_preempt_enable() {
            <$t as $crate::preempt::Scheduler>::enable(&$name)
        }
        #[no_mangle]
        fn esp_wifi_preempt_disable() {
            <$t as $crate::preempt::Scheduler>::disable(&$name)
        }
        #[no_mangle]
        fn esp_wifi_preempt_yield_task() {
            <$t as $crate::preempt::Scheduler>::yield_task(&$name)
        }
        #[no_mangle]
        fn esp_wifi_preempt_current_task() -> *mut c_void {
            <$t as $crate::preempt::Scheduler>::current_task(&$name)
        }
        #[no_mangle]
        fn esp_wifi_preempt_task_create(
            task: extern "C" fn(*mut c_void),
            param: *mut c_void,
            task_stack_size: usize,
        ) -> *mut c_void {
            <$t as $crate::preempt::Scheduler>::task_create(&$name, task, param, task_stack_size)
        }
        #[no_mangle]
        fn esp_wifi_preempt_schedule_task_deletion(task_handle: *mut c_void) {
            <$t as $crate::preempt::Scheduler>::schedule_task_deletion(&$name, task_handle)
        }
        #[no_mangle]
        fn esp_wifi_preempt_current_task_thread_semaphore() -> *mut c_void {
            <$t as $crate::preempt::Scheduler>::current_task_thread_semaphore(&$name)
        }
    };
}