use crate::rtc; use core::cell::UnsafeCell; use core::fmt::{self, Write}; use core::mem::MaybeUninit; use log::{Metadata, Record}; use nrf52840_hal::{target::UARTE0, uarte, Uarte}; use starb::{Reader, RingBuffer, Writer}; static mut UARTE0: usize = 0; struct JoinedRingBuffer<'a> { lbr: Reader<'a, u8>, lbw: Writer<'a, u8>, } impl<'a> JoinedRingBuffer<'a> { const fn new(rb: &'a RingBuffer) -> Self { let (lbr, lbw) = rb.split(); Self { lbr: lbr, lbw: lbw } } } impl fmt::Write for JoinedRingBuffer<'_> { fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { // Drop anything that couldn't fit on the ground so we don't // back up. self.lbw.unshift_from(s.as_bytes()); Ok(()) } } static mut LB: RingBuffer = RingBuffer::::new(); static mut JRB: JoinedRingBuffer = unsafe { JoinedRingBuffer::new(&LB) }; // The UART isn't necessarily Sync, so wrap it in something that // is. As long as flush() is only called from one thread, we're fine, // but this is a guarantee that the logger module doesn't make. pub struct WriteWrapper { w: W, } impl WriteWrapper { pub fn new(writer: W) -> Self { Self { w: writer } } } unsafe impl Sync for WriteWrapper {} pub struct SerialLogger { writer: UnsafeCell>>, } impl SerialLogger where W: uarte::Instance, { pub fn new(writer: WriteWrapper>) -> Self { // Stash this for unsafe usage in case there's an issue with // the rest of the logging. unsafe { UARTE0 = core::mem::transmute(&writer.w) }; Self { writer: UnsafeCell::new(writer), } } } unsafe impl Send for SerialLogger {} unsafe impl Sync for SerialLogger {} impl log::Log for SerialLogger where W: uarte::Instance, { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= log::max_level() } fn log(&self, record: &Record) { if !self.enabled(record.metadata()) { return; } let jrb = unsafe { &mut JRB }; if record.target() == "usb" { write!(jrb, "{}", record.args()).ok(); } else { write!( jrb, "[{}] {} {} -- {}\r\n", rtc::millis(), record.level(), record.target(), record.args() ) .ok(); } } fn flush(&self) { //let start = rtc::millis(); let jrb = unsafe { &mut JRB }; let writer = unsafe { &mut (*self.writer.get()) }; let mut buf: [u8; 256] = unsafe { MaybeUninit::<[u8; 256]>::uninit().assume_init() }; let len = jrb.lbr.shift_into(&mut buf); if len > 0 { writer.w.write(&buf[0..len]).expect("writing log"); /* let end = rtc::millis(); writer .w .write(&[b' ', b'-', b' ', b'0' + (end as u8 - start as u8)]) .ok(); */ } } } // Write to the UART right now, instead of putting it on a ring // buffer. This function is a huge hack, and only useful for debugging // either before the main loop starts or if the ring buffer is broken. pub unsafe fn write_fmt_now(args: fmt::Arguments, nl: bool) { if UARTE0 == 0 { return; } let uart: &mut Uarte = core::mem::transmute(UARTE0); fmt::write(uart, args).expect("writing fmt now to uart"); if nl { uart.write_str("\r\n").expect("writing nl now to uart"); } }