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}