summaryrefslogtreecommitdiffstats
path: root/ble/src/logger.rs
blob: f102b7491ec3663401b0c609fdb8806f6a7a3141 (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
118
119
120
121
122
123
124
125
126
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");
    }
}