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
//! Access to [messages](https://doc.riot-os.org/group__core__msg.html) by explicit type indication
//!
//! ## Safety
//!
//! This overall method of sending and receiving messages provides no guarantees that even a
//! pure-Rust thread doesn't accidentally reuse a number or does something else to misuse
//! ContainerMsg::recognize; a better interface is WIP in the [v2] module.

use crate::thread::KernelPID;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use riot_sys::libc;
use riot_sys::{self, kernel_pid_t, msg_receive, msg_reply, msg_send, msg_send_receive, msg_t};

#[cfg(feature = "with_msg_v2")]
pub mod v2;

/// The source of a message
// Ideally this would be represented in memory 1:1 like a KernelPID, but I can't tell Rust that a
// KernelPID has a valid range from KERNEL_PID_FIRST to KERNEL_PID_LAST and have it use that
// knowledge.
#[derive(Debug, PartialEq)]
pub enum MsgSender {
    Invalid, // = riot_sys::KERNEL_PID_UNDEF
    Thread(KernelPID),
    ISR, // = riot_sys::KERNEL_PID_ISR
}

impl MsgSender {
    fn from_pid(pid: kernel_pid_t) -> Self {
        if pid == crate::thread::KERNEL_PID_ISR {
            MsgSender::ISR
        } else {
            KernelPID::new(pid)
                .map(MsgSender::Thread)
                .unwrap_or(MsgSender::Invalid)
        }
    }
}

#[derive(Debug)]
pub enum MsgSendError {
    ReceiverNotWaiting,
    InvalidPID,
}

pub trait Msg {
    fn get_sender(&self) -> MsgSender;
    fn get_type(&self) -> u16;
    fn send(self, target: &KernelPID) -> Result<(), MsgSendError>;
    fn send_receive(self, target: &KernelPID) -> OpaqueMsg;
    fn reply(self, response: impl WrapsMsgT) -> Result<(), ()>;
}

/// Helper trait to implement Msg for the various Msg value styles. This is not supposed to be
/// public, but leaks due to the implementation.
pub trait WrapsMsgT {
    fn extract(self) -> msg_t;
    fn view(&self) -> &msg_t;
}

impl<T> Msg for T
where
    T: WrapsMsgT,
{
    fn send(self, target: &KernelPID) -> Result<(), MsgSendError> {
        let mut m = self.extract();
        match unsafe { msg_send(&mut m, target.into()) } {
            1 => Ok(()),
            0 => Err(MsgSendError::ReceiverNotWaiting),
            _ => Err(MsgSendError::InvalidPID),
        }
    }

    fn send_receive(self, target: &KernelPID) -> OpaqueMsg {
        let mut m = self.extract();
        let _ = unsafe { msg_send_receive(&mut m, &mut m, target.into()) };
        OpaqueMsg(m)
    }

    fn reply(self, response: impl WrapsMsgT) -> Result<(), ()> {
        let mut m = self.extract();
        let mut r = response.extract();
        let result = unsafe { msg_reply(&mut m, &mut r) };
        match result {
            1 => Ok(()),
            _ => Err(()),
        }
    }

    fn get_sender(&self) -> MsgSender {
        MsgSender::from_pid(self.view().sender_pid)
    }

    fn get_type(&self) -> u16 {
        self.view().type_
    }
}

/// An initialized message with inaccessible value.
pub struct OpaqueMsg(msg_t);

impl OpaqueMsg {
    pub fn receive() -> OpaqueMsg {
        let mut m: MaybeUninit<msg_t> = MaybeUninit::uninit();
        let _ = unsafe { msg_receive(m.as_mut_ptr()) };
        OpaqueMsg(unsafe { m.assume_init() })
    }
}

impl ::core::fmt::Debug for OpaqueMsg {
    fn fmt(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        formatter
            .debug_struct("OpaqueMsg")
            .field("from", &self.get_sender())
            .field("type", &self.get_type())
            .finish()
    }
}

impl WrapsMsgT for OpaqueMsg {
    fn extract(self) -> msg_t {
        self.0
    }
    fn view(&self) -> &msg_t {
        &self.0
    }
}

/// An initialized message of a type that needs no access to a value
pub struct EmptyMsg(msg_t);

impl EmptyMsg {
    pub fn new(type_: u16) -> Self {
        EmptyMsg(msg_t {
            type_,
            ..msg_t::default()
        })
    }
}

impl WrapsMsgT for EmptyMsg {
    fn extract(self) -> msg_t {
        self.0
    }
    fn view(&self) -> &msg_t {
        &self.0
    }
}

/// An initialized message of a type whose payload is numeric
#[derive(Debug)]
pub struct NumericMsg(msg_t);

impl NumericMsg {
    pub fn new(type_: u16, value: u32) -> Self {
        NumericMsg(msg_t {
            type_,
            content: riot_sys::msg_t__bindgen_ty_1 { value: value },
            ..msg_t::default()
        })
    }

    /// Interpret an opaque message as a numeric one. The caller needs to ensure that the message
    /// was initialized with a u32 value, typically by checking the message type (or receiving the
    /// message as a response) and relying on senders to properly initialize the message.
    pub unsafe fn recognize(msg: OpaqueMsg) -> Self {
        NumericMsg(msg.extract())
    }

    pub fn get_value(&self) -> u32 {
        unsafe { self.0.content.value }
    }
}

impl WrapsMsgT for NumericMsg {
    fn extract(self) -> msg_t {
        self.0
    }
    fn view(&self) -> &msg_t {
        &self.0
    }
}

pub struct ContainerMsg<T> {
    message: msg_t,
    t: PhantomData<T>,
}

// The tricky part will be creating a Container from an OpaqueMsg -- there we'll have to know for
// sure the type and lifetime involved.
//
// T needs to be Send. If you want to pass around raw pointers as RIOT does in GNRC, wrap them like
// Pktsnip.
impl<T> ContainerMsg<T>
where
    T: Sized + Send,
{
    pub fn new(type_: u16, value: T) -> Self {
        use core::mem::size_of;
        assert!(
            size_of::<T>() <= size_of::<*mut libc::c_void>(),
            "Type too large to send"
        );
        ContainerMsg {
            message: msg_t {
                type_,
                content: riot_sys::msg_t__bindgen_ty_1 {
                    ptr: unsafe { ::core::mem::transmute_copy(&value) },
                },
                ..msg_t::default()
            },
            t: PhantomData,
        }
    }

    pub fn get_value(self) -> T {
        unsafe { ::core::mem::transmute_copy(&self.message.content.ptr) }
    }

    pub unsafe fn recognize(msg: OpaqueMsg) -> Self {
        ContainerMsg {
            message: msg.extract(),
            t: PhantomData,
        }
    }
}