nano33ble_rev2/io.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 2023.
4
5use core::fmt::Write;
6use core::panic::PanicInfo;
7use core::ptr::{addr_of, addr_of_mut};
8
9use cortexm4;
10use kernel::debug;
11use kernel::debug::IoWrite;
12use kernel::hil::led;
13use kernel::hil::uart::{self};
14use kernel::ErrorCode;
15use nrf52840::gpio::Pin;
16
17use kernel::hil::uart::Transmit;
18use kernel::utilities::cells::VolatileCell;
19
20struct Writer {
21 initialized: bool,
22}
23
24static mut WRITER: Writer = Writer { initialized: false };
25
26impl Write for Writer {
27 fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
28 self.write(s.as_bytes());
29 Ok(())
30 }
31}
32
33const BUF_LEN: usize = 512;
34static mut STATIC_PANIC_BUF: [u8; BUF_LEN] = [0; BUF_LEN];
35
36static mut DUMMY: DummyUsbClient = DummyUsbClient {
37 fired: VolatileCell::new(false),
38};
39
40struct DummyUsbClient {
41 fired: VolatileCell<bool>,
42}
43
44impl uart::TransmitClient for DummyUsbClient {
45 fn transmitted_buffer(&self, _: &'static mut [u8], _: usize, _: Result<(), ErrorCode>) {
46 self.fired.set(true);
47 }
48}
49
50impl IoWrite for Writer {
51 fn write(&mut self, buf: &[u8]) -> usize {
52 if !self.initialized {
53 self.initialized = true;
54 }
55 // Here we mimic a synchronous UART output by calling transmit_buffer
56 // on the CDC stack and then spinning on USB interrupts until the transaction
57 // is complete. If the USB or CDC stack panicked, this may fail. It will also
58 // fail if the panic occurred prior to the USB connection being initialized.
59 // In the latter case, the LEDs should still blink in the panic pattern.
60
61 // spin so that if any USB DMA is ongoing it will finish
62 // we should only need this on the first call to write()
63 let mut i = 0;
64 loop {
65 i += 1;
66 cortexm4::support::nop();
67 if i > 10000 {
68 break;
69 }
70 }
71
72 // copy_from_slice() requires equal length slices
73 // This will truncate any writes longer than BUF_LEN, but simplifies the
74 // code. In practice, BUF_LEN=512 always seems sufficient for the size of
75 // individual calls to write made by the panic handler.
76 let mut max = BUF_LEN;
77 if buf.len() < BUF_LEN {
78 max = buf.len();
79 }
80
81 unsafe {
82 // If CDC_REF_FOR_PANIC is not yet set we panicked very early,
83 // and not much we can do. Don't want to double fault,
84 // so just return.
85 super::CDC_REF_FOR_PANIC.map(|cdc| {
86 // Lots of unsafe dereferencing of global static mut objects here.
87 // However, this should be okay, because it all happens within
88 // a single thread, and:
89 // - This is the only place the global CDC_REF_FOR_PANIC is used, the logic is the same
90 // as applies for the global CHIP variable used in the panic handler.
91 // - We do create multiple mutable references to the STATIC_PANIC_BUF, but we never
92 // access the STATIC_PANIC_BUF after a slice of it is passed to transmit_buffer
93 // until the slice has been returned in the uart callback.
94 // - Similarly, only this function uses the global DUMMY variable, and we do not
95 // mutate it.
96 let usb = &mut cdc.controller();
97 STATIC_PANIC_BUF[..max].copy_from_slice(&buf[..max]);
98 let static_buf = &mut *addr_of_mut!(STATIC_PANIC_BUF);
99 cdc.set_transmit_client(&*addr_of!(DUMMY));
100 let _ = cdc.transmit_buffer(static_buf, max);
101 loop {
102 if let Some(interrupt) = cortexm4::nvic::next_pending() {
103 if interrupt == 39 {
104 usb.handle_interrupt();
105 }
106 let n = cortexm4::nvic::Nvic::new(interrupt);
107 n.clear_pending();
108 n.enable();
109 }
110 if (*addr_of!(DUMMY)).fired.get() {
111 // buffer finished transmitting, return so we can output additional
112 // messages when requested by the panic handler.
113 break;
114 }
115 }
116 (*addr_of!(DUMMY)).fired.set(false);
117 });
118 }
119 buf.len()
120 }
121}
122
123/// Default panic handler for the Nano 33 Board.
124///
125/// We just use the standard default provided by the debug module in the kernel.
126#[cfg(not(test))]
127#[panic_handler]
128pub unsafe fn panic_fmt(pi: &PanicInfo) -> ! {
129 let led_kernel_pin = &nrf52840::gpio::GPIOPin::new(Pin::P0_13);
130 let led = &mut led::LedLow::new(led_kernel_pin);
131 let writer = &mut *addr_of_mut!(WRITER);
132 debug::panic(
133 &mut [led],
134 writer,
135 pi,
136 &cortexm4::support::nop,
137 crate::PANIC_RESOURCES.get(),
138 )
139}