components/
console.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 2022.
4
5//! Components for Console and ConsoleOrdered.
6//!
7//! These are two alternative implementations of the serial console
8//! system call interface. Console allows prints of arbitrary length
9//! but does not have ordering or atomicity
10//! guarantees. ConsoleOrdered, in contrast, has limits on the maximum
11//! lengths of prints but provides a temporal ordering and ensures a
12//! print is atomic at least up to particular length (typically 200
13//! bytes). Console is useful when userspace is printing large
14//! messages. ConsoleOrdered is useful when you are debugging and
15//! there are inter-related messages from the kernel and userspace,
16//! whose ordering is important to maintain.
17//!
18//!
19//! This provides three Components, `ConsoleComponent` and
20//! `ConsoleOrderedComponent`, which implement a buffered read/write
21//! console over a serial port, and `UartMuxComponent`, which provides
22//! multiplexed access to hardware UART. As an example, the serial
23//! port used for console on Imix is typically USART3 (the DEBUG USB
24//! connector).
25//!
26//! Usage
27//! -----
28//! ```rust
29//! let uart_mux = UartMuxComponent::new(&sam4l::usart::USART3,
30//!                                      115200,
31//!                                      deferred_caller).finalize(components::uart_mux_component_static!());
32//! let console = ConsoleComponent::new(board_kernel, uart_mux)
33//!    .finalize(console_component_static!());
34//! ```
35// Author: Philip Levis <pal@cs.stanford.edu>
36// Last modified: 1/08/2023
37
38use capsules_core::console;
39use capsules_core::console_ordered::ConsoleOrdered;
40
41use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
42use capsules_core::virtualizers::virtual_uart::{MuxUart, UartDevice};
43use core::mem::MaybeUninit;
44use kernel::capabilities;
45use kernel::component::Component;
46use kernel::create_capability;
47use kernel::hil;
48use kernel::hil::time::{self, Alarm};
49use kernel::hil::uart;
50
51use capsules_core::console::DEFAULT_BUF_SIZE;
52
53#[macro_export]
54macro_rules! uart_mux_component_static {
55    // Common logic for both branches
56    ($rx_buffer_len: expr) => {{
57        use capsules_core::virtualizers::virtual_uart::MuxUart;
58        use kernel::static_buf;
59        let uart_mux = static_buf!(MuxUart<'static>);
60        let rx_buf = static_buf!([u8; $rx_buffer_len]);
61        (uart_mux, rx_buf)
62    }};
63    () => {
64        $crate::uart_mux_component_static!(capsules_core::virtualizers::virtual_uart::RX_BUF_LEN);
65    };
66    ($rx_buffer_len: literal) => {
67        $crate::uart_mux_component_static!($rx_buffer_len);
68    };
69}
70
71pub struct UartMuxComponent<const RX_BUF_LEN: usize> {
72    uart: &'static dyn uart::Uart<'static>,
73    baud_rate: u32,
74}
75
76impl<const RX_BUF_LEN: usize> UartMuxComponent<RX_BUF_LEN> {
77    pub fn new(
78        uart: &'static dyn uart::Uart<'static>,
79        baud_rate: u32,
80    ) -> UartMuxComponent<RX_BUF_LEN> {
81        UartMuxComponent { uart, baud_rate }
82    }
83}
84
85impl<const RX_BUF_LEN: usize> Component for UartMuxComponent<RX_BUF_LEN> {
86    type StaticInput = (
87        &'static mut MaybeUninit<MuxUart<'static>>,
88        &'static mut MaybeUninit<[u8; RX_BUF_LEN]>,
89    );
90    type Output = &'static MuxUart<'static>;
91
92    fn finalize(self, s: Self::StaticInput) -> Self::Output {
93        let rx_buf = s.1.write([0; RX_BUF_LEN]);
94        let uart_mux = s.0.write(MuxUart::new(self.uart, rx_buf, self.baud_rate));
95        kernel::deferred_call::DeferredCallClient::register(uart_mux);
96
97        uart_mux.initialize();
98        hil::uart::Transmit::set_transmit_client(self.uart, uart_mux);
99        hil::uart::Receive::set_receive_client(self.uart, uart_mux);
100
101        uart_mux
102    }
103}
104
105#[macro_export]
106macro_rules! console_component_static {
107    // Common logic for both branches
108    ($rx_buffer_len: expr, $tx_buffer_len: expr) => {{
109        use capsules_core::console::{Console, DEFAULT_BUF_SIZE};
110        use capsules_core::virtualizers::virtual_uart::UartDevice;
111        use kernel::static_buf;
112        let read_buf = static_buf!([u8; $rx_buffer_len]);
113        let write_buf = static_buf!([u8; $tx_buffer_len]);
114        // Create virtual device for console.
115        let console_uart = static_buf!(UartDevice);
116        let console = static_buf!(Console<'static>);
117        (write_buf, read_buf, console_uart, console)
118    }};
119    () => {
120        $crate::console_component_static!(DEFAULT_BUF_SIZE, DEFAULT_BUF_SIZE);
121    };
122    ($rx_buffer_len: literal, $tx_buffer_len: literal) => {
123        $crate::console_component_static!($rx_buffer_len, $tx_buffer_len);
124    };
125}
126
127pub struct ConsoleComponent<const RX_BUF_LEN: usize, const TX_BUF_LEN: usize> {
128    board_kernel: &'static kernel::Kernel,
129    driver_num: usize,
130    uart_mux: &'static MuxUart<'static>,
131}
132
133impl<const RX_BUF_LEN: usize, const TX_BUF_LEN: usize> ConsoleComponent<RX_BUF_LEN, TX_BUF_LEN> {
134    pub fn new(
135        board_kernel: &'static kernel::Kernel,
136        driver_num: usize,
137        uart_mux: &'static MuxUart,
138    ) -> ConsoleComponent<RX_BUF_LEN, TX_BUF_LEN> {
139        ConsoleComponent {
140            board_kernel,
141            driver_num,
142            uart_mux,
143        }
144    }
145}
146
147impl<const RX_BUF_LEN: usize, const TX_BUF_LEN: usize> Component
148    for ConsoleComponent<RX_BUF_LEN, TX_BUF_LEN>
149{
150    type StaticInput = (
151        &'static mut MaybeUninit<[u8; TX_BUF_LEN]>,
152        &'static mut MaybeUninit<[u8; RX_BUF_LEN]>,
153        &'static mut MaybeUninit<UartDevice<'static>>,
154        &'static mut MaybeUninit<console::Console<'static>>,
155    );
156    type Output = &'static console::Console<'static>;
157
158    fn finalize(self, s: Self::StaticInput) -> Self::Output {
159        let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
160
161        let write_buffer = s.0.write([0; TX_BUF_LEN]);
162
163        let read_buffer = s.1.write([0; RX_BUF_LEN]);
164
165        let console_uart = s.2.write(UartDevice::new(self.uart_mux, true));
166        console_uart.setup();
167
168        let console = s.3.write(console::Console::new(
169            console_uart,
170            write_buffer,
171            read_buffer,
172            self.board_kernel.create_grant(self.driver_num, &grant_cap),
173        ));
174        hil::uart::Transmit::set_transmit_client(console_uart, console);
175        hil::uart::Receive::set_receive_client(console_uart, console);
176
177        console
178    }
179}
180#[macro_export]
181macro_rules! console_ordered_component_static {
182    ($A:ty $(,)?) => {{
183        let mux_alarm = kernel::static_buf!(VirtualMuxAlarm<'static, $A>);
184        let read_buf = static_buf!([u8; capsules_core::console::DEFAULT_BUF_SIZE]);
185        let console_uart =
186            kernel::static_buf!(capsules_core::virtualizers::virtual_uart::UartDevice);
187        let console = kernel::static_buf!(ConsoleOrdered<'static, VirtualMuxAlarm<'static, $A>>);
188        (mux_alarm, read_buf, console_uart, console)
189    };};
190}
191
192pub struct ConsoleOrderedComponent<A: 'static + time::Alarm<'static>> {
193    board_kernel: &'static kernel::Kernel,
194    driver_num: usize,
195    uart_mux: &'static MuxUart<'static>,
196    alarm_mux: &'static MuxAlarm<'static, A>,
197    atomic_size: usize,
198    retry_timer: u32,
199    write_timer: u32,
200}
201
202impl<A: 'static + time::Alarm<'static>> ConsoleOrderedComponent<A> {
203    pub fn new(
204        board_kernel: &'static kernel::Kernel,
205        driver_num: usize,
206        uart_mux: &'static MuxUart<'static>,
207        alarm_mux: &'static MuxAlarm<'static, A>,
208        atomic_size: usize,
209        retry_timer: u32,
210        write_timer: u32,
211    ) -> ConsoleOrderedComponent<A> {
212        ConsoleOrderedComponent {
213            board_kernel,
214            driver_num,
215            uart_mux,
216            alarm_mux,
217            atomic_size,
218            retry_timer,
219            write_timer,
220        }
221    }
222}
223
224impl<A: 'static + time::Alarm<'static>> Component for ConsoleOrderedComponent<A> {
225    type StaticInput = (
226        &'static mut MaybeUninit<VirtualMuxAlarm<'static, A>>,
227        &'static mut MaybeUninit<[u8; DEFAULT_BUF_SIZE]>,
228        &'static mut MaybeUninit<UartDevice<'static>>,
229        &'static mut MaybeUninit<ConsoleOrdered<'static, VirtualMuxAlarm<'static, A>>>,
230    );
231    type Output = &'static ConsoleOrdered<'static, VirtualMuxAlarm<'static, A>>;
232
233    fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
234        let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
235
236        let virtual_alarm1 = static_buffer.0.write(VirtualMuxAlarm::new(self.alarm_mux));
237        virtual_alarm1.setup();
238
239        let read_buffer = static_buffer.1.write([0; DEFAULT_BUF_SIZE]);
240
241        let console_uart = static_buffer.2.write(UartDevice::new(self.uart_mux, true));
242        console_uart.setup();
243
244        let console = static_buffer.3.write(ConsoleOrdered::new(
245            console_uart,
246            virtual_alarm1,
247            read_buffer,
248            self.board_kernel.create_grant(self.driver_num, &grant_cap),
249            self.atomic_size,
250            self.retry_timer,
251            self.write_timer,
252        ));
253
254        virtual_alarm1.set_alarm_client(console);
255        hil::uart::Receive::set_receive_client(console_uart, console);
256        console
257    }
258}