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