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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Specifies the interface for IEEE 802.15.4 MAC protocol layers. MAC protocols
//! expose similar configuration (address, PAN, transmission power) options
//! as ieee802154::device::MacDevice layers above it, but retain control over
//! radio power management and channel selection. All frame processing should
//! be completed above this layer such that Mac implementations receive fully
//! formatted 802.15.4 MAC frames for transmission.
//!
//! AwakeMac provides a default implementation of such a layer, maintaining
//! the underlying kernel::hil::radio::Radio powered at all times and passing
//! through each frame for transmission.

use crate::net::ieee802154::{Header, MacAddress};
use kernel::hil::radio::{self, MAX_FRAME_SIZE, PSDU_OFFSET};
use kernel::utilities::cells::OptionalCell;
use kernel::ErrorCode;

pub trait Mac<'a> {
    /// Initializes the layer.
    fn initialize(&self) -> Result<(), ErrorCode>;

    /// Sets the notified client for configuration changes
    fn set_config_client(&self, client: &'a dyn radio::ConfigClient);
    /// Sets the notified client for transmission completions
    fn set_transmit_client(&self, client: &'a dyn radio::TxClient);
    /// Sets the notified client for frame receptions
    fn set_receive_client(&self, client: &'a dyn radio::RxClient);
    /// Sets the buffer for packet reception
    fn set_receive_buffer(&self, buffer: &'static mut [u8]);

    /// The short 16-bit address of the radio
    fn get_address(&self) -> u16;
    /// The long 64-bit address of the radio
    fn get_address_long(&self) -> [u8; 8];
    /// The 16-bit PAN id of the radio
    fn get_pan(&self) -> u16;

    /// Sets the short 16-bit address of the radio
    fn set_address(&self, addr: u16);
    /// Sets the long 64-bit address of the radio
    fn set_address_long(&self, addr: [u8; 8]);
    /// Sets the 16-bit PAN id of the radio
    fn set_pan(&self, id: u16);

    /// Must be called after one or more calls to `set_*`. If
    /// `set_*` is called without calling `config_commit`, there is no guarantee
    /// that the underlying hardware configuration (addresses, pan ID) is in
    /// line with this MAC protocol implementation. The specified config_client is
    /// notified on completed reconfiguration.
    fn config_commit(&self);

    /// Indicates whether or not the MAC protocol is active and can send frames
    fn is_on(&self) -> bool;

    /// Transmits complete MAC frames, which must be prepared by an ieee802154::device::MacDevice
    /// before being passed to the Mac layer. Returns the frame buffer in case of an error.
    fn transmit(
        &self,
        full_mac_frame: &'static mut [u8],
        frame_len: usize,
    ) -> Result<(), (ErrorCode, &'static mut [u8])>;
}

///
/// Default implementation of a Mac layer. Acts as a pass-through between a MacDevice
/// implementation and the underlying radio::Radio device. Does not change the power
/// state of the radio during operation.
///
pub struct AwakeMac<'a, R: radio::Radio<'a>> {
    radio: &'a R,

    tx_client: OptionalCell<&'a dyn radio::TxClient>,
    rx_client: OptionalCell<&'a dyn radio::RxClient>,
}

impl<'a, R: radio::Radio<'a>> AwakeMac<'a, R> {
    pub fn new(radio: &'a R) -> AwakeMac<'a, R> {
        AwakeMac {
            radio,
            tx_client: OptionalCell::empty(),
            rx_client: OptionalCell::empty(),
        }
    }
}

impl<'a, R: radio::Radio<'a>> Mac<'a> for AwakeMac<'a, R> {
    fn initialize(&self) -> Result<(), ErrorCode> {
        // do nothing, extra buffer unnecessary
        Ok(())
    }

    fn is_on(&self) -> bool {
        self.radio.is_on()
    }

    fn set_config_client(&self, client: &'a dyn radio::ConfigClient) {
        self.radio.set_config_client(client)
    }

    fn set_address(&self, addr: u16) {
        self.radio.set_address(addr)
    }

    fn set_address_long(&self, addr: [u8; 8]) {
        self.radio.set_address_long(addr)
    }

    fn set_pan(&self, id: u16) {
        self.radio.set_pan(id)
    }

    fn get_address(&self) -> u16 {
        self.radio.get_address()
    }

    fn get_address_long(&self) -> [u8; 8] {
        self.radio.get_address_long()
    }

    fn get_pan(&self) -> u16 {
        self.radio.get_pan()
    }

    fn config_commit(&self) {
        self.radio.config_commit()
    }

    fn set_transmit_client(&self, client: &'a dyn radio::TxClient) {
        self.tx_client.set(client);
    }

    fn set_receive_client(&self, client: &'a dyn radio::RxClient) {
        self.rx_client.set(client);
    }

    fn set_receive_buffer(&self, buffer: &'static mut [u8]) {
        self.radio.set_receive_buffer(buffer);
    }

    fn transmit(
        &self,
        full_mac_frame: &'static mut [u8],
        frame_len: usize,
    ) -> Result<(), (ErrorCode, &'static mut [u8])> {
        // We must add the PSDU_OFFSET required for the radio
        // hardware. We first error check the provided arguments
        // and then shift the 15.4 frame by the `PSDU_OFFSET`.

        if full_mac_frame.len() < frame_len + PSDU_OFFSET {
            return Err((ErrorCode::NOMEM, full_mac_frame));
        }

        if frame_len > MAX_FRAME_SIZE {
            return Err((ErrorCode::INVAL, full_mac_frame));
        }

        full_mac_frame.copy_within(0..frame_len, PSDU_OFFSET);
        self.radio.transmit(full_mac_frame, frame_len)
    }
}

impl<'a, R: radio::Radio<'a>> radio::TxClient for AwakeMac<'a, R> {
    fn send_done(&self, buf: &'static mut [u8], acked: bool, result: Result<(), ErrorCode>) {
        self.tx_client.map(move |c| {
            c.send_done(buf, acked, result);
        });
    }
}

impl<'a, R: radio::Radio<'a>> radio::RxClient for AwakeMac<'a, R> {
    fn receive(
        &self,
        buf: &'static mut [u8],
        frame_len: usize,
        lqi: u8,
        crc_valid: bool,
        result: Result<(), ErrorCode>,
    ) {
        // Filter packets by destination because radio is in promiscuous mode
        let mut addr_match = false;
        if let Some((_, (header, _))) = Header::decode(&buf[radio::PSDU_OFFSET..], false).done() {
            if let Some(dst_addr) = header.dst_addr {
                addr_match = match dst_addr {
                    MacAddress::Short(addr) => {
                        // Check if address matches radio or is set to multicast short addr 0xFFFF
                        (addr == self.radio.get_address()) || (addr == 0xFFFF)
                    }
                    MacAddress::Long(long_addr) => long_addr == self.radio.get_address_long(),
                };
            }
        }
        if addr_match {
            // debug!("[AwakeMAC] Rcvd a 15.4 frame addressed to this device");
            self.rx_client.map(move |c| {
                c.receive(buf, frame_len, lqi, crc_valid, result);
            });
        } else {
            // debug!("[AwakeMAC] Received a packet, but not addressed to us");
            self.radio.set_receive_buffer(buf);
        }
    }
}