capsules_system/debug_writer/
uart_debug_writer.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5//! Implementation of [`DebugWriter`] using UART.
6
7use kernel::collections::queue::Queue;
8use kernel::collections::ring_buffer::RingBuffer;
9use kernel::debug::DebugWriter;
10use kernel::debug::IoWrite;
11use kernel::hil;
12use kernel::utilities::cells::TakeCell;
13use kernel::ErrorCode;
14
15/// Buffered [`DebugWriter`] implementation using a UART.
16///
17/// Currently used as a default implementation of DebugWriterComponent.
18pub struct UartDebugWriter {
19    /// What provides the actual writing mechanism.
20    uart: &'static dyn hil::uart::Transmit<'static>,
21    /// The buffer that is passed to the writing mechanism.
22    output_buffer: TakeCell<'static, [u8]>,
23    /// An internal buffer that is used to hold debug!() calls as they come in.
24    internal_buffer: TakeCell<'static, RingBuffer<'static, u8>>,
25}
26
27impl UartDebugWriter {
28    pub fn new(
29        uart: &'static dyn hil::uart::Transmit,
30        out_buffer: &'static mut [u8],
31        internal_buffer: &'static mut RingBuffer<'static, u8>,
32    ) -> UartDebugWriter {
33        UartDebugWriter {
34            uart,
35            output_buffer: TakeCell::new(out_buffer),
36            internal_buffer: TakeCell::new(internal_buffer),
37        }
38    }
39}
40
41impl DebugWriter for UartDebugWriter {
42    fn write(&self, bytes: &[u8], overflow_message: &[u8]) -> usize {
43        // If we have a buffer, write to it.
44        if let Some(ring_buffer) = self.internal_buffer.take() {
45            let available_len = ring_buffer
46                .available_len()
47                .saturating_sub(overflow_message.len());
48            let total_written = if available_len >= bytes.len() {
49                for &b in bytes {
50                    ring_buffer.enqueue(b);
51                }
52                bytes.len()
53            } else {
54                // If we don't have enough space, write as much as we can and
55                // then write the overflow message.
56                for &b in &bytes[..available_len] {
57                    ring_buffer.enqueue(b);
58                }
59                for &b in overflow_message {
60                    ring_buffer.enqueue(b);
61                }
62                available_len + overflow_message.len()
63            };
64            // Put the buffer back.
65            self.internal_buffer.replace(ring_buffer);
66            total_written
67        } else {
68            // No buffer, so just return the number of bytes.
69            bytes.len()
70        }
71    }
72
73    fn publish(&self) -> usize {
74        // Can only publish if we have the output_buffer. If we don't that is
75        // fine, we will do it when the transmit done callback happens.
76        self.internal_buffer.map_or(0, |ring_buffer| {
77            if let Some(out_buffer) = self.output_buffer.take() {
78                let mut count = 0;
79
80                for dst in out_buffer.iter_mut() {
81                    match ring_buffer.dequeue() {
82                        Some(src) => {
83                            *dst = src;
84                            count += 1;
85                        }
86                        None => {
87                            break;
88                        }
89                    }
90                }
91
92                if count != 0 {
93                    // Transmit the data in the output buffer.
94                    if let Err((_err, buf)) = self.uart.transmit_buffer(out_buffer, count) {
95                        self.output_buffer.put(Some(buf));
96                    } else {
97                        self.output_buffer.put(None);
98                    }
99                }
100                count
101            } else {
102                0
103            }
104        })
105    }
106
107    fn flush(&self, writer: &mut dyn IoWrite) {
108        self.internal_buffer.map(|ring_buffer| {
109            writer.write_ring_buffer(ring_buffer);
110        });
111    }
112
113    fn available_len(&self) -> usize {
114        self.internal_buffer.map_or(0, |rb| rb.available_len())
115    }
116
117    fn to_write_len(&self) -> usize {
118        self.internal_buffer.map_or(0, |rb| rb.len())
119    }
120}
121
122impl hil::uart::TransmitClient for UartDebugWriter {
123    fn transmitted_buffer(
124        &self,
125        buffer: &'static mut [u8],
126        _tx_len: usize,
127        _rcode: Result<(), ErrorCode>,
128    ) {
129        // Replace this buffer since we are done with it.
130        self.output_buffer.replace(buffer);
131
132        if self.internal_buffer.map_or(false, |buf| buf.has_elements()) {
133            // Buffer not empty, go around again
134            self.publish();
135        }
136    }
137    fn transmitted_word(&self, _rcode: Result<(), ErrorCode>) {}
138}
139
140impl IoWrite for UartDebugWriter {
141    fn write(&mut self, bytes: &[u8]) -> usize {
142        const FULL_MSG: &[u8] = b"\n*** DEBUG BUFFER FULL ***\n";
143        self.internal_buffer.map_or(0, |ring_buffer| {
144            let available_len_for_msg = ring_buffer.available_len().saturating_sub(FULL_MSG.len());
145
146            if available_len_for_msg >= bytes.len() {
147                for &b in bytes {
148                    ring_buffer.enqueue(b);
149                }
150                bytes.len()
151            } else {
152                for &b in &bytes[..available_len_for_msg] {
153                    ring_buffer.enqueue(b);
154                }
155                // When the buffer is close to full, print a warning and drop the current
156                // string.
157                for &b in FULL_MSG {
158                    ring_buffer.enqueue(b);
159                }
160                available_len_for_msg
161            }
162        })
163    }
164}