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