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