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