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.
45//! 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.
1718use crate::net::ieee802154::{Header, MacAddress};
19use kernel::hil::radio::{self, MAX_FRAME_SIZE, PSDU_OFFSET};
20use kernel::utilities::cells::OptionalCell;
21use kernel::ErrorCode;
2223pub trait Mac<'a> {
24/// Initializes the layer.
25fn initialize(&self) -> Result<(), ErrorCode>;
2627/// Sets the notified client for configuration changes
28fn set_config_client(&self, client: &'a dyn radio::ConfigClient);
29/// Sets the notified client for transmission completions
30fn set_transmit_client(&self, client: &'a dyn radio::TxClient);
31/// Sets the notified client for frame receptions
32fn set_receive_client(&self, client: &'a dyn radio::RxClient);
33/// Sets the buffer for packet reception
34fn set_receive_buffer(&self, buffer: &'static mut [u8]);
3536/// The short 16-bit address of the radio
37fn get_address(&self) -> u16;
38/// The long 64-bit address of the radio
39fn get_address_long(&self) -> [u8; 8];
40/// The 16-bit PAN id of the radio
41fn get_pan(&self) -> u16;
4243/// Sets the short 16-bit address of the radio
44fn set_address(&self, addr: u16);
45/// Sets the long 64-bit address of the radio
46fn set_address_long(&self, addr: [u8; 8]);
47/// Sets the 16-bit PAN id of the radio
48fn set_pan(&self, id: u16);
4950/// 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.
55fn config_commit(&self);
5657/// Indicates whether or not the MAC protocol is active and can send frames
58fn is_on(&self) -> bool;
5960/// 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.
69fn start(&self) -> Result<(), ErrorCode>;
7071/// 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.
73fn transmit(
74&self,
75 full_mac_frame: &'static mut [u8],
76 frame_len: usize,
77 ) -> Result<(), (ErrorCode, &'static mut [u8])>;
78}
7980///
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,
8788 tx_client: OptionalCell<&'a dyn radio::TxClient>,
89 rx_client: OptionalCell<&'a dyn radio::RxClient>,
90}
9192impl<'a, R: radio::Radio<'a>> AwakeMac<'a, R> {
93pub 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}
101102impl<'a, R: radio::Radio<'a>> Mac<'a> for AwakeMac<'a, R> {
103fn initialize(&self) -> Result<(), ErrorCode> {
104// do nothing, extra buffer unnecessary
105Ok(())
106 }
107108fn is_on(&self) -> bool {
109self.radio.is_on()
110 }
111112fn start(&self) -> Result<(), ErrorCode> {
113self.radio.start()
114 }
115116fn set_config_client(&self, client: &'a dyn radio::ConfigClient) {
117self.radio.set_config_client(client)
118 }
119120fn set_address(&self, addr: u16) {
121self.radio.set_address(addr)
122 }
123124fn set_address_long(&self, addr: [u8; 8]) {
125self.radio.set_address_long(addr)
126 }
127128fn set_pan(&self, id: u16) {
129self.radio.set_pan(id)
130 }
131132fn get_address(&self) -> u16 {
133self.radio.get_address()
134 }
135136fn get_address_long(&self) -> [u8; 8] {
137self.radio.get_address_long()
138 }
139140fn get_pan(&self) -> u16 {
141self.radio.get_pan()
142 }
143144fn config_commit(&self) {
145self.radio.config_commit()
146 }
147148fn set_transmit_client(&self, client: &'a dyn radio::TxClient) {
149self.tx_client.set(client);
150 }
151152fn set_receive_client(&self, client: &'a dyn radio::RxClient) {
153self.rx_client.set(client);
154 }
155156fn set_receive_buffer(&self, buffer: &'static mut [u8]) {
157self.radio.set_receive_buffer(buffer);
158 }
159160fn 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`.
168169if full_mac_frame.len() < frame_len + PSDU_OFFSET {
170return Err((ErrorCode::NOMEM, full_mac_frame));
171 }
172173if frame_len > MAX_FRAME_SIZE {
174return Err((ErrorCode::INVAL, full_mac_frame));
175 }
176177 full_mac_frame.copy_within(0..frame_len, PSDU_OFFSET);
178self.radio.transmit(full_mac_frame, frame_len)
179 }
180}
181182impl<'a, R: radio::Radio<'a>> radio::TxClient for AwakeMac<'a, R> {
183fn send_done(&self, buf: &'static mut [u8], acked: bool, result: Result<(), ErrorCode>) {
184self.tx_client.map(move |c| {
185 c.send_done(buf, acked, result);
186 });
187 }
188}
189190impl<'a, R: radio::Radio<'a>> radio::RxClient for AwakeMac<'a, R> {
191fn 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
200let mut addr_match = false;
201if let Some((_, (header, _))) = Header::decode(&buf[radio::PSDU_OFFSET..], false).done() {
202if 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 }
212if addr_match {
213// debug!("[AwakeMAC] Rcvd a 15.4 frame addressed to this device");
214self.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");
219self.radio.set_receive_buffer(buf);
220 }
221 }
222}