summaryrefslogtreecommitdiffstats
path: root/ble/src/logger.rs
blob: b4167b2ba61418227e5d75d07c0ca891534997b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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<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> {
        // 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<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) {
        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")
        }
    }
}

// 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");
    }
}