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