opentitanlib/io/uart/
mod.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::time::Duration;
8
9use anyhow::Result;
10use clap::Args;
11use serde::{Deserialize, Serialize};
12pub use serialport::Parity;
13use thiserror::Error;
14
15use crate::app::TransportWrapper;
16use crate::impl_serializable_error;
17use crate::io::console::{ConsoleDevice, ConsoleExt};
18use crate::transport::TransportError;
19
20pub mod flow;
21pub mod serial;
22
23#[derive(Clone, Debug, Args, Serialize, Deserialize)]
24pub struct UartParams {
25    /// UART instance.
26    #[arg(long, default_value = "CONSOLE")]
27    uart: String,
28
29    /// UART baudrate.
30    #[arg(long)]
31    baudrate: Option<u32>,
32
33    /// Enable software flow control.
34    #[arg(long)]
35    flow_control: bool,
36}
37
38impl UartParams {
39    pub fn create(&self, transport: &TransportWrapper) -> Result<Rc<dyn Uart>> {
40        let uart = transport.uart(&self.uart)?;
41        if let Some(baudrate) = self.baudrate {
42            uart.set_baudrate(baudrate)?;
43        }
44        log::info!("set_flow_control to {}", self.flow_control);
45        uart.set_flow_control(self.flow_control)?;
46        Ok(uart)
47    }
48}
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
51#[repr(u8)]
52pub enum FlowControl {
53    // No flow control.
54    None = 0,
55    // Pause aka XOFF aka Ctrl-S ("Stop")
56    Pause = 19,
57    // Resume aka XON aka Ctrl-Q ("Quit Stopping")
58    Resume = 17,
59}
60
61/// A trait which represents a UART.
62pub trait Uart: ConsoleDevice {
63    /// Returns the UART baudrate.  May return zero for virtual UARTs.
64    fn get_baudrate(&self) -> Result<u32>;
65
66    /// Sets the UART baudrate.  May do nothing for virtual UARTs.
67    fn set_baudrate(&self, baudrate: u32) -> Result<()>;
68
69    // Returns whether software flow control is enabled for the UART `write`s.
70    fn get_flow_control(&self) -> Result<FlowControl> {
71        unimplemented!();
72    }
73
74    /// Enables software flow control for `write`s.
75    fn set_flow_control(&self, flow_control: bool) -> Result<()> {
76        if flow_control {
77            unimplemented!();
78        }
79        Ok(())
80    }
81
82    /// Returns `"/dev/ttyUSBn"` or similar OS device path usable by external programs for
83    /// directly accessing the serial port.
84    fn get_device_path(&self) -> Result<String> {
85        Err(TransportError::UnsupportedOperation.into())
86    }
87
88    /// Clears the UART RX buffer.
89    fn clear_rx_buffer(&self) -> Result<()> {
90        // Keep reading while until the RX buffer is empty.
91        // Note: This default implementation is overriden in backends where we can do better.
92        const TIMEOUT: Duration = Duration::from_millis(5);
93        let mut buf = [0u8; 256];
94        while self.read_timeout(&mut buf, TIMEOUT)? > 0 {}
95        Ok(())
96    }
97
98    fn set_parity(&self, _parity: Parity) -> Result<()> {
99        Err(TransportError::UnsupportedOperation.into())
100    }
101
102    fn get_parity(&self) -> Result<Parity> {
103        Err(TransportError::UnsupportedOperation.into())
104    }
105}
106
107impl Read for &dyn Uart {
108    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
109        ConsoleExt::read(&**self, buf).map_err(io::Error::other)
110    }
111}
112
113/// Errors related to the UART interface.
114#[derive(Error, Debug, Serialize, Deserialize)]
115pub enum UartError {
116    #[error("Enumerating: {0}")]
117    EnumerationError(String),
118    #[error("Opening: {0}")]
119    OpenError(String),
120    #[error("Invalid option: {0}")]
121    InvalidOption(String),
122    #[error("Invalid speed: {0}")]
123    InvalidSpeed(u32),
124    #[error("Reading: {0}")]
125    ReadError(String),
126    #[error("Writing: {0}")]
127    WriteError(String),
128    #[error("{0}")]
129    GenericError(String),
130}
131impl_serializable_error!(UartError);