#![no_std]
use core::sync::atomic::{AtomicBool, Ordering};
use defmt::global_logger;
static mut ENCODER: defmt::Encoder = defmt::Encoder::new();
static TAKEN: AtomicBool = AtomicBool::new(false);
static mut CS_RESTORE: critical_section::RestoreState = critical_section::RestoreState::invalid();
type WriteCB = unsafe fn(SFn);
static mut WRITEFN: Option<WriteCB> = None;
enum SFn<'a> {
Buf(&'a [u8]),
Flush,
}
unsafe fn trampoline<F>(buf: SFn)
where
F: FnMut(SFn),
{
if let Some(wfn) = WRITEFN {
let wfn = &mut *(wfn as *mut F);
wfn(buf);
}
}
fn get_trampoline<F>(_closure: &F) -> WriteCB
where
F: FnMut(SFn),
{
trampoline::<F>
}
pub fn defmt_serial(serial: impl embedded_hal::blocking::serial::Write<u8> + 'static) {
let mut serial = core::mem::ManuallyDrop::new(serial);
let wfn = move |a: SFn| {
match a {
SFn::Buf(buf) => {
for b in buf {
serial.bwrite_all(&b.to_ne_bytes()).ok();
}
}
SFn::Flush => {
serial.bflush().ok();
}
};
};
let trampoline = get_trampoline(&wfn);
unsafe {
let token = critical_section::acquire();
WRITEFN = Some(trampoline);
critical_section::release(token);
}
}
#[global_logger]
struct GlobalSerialLogger;
unsafe impl defmt::Logger for GlobalSerialLogger {
fn acquire() {
let restore = unsafe { critical_section::acquire() };
if TAKEN.load(Ordering::Relaxed) {
panic!("defmt logger taken reentrantly");
}
TAKEN.store(true, Ordering::Relaxed);
unsafe {
CS_RESTORE = restore;
}
unsafe { ENCODER.start_frame(write_serial) }
}
unsafe fn release() {
ENCODER.end_frame(write_serial);
TAKEN.store(false, Ordering::Relaxed);
let restore = CS_RESTORE;
critical_section::release(restore);
}
unsafe fn write(bytes: &[u8]) {
ENCODER.write(bytes, write_serial);
}
unsafe fn flush() {
if let Some(wfn) = WRITEFN {
wfn(SFn::Flush);
}
}
}
fn write_serial(remaining: &[u8]) {
unsafe {
if let Some(wfn) = WRITEFN {
wfn(SFn::Buf(remaining));
}
}
}