capsules_extra/ieee802154/
virtual_mac.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Virtual IEEE 802.15.4 MAC device
6//!
7//! `MuxMac` provides multiplexed access to an 802.15.4 MAC device. This enables
8//! a single underlying 802.15.4 radio to be shared transparently by multiple
9//! users. For example, the kernel might want to send raw 802.15.4 frames and
10//! subsequently 6LoWPAN-encoded and fragmented IP packets. This capsule allows
11//! that to happen by providing a mechanism for sequencing transmission attempts,
12//! Every radio frame received is provided to all listening clients so that each
13//! client can perform its own frame filtering logic.
14//!
15//! Usage
16//! -----
17//!
18//! ```rust,ignore
19//! # use kernel::static_init;
20//!
21//! // Create the mux.
22//! let mux_mac = static_init!(
23//!     capsules::ieee802154::virtual_mac::MuxMac<'static>,
24//!     capsules::ieee802154::virtual_mac::MuxMac::new(&'static mac_device));
25//! mac_device.set_transmit_client(mux_mac);
26//! mac_device.set_receive_client(mux_mac);
27//!
28//! // Everything that uses the virtualized MAC device must create one of these.
29//! let virtual_mac = static_init!(
30//!     capsules::ieee802154::virtual_mac::MacUser<'static>,
31//!     capsules::ieee802154::virtual_mac::MacUser::new(mux_mac));
32//! mux_mac.add_user(virtual_mac);
33//! ```
34
35use crate::ieee802154::{device, framer};
36use crate::net::ieee802154::{Header, KeyId, MacAddress, PanID, SecurityLevel};
37
38use kernel::collections::list::{List, ListLink, ListNode};
39use kernel::utilities::cells::{MapCell, OptionalCell};
40use kernel::ErrorCode;
41
42/// IEE 802.15.4 MAC device muxer that keeps a list of MAC users and sequences
43/// any pending transmission requests. Any received frames from the underlying
44/// MAC device are sent to all users.
45pub struct MuxMac<'a, M: device::MacDevice<'a>> {
46    mac: &'a M,
47    users: List<'a, MacUser<'a, M>>,
48    inflight: OptionalCell<&'a MacUser<'a, M>>,
49}
50
51impl<'a, M: device::MacDevice<'a>> device::TxClient for MuxMac<'a, M> {
52    fn send_done(&self, spi_buf: &'static mut [u8], acked: bool, result: Result<(), ErrorCode>) {
53        self.inflight.take().map(move |user| {
54            user.send_done(spi_buf, acked, result);
55        });
56        self.do_next_op_async();
57    }
58}
59
60impl<'a, M: device::MacDevice<'a>> device::RxClient for MuxMac<'a, M> {
61    fn receive<'b>(
62        &self,
63        buf: &'b [u8],
64        header: Header<'b>,
65        lqi: u8,
66        data_offset: usize,
67        data_len: usize,
68    ) {
69        for user in self.users.iter() {
70            user.receive(buf, header, lqi, data_offset, data_len);
71        }
72    }
73}
74
75impl<'a, M: device::MacDevice<'a>> MuxMac<'a, M> {
76    pub const fn new(mac: &'a M) -> MuxMac<'a, M> {
77        MuxMac {
78            mac,
79            users: List::new(),
80            inflight: OptionalCell::empty(),
81        }
82    }
83
84    /// Registers a MAC user with this MAC mux device. Each MAC user should only
85    /// be registered once.
86    pub fn add_user(&self, user: &'a MacUser<'a, M>) {
87        self.users.push_head(user);
88    }
89
90    /// Gets the next `MacUser` and operation to perform if an operation is not
91    /// already underway.
92    fn get_next_op_if_idle(&self) -> Option<(&'a MacUser<'a, M>, Op)> {
93        if self.inflight.is_some() {
94            return None;
95        }
96
97        let mnode = self.users.iter().find(|node| {
98            node.operation.take().is_some_and(|op| {
99                let pending = op != Op::Idle;
100                node.operation.replace(op);
101                pending
102            })
103        });
104        mnode.and_then(|node| {
105            node.operation.take().map(|op| {
106                node.operation.replace(Op::Idle);
107                (node, op)
108            })
109        })
110    }
111
112    /// Performs a non-idle operation on a `MacUser` asynchronously: that is, if the
113    /// transmission operation results in immediate failure, then return the
114    /// buffer to the `MacUser` via its transmit client.
115    fn perform_op_async(&self, node: &'a MacUser<'a, M>, op: Op) {
116        if let Op::Transmit(frame) = op {
117            match self.mac.transmit(frame) {
118                // If Err, the transmission failed,
119                // otherwise it succeeded.
120                Ok(()) => {
121                    self.inflight.set(node);
122                }
123                Err((ecode, buf)) => {
124                    node.send_done(buf, false, Err(ecode));
125                }
126            }
127        }
128    }
129
130    /// Performs a non-idle operation on a `MacUser` synchronously, returning
131    /// the error code and the buffer immediately.
132    fn perform_op_sync(
133        &self,
134        node: &'a MacUser<'a, M>,
135        op: Op,
136    ) -> Option<Result<(), (ErrorCode, &'static mut [u8])>> {
137        if let Op::Transmit(frame) = op {
138            let result = self.mac.transmit(frame);
139            if result.is_ok() {
140                self.inflight.set(node);
141            }
142            Some(result)
143        } else {
144            None
145        }
146    }
147
148    /// Begins the next outstanding transmission if there is no ongoing
149    /// operation and there is a user waiting to transmit a frame.
150    /// Since this is being called asynchronously, return any buffers to the active
151    /// `tx_client` via the `send_done` callback in the event of failure.
152    fn do_next_op_async(&self) {
153        self.get_next_op_if_idle()
154            .map(|(node, op)| self.perform_op_async(node, op));
155    }
156
157    /// Begins the next outstanding transmission if there is no ongoing
158    /// operation and there is a user waiting to transmit a frame. Since this is
159    /// being called synchronously, there is a need to identify the MacUser that
160    /// just queued its transmission request. This can only be done by comparing
161    /// the raw pointer references of the two users, since there is no
162    /// type-level way to guarantee that the enqueued user is actually in this Mux device's
163    /// `users` list. It's safe because the raw pointer references are never
164    /// dereferenced.
165    ///
166    /// If the newly-enqueued transmission is immediately executed by this mux
167    /// device but fails immediately, return the buffer synchronously.
168    fn do_next_op_sync(
169        &self,
170        new_node: &MacUser<'a, M>,
171    ) -> Option<Result<(), (ErrorCode, &'static mut [u8])>> {
172        self.get_next_op_if_idle().and_then(|(node, op)| {
173            if core::ptr::eq(node, new_node) {
174                // The new node's operation is the one being scheduled, so the
175                // operation is synchronous
176                self.perform_op_sync(node, op)
177            } else {
178                // The operation being scheduled is not the new node, so the
179                // operation is asynchronous with respect to the new node.
180                self.perform_op_async(node, op);
181                None
182            }
183        })
184    }
185}
186
187#[derive(Eq, PartialEq, Debug)]
188enum Op {
189    Idle,
190    Transmit(framer::Frame),
191}
192
193/// Keep state for each Mac user.
194///
195/// All users of the virtualized MAC interface need to create one of
196/// these and register it with the MAC device muxer `MuxMac` by
197/// calling `MuxMac#add_user`. Then, each `MacUser` behaves exactly
198/// like an independent MAC device, except MAC device state is shared
199/// between all MacUsers because there is only one MAC device. For
200/// example, the MAC device address is shared, so calling
201/// `set_address` on one `MacUser` sets the MAC address for all
202/// `MacUser`s.
203pub struct MacUser<'a, M: device::MacDevice<'a>> {
204    mux: &'a MuxMac<'a, M>,
205    operation: MapCell<Op>,
206    next: ListLink<'a, MacUser<'a, M>>,
207    tx_client: OptionalCell<&'a dyn device::TxClient>,
208    rx_client: OptionalCell<&'a dyn device::RxClient>,
209}
210
211impl<'a, M: device::MacDevice<'a>> MacUser<'a, M> {
212    pub const fn new(mux: &'a MuxMac<'a, M>) -> Self {
213        Self {
214            mux,
215            operation: MapCell::new(Op::Idle),
216            next: ListLink::empty(),
217            tx_client: OptionalCell::empty(),
218            rx_client: OptionalCell::empty(),
219        }
220    }
221}
222
223impl<'a, M: device::MacDevice<'a>> MacUser<'a, M> {
224    fn send_done(&self, spi_buf: &'static mut [u8], acked: bool, result: Result<(), ErrorCode>) {
225        self.tx_client
226            .get()
227            .map(move |client| client.send_done(spi_buf, acked, result));
228    }
229
230    fn receive<'b>(
231        &self,
232        buf: &'b [u8],
233        header: Header<'b>,
234        lqi: u8,
235        data_offset: usize,
236        data_len: usize,
237    ) {
238        self.rx_client
239            .get()
240            .map(move |client| client.receive(buf, header, lqi, data_offset, data_len));
241    }
242}
243
244impl<'a, M: device::MacDevice<'a>> ListNode<'a, MacUser<'a, M>> for MacUser<'a, M> {
245    fn next(&'a self) -> &'a ListLink<'a, MacUser<'a, M>> {
246        &self.next
247    }
248}
249
250impl<'a, M: device::MacDevice<'a>> device::MacDevice<'a> for MacUser<'a, M> {
251    fn set_transmit_client(&self, client: &'a dyn device::TxClient) {
252        self.tx_client.set(client);
253    }
254
255    fn set_receive_client(&self, client: &'a dyn device::RxClient) {
256        self.rx_client.set(client);
257    }
258
259    fn get_address(&self) -> u16 {
260        self.mux.mac.get_address()
261    }
262
263    fn get_address_long(&self) -> [u8; 8] {
264        self.mux.mac.get_address_long()
265    }
266
267    fn get_pan(&self) -> u16 {
268        self.mux.mac.get_pan()
269    }
270
271    fn set_address(&self, addr: u16) {
272        self.mux.mac.set_address(addr)
273    }
274
275    fn set_address_long(&self, addr: [u8; 8]) {
276        self.mux.mac.set_address_long(addr)
277    }
278
279    fn set_pan(&self, id: u16) {
280        self.mux.mac.set_pan(id)
281    }
282
283    fn config_commit(&self) {
284        self.mux.mac.config_commit()
285    }
286
287    fn is_on(&self) -> bool {
288        self.mux.mac.is_on()
289    }
290
291    fn start(&self) -> Result<(), ErrorCode> {
292        self.mux.mac.start()
293    }
294
295    fn prepare_data_frame(
296        &self,
297        buf: &'static mut [u8],
298        dst_pan: PanID,
299        dst_addr: MacAddress,
300        src_pan: PanID,
301        src_addr: MacAddress,
302        security_needed: Option<(SecurityLevel, KeyId)>,
303    ) -> Result<framer::Frame, &'static mut [u8]> {
304        self.mux
305            .mac
306            .prepare_data_frame(buf, dst_pan, dst_addr, src_pan, src_addr, security_needed)
307    }
308
309    fn transmit(&self, frame: framer::Frame) -> Result<(), (ErrorCode, &'static mut [u8])> {
310        // If the muxer is idle, immediately transmit the frame, otherwise
311        // attempt to queue the transmission request. However, each MAC user can
312        // only have one pending transmission request, so if there already is a
313        // pending transmission then we must fail to entertain this one.
314        match self.operation.take() {
315            None => Err((ErrorCode::FAIL, frame.into_buf())),
316            Some(op) => match op {
317                Op::Idle => {
318                    self.operation.replace(Op::Transmit(frame));
319                    self.mux.do_next_op_sync(self).unwrap_or(Ok(()))
320                }
321                Op::Transmit(old_frame) => {
322                    self.operation.replace(Op::Transmit(old_frame));
323                    Err((ErrorCode::BUSY, frame.into_buf()))
324                }
325            },
326        }
327    }
328}