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