esp_println/
defmt.rs

1//! defmt global logger implementation.
2// Implementation taken from defmt-rtt, with a custom framing prefix
3
4#[cfg(feature = "critical-section")]
5use critical_section::RestoreState;
6
7use super::{LockToken, PrinterImpl};
8
9/// Global logger lock.
10#[cfg(feature = "critical-section")]
11static mut TAKEN: bool = false;
12#[cfg(feature = "critical-section")]
13static mut CS_RESTORE: RestoreState = RestoreState::invalid();
14static mut ENCODER: defmt::Encoder = defmt::Encoder::new();
15
16#[defmt::global_logger]
17pub struct Logger;
18unsafe impl defmt::Logger for Logger {
19    fn acquire() {
20        #[cfg(feature = "critical-section")]
21        unsafe {
22            // safety: Must be paired with corresponding call to release(), see below
23            let restore = critical_section::acquire();
24
25            // safety: accessing the `static mut` is OK because we have acquired a critical
26            // section.
27            if TAKEN {
28                panic!("defmt logger taken reentrantly")
29            }
30
31            // safety: accessing the `static mut` is OK because we have acquired a critical
32            // section.
33            TAKEN = true;
34
35            // safety: accessing the `static mut` is OK because we have acquired a critical
36            // section.
37            CS_RESTORE = restore;
38        }
39
40        // If not disabled, write a non-UTF8 sequence to indicate the start of a defmt
41        // frame. We need this to distinguish defmt frames from other data that
42        // might be written to the printer.
43        do_write(&[0xFF, 0x00]);
44
45        // safety: accessing the `static mut` is OK because we have acquired a critical
46        // section.
47        unsafe { ENCODER.start_frame(do_write) }
48    }
49
50    unsafe fn release() {
51        unsafe {
52            // safety: accessing the `static mut` is OK because we have acquired a critical
53            // section.
54            ENCODER.end_frame(do_write);
55
56            Self::flush();
57
58            #[cfg(feature = "critical-section")]
59            {
60                // We don't need to write a custom end-of-frame sequence because:
61                //  - using `defmt`, the rzcobs encoding already includes a terminating zero
62                //  - using `defmt-raw`, we don't add any additional framing data
63
64                // safety: accessing the `static mut` is OK because we have acquired a critical
65                // section.
66                TAKEN = false;
67
68                // safety: accessing the `static mut` is OK because we have acquired a critical
69                // section.
70                let restore = CS_RESTORE;
71
72                // safety: Must be paired with corresponding call to acquire(), see above
73                critical_section::release(restore);
74            }
75        }
76    }
77
78    unsafe fn flush() {
79        let token = unsafe {
80            // Safety: the implementation ensures this is only called in a critical
81            // section.
82            LockToken::conjure()
83        };
84        PrinterImpl::flush(token);
85    }
86
87    unsafe fn write(bytes: &[u8]) {
88        unsafe {
89            // safety: accessing the `static mut` is OK because we have acquired a critical
90            // section.
91            ENCODER.write(bytes, do_write);
92        }
93    }
94}
95
96fn do_write(bytes: &[u8]) {
97    let token = unsafe {
98        // Safety: the above implementation ensures this is only called in a critical
99        // section.
100        LockToken::conjure()
101    };
102    PrinterImpl::write_bytes_in_cs(bytes, token)
103}
104
105#[cfg(feature = "timestamp")]
106defmt::timestamp!("{=u64:ms}", {
107    unsafe extern "Rust" {
108        fn _esp_println_timestamp() -> u64;
109    }
110    unsafe { _esp_println_timestamp() }
111});