summaryrefslogtreecommitdiffstats
path: root/ble/src/logger.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ble/src/logger.rs')
-rw-r--r--ble/src/logger.rs127
1 files changed, 127 insertions, 0 deletions
diff --git a/ble/src/logger.rs b/ble/src/logger.rs
new file mode 100644
index 0000000..f102b74
--- /dev/null
+++ b/ble/src/logger.rs
@@ -0,0 +1,127 @@
+use crate::rtc;
+
+use core::cell::UnsafeCell;
+use core::fmt::{self, Write};
+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<u8>) -> 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> {
+ for b in s.bytes() {
+ if let Err(_) = self.lbw.unshift(b) {
+ // Ignore buffer full errors for logging.
+ return Ok(());
+ }
+ }
+ Ok(())
+ }
+}
+
+static mut LB: RingBuffer<u8> = RingBuffer::<u8>::new(0);
+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: W,
+}
+impl<W> WriteWrapper<W> {
+ pub fn new(writer: W) -> Self {
+ Self { w: writer }
+ }
+}
+unsafe impl<W> Sync for WriteWrapper<W> {}
+
+pub struct SerialLogger<W> {
+ writer: UnsafeCell<WriteWrapper<Uarte<W>>>,
+}
+
+impl<W> SerialLogger<W>
+where
+ W: uarte::Instance,
+{
+ pub fn new(writer: WriteWrapper<Uarte<W>>) -> 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<W> Send for SerialLogger<W> {}
+unsafe impl<W> Sync for SerialLogger<W> {}
+
+impl<W> log::Log for SerialLogger<W>
+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 };
+ write!(
+ jrb,
+ "[{}] {} {} -- {}\r\n",
+ rtc::millis(),
+ record.level(),
+ record.target(),
+ record.args()
+ )
+ .ok();
+ }
+
+ fn flush(&self) {
+ // Unsafe due to mutable static. We can only deal with the
+ // tail position of the buffer here to keep things safe.
+ let jrb = unsafe { &mut JRB };
+ if jrb.lbr.is_empty() {
+ return;
+ }
+
+ let writer = unsafe { &mut (*self.writer.get()) };
+ while let Some(b) = jrb.lbr.shift() {
+ // TODO: The UARTE peripheral uses DMA to send a bunch of
+ // stuff at speed, in hardware. It would be nice to use
+ // it, but we can't just take a slice of the ring buffer,
+ // since we don't know about its internal structure here.
+ writer.w.write(&[b]).expect("writing log");
+ }
+ }
+}
+
+// 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<UARTE0> = 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");
+ }
+}