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}