opentitanlib/io/
uart.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::io::{self, Read};
6use std::rc::Rc;
7use std::task::{Context, Poll};
8use std::time::Duration;
9
10use anyhow::Result;
11use clap::Args;
12use serde::{Deserialize, Serialize};
13pub use serialport::Parity;
14use thiserror::Error;
15
16use crate::app::TransportWrapper;
17use crate::impl_serializable_error;
18use crate::io::console::ConsoleDevice;
19use crate::transport::TransportError;
20
21#[derive(Clone, Debug, Args, Serialize, Deserialize)]
22pub struct UartParams {
23    /// UART instance.
24    #[arg(long, default_value = "CONSOLE")]
25    uart: String,
26
27    /// UART baudrate.
28    #[arg(long)]
29    baudrate: Option<u32>,
30
31    /// Enable software flow control.
32    #[arg(long)]
33    flow_control: bool,
34}
35
36impl UartParams {
37    pub fn create(&self, transport: &TransportWrapper) -> Result<Rc<dyn Uart>> {
38        let uart = transport.uart(&self.uart)?;
39        if let Some(baudrate) = self.baudrate {
40            uart.set_baudrate(baudrate)?;
41        }
42        log::info!("set_flow_control to {}", self.flow_control);
43        uart.set_flow_control(self.flow_control)?;
44        Ok(uart)
45    }
46}
47
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
49#[repr(u8)]
50pub enum FlowControl {
51    // No flow control.
52    None = 0,
53    // Pause aka XOFF aka Ctrl-S ("Stop")
54    Pause = 19,
55    // Resume aka XON aka Ctrl-Q ("Quit Stopping")
56    Resume = 17,
57}
58
59/// A trait which represents a UART.
60pub trait Uart {
61    /// Returns the UART baudrate.  May return zero for virtual UARTs.
62    fn get_baudrate(&self) -> Result<u32>;
63
64    /// Sets the UART baudrate.  May do nothing for virtual UARTs.
65    fn set_baudrate(&self, baudrate: u32) -> Result<()>;
66
67    // Returns whether software flow control is enabled for the UART `write`s.
68    fn get_flow_control(&self) -> Result<FlowControl> {
69        unimplemented!();
70    }
71
72    /// Enables software flow control for `write`s.
73    fn set_flow_control(&self, flow_control: bool) -> Result<()> {
74        if flow_control {
75            unimplemented!();
76        }
77        Ok(())
78    }
79
80    /// Returns `"/dev/ttyUSBn"` or similar OS device path usable by external programs for
81    /// directly accessing the serial port.
82    fn get_device_path(&self) -> Result<String> {
83        Err(TransportError::UnsupportedOperation.into())
84    }
85
86    /// Reads UART receive data into `buf`, returning the number of bytes read.
87    /// This function is blocking.
88    fn read(&self, buf: &mut [u8]) -> Result<usize> {
89        crate::util::runtime::block_on(std::future::poll_fn(|cx| self.poll_read(cx, buf)))
90    }
91
92    /// Reads UART receive data into `buf`, returning the number of bytes read.
93    /// The `timeout` may be used to specify a duration to wait for data.
94    /// If timeout expires without any data arriving `Ok(0)` will be returned, never `Err(_)`.
95    fn read_timeout(&self, buf: &mut [u8], timeout: Duration) -> Result<usize> {
96        crate::util::runtime::block_on(async {
97            tokio::time::timeout(timeout, std::future::poll_fn(|cx| self.poll_read(cx, buf))).await
98        })
99        .unwrap_or(Ok(0))
100    }
101
102    /// Reads UART receive data into `buf`, returning the number of bytes read.
103    ///
104    /// If data is not yet ready, `Poll::Pending` will be returned and `cx` would be notified when it's available.
105    /// When this function is called with multiple wakers, all wakers should be notified instead of just the last one.
106    fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>>;
107
108    /// Writes data from `buf` to the UART.
109    fn write(&self, buf: &[u8]) -> Result<()>;
110
111    /// Clears the UART RX buffer.
112    fn clear_rx_buffer(&self) -> Result<()> {
113        // Keep reading while until the RX buffer is empty.
114        // Note: This default implementation is overriden in backends where we can do better.
115        const TIMEOUT: Duration = Duration::from_millis(5);
116        let mut buf = [0u8; 256];
117        while self.read_timeout(&mut buf, TIMEOUT)? > 0 {}
118        Ok(())
119    }
120
121    fn set_break(&self, _enable: bool) -> Result<()> {
122        Err(TransportError::UnsupportedOperation.into())
123    }
124
125    fn set_parity(&self, _parity: Parity) -> Result<()> {
126        Err(TransportError::UnsupportedOperation.into())
127    }
128
129    fn get_parity(&self) -> Result<Parity> {
130        Err(TransportError::UnsupportedOperation.into())
131    }
132}
133
134impl Read for &dyn Uart {
135    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
136        Uart::read(&**self, buf).map_err(io::Error::other)
137    }
138}
139
140impl<'a> ConsoleDevice for dyn Uart + 'a {
141    fn console_poll_read(
142        &self,
143        cx: &mut std::task::Context<'_>,
144        buf: &mut [u8],
145    ) -> std::task::Poll<Result<usize>> {
146        self.poll_read(cx, buf)
147    }
148
149    fn console_write(&self, buf: &[u8]) -> Result<()> {
150        self.write(buf)
151    }
152
153    fn set_break(&self, enable: bool) -> Result<()> {
154        <Self as Uart>::set_break(self, enable)
155    }
156}
157
158/// Errors related to the UART interface.
159#[derive(Error, Debug, Serialize, Deserialize)]
160pub enum UartError {
161    #[error("Enumerating: {0}")]
162    EnumerationError(String),
163    #[error("Opening: {0}")]
164    OpenError(String),
165    #[error("Invalid option: {0}")]
166    InvalidOption(String),
167    #[error("Invalid speed: {0}")]
168    InvalidSpeed(u32),
169    #[error("Reading: {0}")]
170    ReadError(String),
171    #[error("Writing: {0}")]
172    WriteError(String),
173    #[error("{0}")]
174    GenericError(String),
175}
176impl_serializable_error!(UartError);