capsules_extra/ieee802154/
phy_driver.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 2024.
4
5//! IEEE 802.15.4 userspace interface for configuration and transmit/receive.
6//!
7//! Implements a userspace interface for sending and receiving raw IEEE 802.15.4
8//! frames.
9//!
10//! Sending - Userspace fully forms the 15.4 frame and passes it to the driver.
11//!
12//! Receiving - The driver receives 15.4 frames and passes them to the process.
13//! To accomplish this, the process must first `allow` a read/write ring buffer
14//! to the kernel. The kernel will then fill this buffer with received frames
15//! and schedule an upcall upon receipt of the first packet.
16//!
17//! The ring buffer provided by the process must be of the form:
18//!
19//! ```text
20//! | read index | write index | user_frame 0 | user_frame 1 | ... | user_frame n |
21//! ```
22//!
23//! `user_frame` denotes the 15.4 frame in addition to the relevant 3 bytes of
24//! metadata (offset to data payload, length of data payload, and the MIC len).
25//! The capsule assumes that this is the form of the buffer. Errors or deviation
26//! in the form of the provided buffer will likely result in incomplete or
27//! dropped packets.
28//!
29//! Because the scheduled receive upcall must be handled by the process, there
30//! is no guarantee as to when this will occur and if additional packets will be
31//! received prior to the upcall being handled. Without a ring buffer (or some
32//! equivalent data structure), the original packet will be lost. The ring
33//! buffer allows for the upcall to be scheduled and for all received packets to
34//! be passed to the process. The ring buffer is designed to overwrite old
35//! packets if the buffer becomes full. If the process notices a high number of
36//! "dropped" packets, this may be the cause. The process can mitigate this
37//! issue by increasing the size of the ring buffer provided to the capsule.
38
39use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
40use kernel::hil;
41use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer};
42use kernel::syscall::{CommandReturn, SyscallDriver};
43use kernel::utilities::cells::{OptionalCell, TakeCell};
44use kernel::{ErrorCode, ProcessId};
45
46/// IDs for subscribed upcalls.
47mod upcall {
48    /// Frame is received
49    pub const FRAME_RECEIVED: usize = 0;
50    /// Frame is transmitted
51    pub const FRAME_TRANSMITTED: usize = 1;
52    /// Number of upcalls.
53    pub const COUNT: u8 = 2;
54}
55
56/// Ids for read-only allow buffers
57mod ro_allow {
58    /// Write buffer. Contains the frame payload to be transmitted.
59    pub const WRITE: usize = 0;
60    /// The number of allow buffers the kernel stores for this grant
61    pub const COUNT: u8 = 1;
62}
63
64/// Ids for read-write allow buffers
65mod rw_allow {
66    /// Read buffer. Will contain the received frame.
67    pub const READ: usize = 0;
68    /// The number of allow buffers the kernel stores for this grant
69    pub const COUNT: u8 = 1;
70}
71
72use capsules_core::driver;
73pub const DRIVER_NUM: usize = driver::NUM::Ieee802154 as usize;
74
75#[derive(Default)]
76pub struct App {
77    pending_tx: bool,
78}
79
80pub struct RadioDriver<'a, R: hil::radio::Radio<'a>> {
81    /// Underlying radio.
82    radio: &'a R,
83
84    /// Grant of apps that use this radio driver.
85    apps: Grant<
86        App,
87        UpcallCount<{ upcall::COUNT }>,
88        AllowRoCount<{ ro_allow::COUNT }>,
89        AllowRwCount<{ rw_allow::COUNT }>,
90    >,
91    /// ID of app whose transmission request is being processed.
92    current_app: OptionalCell<ProcessId>,
93
94    /// Buffer that stores the IEEE 802.15.4 frame to be transmitted.
95    kernel_tx: TakeCell<'static, [u8]>,
96}
97
98impl<'a, R: hil::radio::Radio<'a>> RadioDriver<'a, R> {
99    pub fn new(
100        radio: &'a R,
101        grant: Grant<
102            App,
103            UpcallCount<{ upcall::COUNT }>,
104            AllowRoCount<{ ro_allow::COUNT }>,
105            AllowRwCount<{ rw_allow::COUNT }>,
106        >,
107        kernel_tx: &'static mut [u8],
108    ) -> Self {
109        Self {
110            radio,
111            apps: grant,
112            current_app: OptionalCell::empty(),
113            kernel_tx: TakeCell::new(kernel_tx),
114        }
115    }
116
117    /// Performs `processid`'s pending transmission. Assumes that the driver is
118    /// currently idle and the app has a pending transmission.
119    fn perform_tx(&self, processid: ProcessId) -> Result<(), ErrorCode> {
120        self.apps.enter(processid, |app, kernel_data| {
121            app.pending_tx = false;
122
123            self.kernel_tx.take().map_or(Err(ErrorCode::NOMEM), |kbuf| {
124                kernel_data
125                    .get_readonly_processbuffer(ro_allow::WRITE)
126                    .and_then(|write| {
127                        write.enter(|payload| {
128                            let frame_len = payload.len();
129                            let dst_start = hil::radio::PSDU_OFFSET;
130                            let dst_end = dst_start + frame_len;
131                            payload.copy_to_slice(&mut kbuf[dst_start..dst_end]);
132
133                            self.radio.transmit(kbuf, frame_len).map_or_else(
134                                |(errorcode, error_buf)| {
135                                    self.kernel_tx.replace(error_buf);
136                                    Err(errorcode)
137                                },
138                                |()| {
139                                    self.current_app.set(processid);
140                                    Ok(())
141                                },
142                            )
143                        })
144                    })?
145            })
146        })?
147    }
148
149    /// If the driver is currently idle and there are pending transmissions,
150    /// pick an app with a pending transmission and return its `ProcessId`.
151    fn get_next_tx_if_idle(&self) -> Option<ProcessId> {
152        if self.current_app.is_some() {
153            return None;
154        }
155        let mut pending_app = None;
156        for app in self.apps.iter() {
157            let processid = app.processid();
158            app.enter(|app, _| {
159                if app.pending_tx {
160                    pending_app = Some(processid);
161                }
162            });
163            if pending_app.is_some() {
164                break;
165            }
166        }
167        pending_app
168    }
169
170    /// Schedule the next transmission if there is one pending.
171    fn do_next_tx(&self) {
172        self.get_next_tx_if_idle()
173            .map(|processid| match self.perform_tx(processid) {
174                Ok(()) => {}
175                Err(e) => {
176                    let _ = self.apps.enter(processid, |_app, upcalls| {
177                        let _ = upcalls.schedule_upcall(
178                            upcall::FRAME_TRANSMITTED,
179                            (kernel::errorcode::into_statuscode(Err(e)), 0, 0),
180                        );
181                    });
182                }
183            });
184    }
185}
186
187impl<'a, R: hil::radio::Radio<'a>> SyscallDriver for RadioDriver<'a, R> {
188    /// IEEE 802.15.4 low-level control.
189    ///
190    /// ### `command_num`
191    ///
192    /// - `0`: Driver existence check.
193    /// - `1`: Return radio status. Ok(())/OFF = on/off.
194    /// - `2`: Set short address.
195    /// - `4`: Set PAN ID.
196    /// - `5`: Set channel.
197    /// - `6`: Set transmission power.
198    /// - `7`: Commit any configuration changes.
199    /// - `8`: Get the short MAC address.
200    /// - `10`: Get the PAN ID.
201    /// - `11`: Get the channel.
202    /// - `12`: Get the transmission power.
203    /// - `27`: Transmit a frame. The frame must be stored in the write RO allow
204    ///   buffer 0. The allowed buffer must be the length of the frame. The
205    ///   frame includes the PDSU (i.e., the MAC payload) _without_ the MFR
206    ///   (i.e., CRC) bytes.
207    /// - `28`: Set long address.
208    /// - `29`: Get the long MAC address.
209    /// - `30`: Turn the radio on.
210    /// - `31`: Turn the radio off.
211    fn command(
212        &self,
213        command_number: usize,
214        arg1: usize,
215        arg2: usize,
216        processid: ProcessId,
217    ) -> CommandReturn {
218        match command_number {
219            0 => CommandReturn::success(),
220            1 => {
221                if self.radio.is_on() {
222                    CommandReturn::success()
223                } else {
224                    CommandReturn::failure(ErrorCode::OFF)
225                }
226            }
227            2 => {
228                self.radio.set_address(arg1 as u16);
229                CommandReturn::success()
230            }
231            4 => {
232                self.radio.set_pan(arg1 as u16);
233                CommandReturn::success()
234            }
235            5 => {
236                let channel = (arg1 as u8).try_into();
237                channel.map_or(CommandReturn::failure(ErrorCode::INVAL), |chan| {
238                    self.radio.set_channel(chan);
239                    CommandReturn::success()
240                })
241            }
242            6 => self.radio.set_tx_power(arg1 as i8).into(),
243            7 => {
244                self.radio.config_commit();
245                CommandReturn::success()
246            }
247            8 => {
248                // Guarantee that address is positive by adding 1
249                let addr = self.radio.get_address();
250                CommandReturn::success_u32(addr as u32 + 1)
251            }
252            10 => {
253                // Guarantee that the PAN is positive by adding 1
254                let pan = self.radio.get_pan();
255                CommandReturn::success_u32(pan as u32 + 1)
256            }
257            11 => {
258                let channel = self.radio.get_channel();
259                CommandReturn::success_u32(channel as u32)
260            }
261            12 => {
262                let txpower = self.radio.get_tx_power();
263                CommandReturn::success_u32(txpower as u32)
264            }
265            27 => {
266                self.apps
267                    .enter(processid, |app, _| {
268                        if app.pending_tx {
269                            // Cannot support more than one pending TX per process.
270                            return Err(ErrorCode::BUSY);
271                        }
272                        app.pending_tx = true;
273                        Ok(())
274                    })
275                    .map_or_else(
276                        |err| CommandReturn::failure(err.into()),
277                        |_| {
278                            self.do_next_tx();
279                            CommandReturn::success()
280                        },
281                    )
282            }
283            28 => {
284                let addr_upper: u64 = arg2 as u64;
285                let addr_lower: u64 = arg1 as u64;
286                let addr = addr_upper << 32 | addr_lower;
287                self.radio.set_address_long(addr.to_be_bytes());
288                CommandReturn::success()
289            }
290            29 => {
291                let addr = u64::from_be_bytes(self.radio.get_address_long());
292                CommandReturn::success_u64(addr)
293            }
294            30 => self.radio.start().into(),
295            31 => self.radio.stop().into(),
296            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
297        }
298    }
299
300    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
301        self.apps.enter(processid, |_, _| {})
302    }
303}
304
305impl<'a, R: hil::radio::Radio<'a>> hil::radio::TxClient for RadioDriver<'a, R> {
306    fn send_done(&self, spi_buf: &'static mut [u8], acked: bool, result: Result<(), ErrorCode>) {
307        self.kernel_tx.replace(spi_buf);
308        self.current_app.take().map(|processid| {
309            let _ = self.apps.enter(processid, |_app, upcalls| {
310                upcalls
311                    .schedule_upcall(
312                        upcall::FRAME_TRANSMITTED,
313                        (kernel::errorcode::into_statuscode(result), acked.into(), 0),
314                    )
315                    .ok();
316            });
317        });
318        self.do_next_tx();
319    }
320}
321
322impl<'a, R: hil::radio::Radio<'a>> hil::radio::RxClient for RadioDriver<'a, R> {
323    fn receive<'b>(
324        &self,
325        buf: &'static mut [u8],
326        frame_len: usize,
327        lqi: u8,
328        crc_valid: bool,
329        result: Result<(), ErrorCode>,
330    ) {
331        // Drop invalid packets or packets that had errors during reception.
332        if !crc_valid || result.is_err() {
333            // Replace the RX buffer and drop the packet.
334            self.radio.set_receive_buffer(buf);
335            return;
336        }
337
338        self.apps.each(|_, _, kernel_data| {
339            let read_present = kernel_data
340                .get_readwrite_processbuffer(rw_allow::READ)
341                .and_then(|read| {
342                    read.mut_enter(|rbuf| {
343                        ////////////////////////////////////////////////////////
344                        // NOTE: context for the ring buffer and assumptions
345                        // regarding the ring buffer format and usage can be
346                        // found in the detailed comment at the top of this
347                        // file.
348                        //
349                        // Ring buffer format:
350                        //  | read  | write | user_frame | user_frame |...| user_frame |
351                        //  | index | index | 0          | 1          |   | n          |
352                        //
353                        // user_frame format:
354                        //  | header_len | payload_len | mic_len | 15.4 frame |
355                        //
356                        ////////////////////////////////////////////////////////
357
358                        // 2 bytes for the readwrite buffer metadata (read and
359                        // write index).
360                        const RING_BUF_METADATA_SIZE: usize = 2;
361
362                        /// 3 byte metadata (offset, len, mic_len)
363                        const USER_FRAME_METADATA_SIZE: usize = 3;
364
365                        /// 3 byte metadata + 127 byte max payload
366                        const USER_FRAME_MAX_SIZE: usize =
367                            USER_FRAME_METADATA_SIZE + hil::radio::MAX_FRAME_SIZE;
368
369                        // Confirm the availability of the buffer. A buffer of
370                        // len 0 is indicative of the userprocess not allocating
371                        // a readwrite buffer. We must also confirm that the
372                        // userprocess correctly formatted the buffer to be of
373                        // length 2 + n * USER_FRAME_MAX_SIZE, where n is the
374                        // number of user frames that the buffer can store. We
375                        // combine checking the buffer's non-zero length and the
376                        // case of the buffer being shorter than the
377                        // `RING_BUF_METADATA_SIZE` as an invalid buffer (e.g.
378                        // of length 1) may otherwise errantly pass the second
379                        // conditional check (due to unsigned integer
380                        // arithmetic).
381                        if rbuf.len() <= RING_BUF_METADATA_SIZE
382                            || (rbuf.len() - RING_BUF_METADATA_SIZE) % USER_FRAME_MAX_SIZE != 0
383                        {
384                            return false;
385                        }
386
387                        let mut read_index = rbuf[0].get() as usize;
388                        let mut write_index = rbuf[1].get() as usize;
389
390                        let max_pending_rx =
391                            (rbuf.len() - RING_BUF_METADATA_SIZE) / USER_FRAME_MAX_SIZE;
392
393                        // Confirm user modifiable metadata is valid (i.e.
394                        // within bounds of the provided buffer).
395                        if read_index >= max_pending_rx || write_index >= max_pending_rx {
396                            return false;
397                        }
398
399                        // We don't parse the received packet, so we don't know
400                        // how long all of the pieces are.
401                        let mic_len = 0;
402                        let header_len = 0;
403
404                        // Start in the buffer where we are going to write this
405                        // incoming packet.
406                        let offset = RING_BUF_METADATA_SIZE + (write_index * USER_FRAME_MAX_SIZE);
407
408                        // Copy the entire frame over to userland, preceded by
409                        // three metadata bytes: the header length, the data
410                        // length, and the MIC length.
411                        let dst_start = offset + USER_FRAME_METADATA_SIZE;
412                        let dst_end = dst_start + frame_len;
413                        let src_start = hil::radio::PSDU_OFFSET;
414                        let src_end = src_start + frame_len;
415                        rbuf[dst_start..dst_end].copy_from_slice(&buf[src_start..src_end]);
416
417                        rbuf[offset].set(header_len as u8);
418                        rbuf[offset + 1].set(frame_len as u8);
419                        rbuf[offset + 2].set(mic_len as u8);
420
421                        // Prepare the ring buffer for the next write. The
422                        // current design favors newness; newly received packets
423                        // will begin to overwrite the oldest data in the event
424                        // of the buffer becoming full. The read index must
425                        // always point to the "oldest" data. If we have
426                        // overwritten the oldest data, the next oldest data is
427                        // now at the read index + 1. We must update the read
428                        // index to reflect this.
429                        write_index = (write_index + 1) % max_pending_rx;
430                        if write_index == read_index {
431                            read_index = (read_index + 1) % max_pending_rx;
432                            rbuf[0].set(read_index as u8);
433                        }
434
435                        // Update write index metadata since we have added a
436                        // frame.
437                        rbuf[1].set(write_index as u8);
438                        true
439                    })
440                })
441                .unwrap_or(false);
442            if read_present {
443                // Place lqi as argument to be included in upcall.
444                kernel_data
445                    .schedule_upcall(upcall::FRAME_RECEIVED, (lqi as usize, 0, 0))
446                    .ok();
447            }
448        });
449
450        self.radio.set_receive_buffer(buf);
451    }
452}
453
454impl<'a, R: hil::radio::Radio<'a>> hil::radio::ConfigClient for RadioDriver<'a, R> {
455    fn config_done(&self, _result: Result<(), ErrorCode>) {}
456}
457
458impl<'a, R: hil::radio::Radio<'a>> hil::radio::PowerClient for RadioDriver<'a, R> {
459    fn changed(&self, _on: bool) {}
460}