capsules_extra/ieee802154/
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//! Specifies the interface for IEEE 802.15.4 MAC protocol layers.
6//!
7//! MAC protocols expose similar configuration (address, PAN,
8//! transmission power) options as ieee802154::device::MacDevice
9//! layers above it, but retain control over radio power management
10//! and channel selection. All frame processing should be completed
11//! above this layer such that Mac implementations receive fully
12//! formatted 802.15.4 MAC frames for transmission.
13//!
14//! AwakeMac provides a default implementation of such a layer, maintaining
15//! the underlying kernel::hil::radio::Radio powered at all times and passing
16//! through each frame for transmission.
17
18use crate::net::ieee802154::{Header, MacAddress};
19use kernel::hil::radio::{self, MAX_FRAME_SIZE, PSDU_OFFSET};
20use kernel::utilities::cells::OptionalCell;
21use kernel::ErrorCode;
22
23pub trait Mac<'a> {
24    /// Initializes the layer.
25    fn initialize(&self) -> Result<(), ErrorCode>;
26
27    /// Sets the notified client for configuration changes
28    fn set_config_client(&self, client: &'a dyn radio::ConfigClient);
29    /// Sets the notified client for transmission completions
30    fn set_transmit_client(&self, client: &'a dyn radio::TxClient);
31    /// Sets the notified client for frame receptions
32    fn set_receive_client(&self, client: &'a dyn radio::RxClient);
33    /// Sets the buffer for packet reception
34    fn set_receive_buffer(&self, buffer: &'static mut [u8]);
35
36    /// The short 16-bit address of the radio
37    fn get_address(&self) -> u16;
38    /// The long 64-bit address of the radio
39    fn get_address_long(&self) -> [u8; 8];
40    /// The 16-bit PAN id of the radio
41    fn get_pan(&self) -> u16;
42
43    /// Sets the short 16-bit address of the radio
44    fn set_address(&self, addr: u16);
45    /// Sets the long 64-bit address of the radio
46    fn set_address_long(&self, addr: [u8; 8]);
47    /// Sets the 16-bit PAN id of the radio
48    fn set_pan(&self, id: u16);
49
50    /// Must be called after one or more calls to `set_*`. If
51    /// `set_*` is called without calling `config_commit`, there is no guarantee
52    /// that the underlying hardware configuration (addresses, pan ID) is in
53    /// line with this MAC protocol implementation. The specified config_client is
54    /// notified on completed reconfiguration.
55    fn config_commit(&self);
56
57    /// Indicates whether or not the MAC protocol is active and can send frames
58    fn is_on(&self) -> bool;
59
60    /// Start the radio.
61    ///
62    /// This serves as a passthrough to the underlying radio's `start` method.
63    ///
64    /// ## Return
65    ///
66    /// `Ok(())` on success. On `Err()`, valid errors are:
67    ///
68    /// - `ErrorCode::FAIL`: Internal error occurred.
69    fn start(&self) -> Result<(), ErrorCode>;
70
71    /// Transmits complete MAC frames, which must be prepared by an ieee802154::device::MacDevice
72    /// before being passed to the Mac layer. Returns the frame buffer in case of an error.
73    fn transmit(
74        &self,
75        full_mac_frame: &'static mut [u8],
76        frame_len: usize,
77    ) -> Result<(), (ErrorCode, &'static mut [u8])>;
78}
79
80///
81/// Default implementation of a Mac layer. Acts as a pass-through between a MacDevice
82/// implementation and the underlying radio::Radio device. Does not change the power
83/// state of the radio during operation.
84///
85pub struct AwakeMac<'a, R: radio::Radio<'a>> {
86    radio: &'a R,
87
88    tx_client: OptionalCell<&'a dyn radio::TxClient>,
89    rx_client: OptionalCell<&'a dyn radio::RxClient>,
90}
91
92impl<'a, R: radio::Radio<'a>> AwakeMac<'a, R> {
93    pub fn new(radio: &'a R) -> AwakeMac<'a, R> {
94        AwakeMac {
95            radio,
96            tx_client: OptionalCell::empty(),
97            rx_client: OptionalCell::empty(),
98        }
99    }
100}
101
102impl<'a, R: radio::Radio<'a>> Mac<'a> for AwakeMac<'a, R> {
103    fn initialize(&self) -> Result<(), ErrorCode> {
104        // do nothing, extra buffer unnecessary
105        Ok(())
106    }
107
108    fn is_on(&self) -> bool {
109        self.radio.is_on()
110    }
111
112    fn start(&self) -> Result<(), ErrorCode> {
113        self.radio.start()
114    }
115
116    fn set_config_client(&self, client: &'a dyn radio::ConfigClient) {
117        self.radio.set_config_client(client)
118    }
119
120    fn set_address(&self, addr: u16) {
121        self.radio.set_address(addr)
122    }
123
124    fn set_address_long(&self, addr: [u8; 8]) {
125        self.radio.set_address_long(addr)
126    }
127
128    fn set_pan(&self, id: u16) {
129        self.radio.set_pan(id)
130    }
131
132    fn get_address(&self) -> u16 {
133        self.radio.get_address()
134    }
135
136    fn get_address_long(&self) -> [u8; 8] {
137        self.radio.get_address_long()
138    }
139
140    fn get_pan(&self) -> u16 {
141        self.radio.get_pan()
142    }
143
144    fn config_commit(&self) {
145        self.radio.config_commit()
146    }
147
148    fn set_transmit_client(&self, client: &'a dyn radio::TxClient) {
149        self.tx_client.set(client);
150    }
151
152    fn set_receive_client(&self, client: &'a dyn radio::RxClient) {
153        self.rx_client.set(client);
154    }
155
156    fn set_receive_buffer(&self, buffer: &'static mut [u8]) {
157        self.radio.set_receive_buffer(buffer);
158    }
159
160    fn transmit(
161        &self,
162        full_mac_frame: &'static mut [u8],
163        frame_len: usize,
164    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
165        // We must add the PSDU_OFFSET required for the radio
166        // hardware. We first error check the provided arguments
167        // and then shift the 15.4 frame by the `PSDU_OFFSET`.
168
169        if full_mac_frame.len() < frame_len + PSDU_OFFSET {
170            return Err((ErrorCode::NOMEM, full_mac_frame));
171        }
172
173        if frame_len > MAX_FRAME_SIZE {
174            return Err((ErrorCode::INVAL, full_mac_frame));
175        }
176
177        full_mac_frame.copy_within(0..frame_len, PSDU_OFFSET);
178        self.radio.transmit(full_mac_frame, frame_len)
179    }
180}
181
182impl<'a, R: radio::Radio<'a>> radio::TxClient for AwakeMac<'a, R> {
183    fn send_done(&self, buf: &'static mut [u8], acked: bool, result: Result<(), ErrorCode>) {
184        self.tx_client.map(move |c| {
185            c.send_done(buf, acked, result);
186        });
187    }
188}
189
190impl<'a, R: radio::Radio<'a>> radio::RxClient for AwakeMac<'a, R> {
191    fn receive(
192        &self,
193        buf: &'static mut [u8],
194        frame_len: usize,
195        lqi: u8,
196        crc_valid: bool,
197        result: Result<(), ErrorCode>,
198    ) {
199        // Filter packets by destination because radio is in promiscuous mode
200        let mut addr_match = false;
201        if let Some((_, (header, _))) = Header::decode(&buf[radio::PSDU_OFFSET..], false).done() {
202            if let Some(dst_addr) = header.dst_addr {
203                addr_match = match dst_addr {
204                    MacAddress::Short(addr) => {
205                        // Check if address matches radio or is set to multicast short addr 0xFFFF
206                        (addr == self.radio.get_address()) || (addr == 0xFFFF)
207                    }
208                    MacAddress::Long(long_addr) => long_addr == self.radio.get_address_long(),
209                };
210            }
211        }
212        if addr_match {
213            // debug!("[AwakeMAC] Rcvd a 15.4 frame addressed to this device");
214            self.rx_client.map(move |c| {
215                c.receive(buf, frame_len, lqi, crc_valid, result);
216            });
217        } else {
218            // debug!("[AwakeMAC] Received a packet, but not addressed to us");
219            self.radio.set_receive_buffer(buf);
220        }
221    }
222}