nano33ble/io.rs
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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
use core::fmt::Write;
use core::panic::PanicInfo;
use core::ptr::{addr_of, addr_of_mut};
use kernel::debug;
use kernel::debug::IoWrite;
use kernel::hil::led;
use kernel::hil::uart::{self};
use kernel::ErrorCode;
use nrf52840::gpio::Pin;
use crate::CHIP;
use crate::PROCESSES;
use crate::PROCESS_PRINTER;
use kernel::hil::uart::Transmit;
use kernel::utilities::cells::VolatileCell;
struct Writer {
initialized: bool,
}
static mut WRITER: Writer = Writer { initialized: false };
impl Write for Writer {
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
self.write(s.as_bytes());
Ok(())
}
}
const BUF_LEN: usize = 512;
static mut STATIC_PANIC_BUF: [u8; BUF_LEN] = [0; BUF_LEN];
static mut DUMMY: DummyUsbClient = DummyUsbClient {
fired: VolatileCell::new(false),
};
struct DummyUsbClient {
fired: VolatileCell<bool>,
}
impl uart::TransmitClient for DummyUsbClient {
fn transmitted_buffer(&self, _: &'static mut [u8], _: usize, _: Result<(), ErrorCode>) {
self.fired.set(true);
}
}
impl IoWrite for Writer {
fn write(&mut self, buf: &[u8]) -> usize {
if !self.initialized {
self.initialized = true;
}
// Here we mimic a synchronous UART output by calling transmit_buffer
// on the CDC stack and then spinning on USB interrupts until the transaction
// is complete. If the USB or CDC stack panicked, this may fail. It will also
// fail if the panic occurred prior to the USB connection being initialized.
// In the latter case, the LEDs should still blink in the panic pattern.
// spin so that if any USB DMA is ongoing it will finish
// we should only need this on the first call to write()
let mut i = 0;
loop {
i += 1;
cortexm4::support::nop();
if i > 10000 {
break;
}
}
// copy_from_slice() requires equal length slices
// This will truncate any writes longer than BUF_LEN, but simplifies the
// code. In practice, BUF_LEN=512 always seems sufficient for the size of
// individual calls to write made by the panic handler.
let mut max = BUF_LEN;
if buf.len() < BUF_LEN {
max = buf.len();
}
unsafe {
// If CDC_REF_FOR_PANIC is not yet set we panicked very early,
// and not much we can do. Don't want to double fault,
// so just return.
super::CDC_REF_FOR_PANIC.map(|cdc| {
// Lots of unsafe dereferencing of global static mut objects here.
// However, this should be okay, because it all happens within
// a single thread, and:
// - This is the only place the global CDC_REF_FOR_PANIC is used, the logic is the same
// as applies for the global CHIP variable used in the panic handler.
// - We do create multiple mutable references to the STATIC_PANIC_BUF, but we never
// access the STATIC_PANIC_BUF after a slice of it is passed to transmit_buffer
// until the slice has been returned in the uart callback.
// - Similarly, only this function uses the global DUMMY variable, and we do not
// mutate it.
let usb = &mut cdc.controller();
STATIC_PANIC_BUF[..max].copy_from_slice(&buf[..max]);
let static_buf = &mut *addr_of_mut!(STATIC_PANIC_BUF);
cdc.set_transmit_client(&*addr_of!(DUMMY));
let _ = cdc.transmit_buffer(static_buf, max);
loop {
if let Some(interrupt) = cortexm4::nvic::next_pending() {
if interrupt == 39 {
usb.handle_interrupt();
}
let n = cortexm4::nvic::Nvic::new(interrupt);
n.clear_pending();
n.enable();
}
if (*addr_of!(DUMMY)).fired.get() {
// buffer finished transmitting, return so we can output additional
// messages when requested by the panic handler.
break;
}
}
(*addr_of!(DUMMY)).fired.set(false);
});
}
buf.len()
}
}
/// Default panic handler for the Nano 33 Board.
///
/// We just use the standard default provided by the debug module in the kernel.
#[cfg(not(test))]
#[no_mangle]
#[panic_handler]
pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! {
let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_13);
let led = &mut led::LedLow::new(led_kernel_pin);
let writer = &mut *addr_of_mut!(WRITER);
debug::panic(
&mut [led],
writer,
pi,
&cortexm4::support::nop,
&*addr_of!(PROCESSES),
&*addr_of!(CHIP),
&*addr_of!(PROCESS_PRINTER),
)
}