opentitanlib/io/uart/
serial.rs1use std::cell::{Cell, RefCell};
6use std::io::{ErrorKind, Read, Write};
7use std::os::fd::{AsRawFd, BorrowedFd};
8use std::task::{Context, Poll, ready};
9use std::time::Duration;
10
11use anyhow::{Context as _, Result};
12use serialport::{ClearBuffer, SerialPort, TTYPort};
13use tokio::io::unix::AsyncFd;
14
15use super::{Parity, Uart, UartError};
16use crate::io::console::{ConsoleDevice, ConsoleExt};
17use crate::util;
18use crate::util::runtime::MultiWaker;
19
20pub struct SerialPortUart {
22 port_name: String,
23 port: RefCell<AsyncFd<TTYPort>>,
24 pseudo_baud: Cell<u32>,
25 multi_waker: MultiWaker,
26}
27
28impl SerialPortUart {
29 const FOREVER: Duration = Duration::from_secs(100 * 365 * 86400);
35
36 pub fn open(port_name: &str, baud: u32) -> Result<Self> {
38 let port = TTYPort::open(&serialport::new(port_name, baud).preserve_dtr_on_open())
39 .map_err(|e| UartError::OpenError(e.to_string()))?;
40 let _runtime_guard = crate::util::runtime().enter();
41 let port = AsyncFd::new(port)?;
42 flock_serial(port.get_ref(), port_name)?;
43 Ok(SerialPortUart {
44 port_name: port_name.to_string(),
45 port: RefCell::new(port),
46 pseudo_baud: Cell::new(0),
47 multi_waker: MultiWaker::new(),
48 })
49 }
50
51 pub fn open_pseudo(port_name: &str, baud: u32) -> Result<Self> {
53 let port = TTYPort::open(&serialport::new(port_name, baud).preserve_dtr_on_open())
54 .map_err(|e| UartError::OpenError(e.to_string()))?;
55 let _runtime_guard = crate::util::runtime().enter();
56 let port = AsyncFd::new(port)?;
57 flock_serial(port.get_ref(), port_name)?;
58 Ok(SerialPortUart {
59 port_name: port_name.to_string(),
60 port: RefCell::new(port),
61 pseudo_baud: Cell::new(baud),
62 multi_waker: MultiWaker::new(),
63 })
64 }
65}
66
67impl ConsoleDevice for SerialPortUart {
68 fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>> {
69 let mut port = self.port.borrow_mut();
70
71 loop {
72 let mut guard = ready!(
73 self.multi_waker
74 .poll_with(cx, |cx| port.poll_read_ready_mut(cx))
75 )?;
76
77 match guard.try_io(|inner| {
78 inner.get_mut().set_timeout(Duration::ZERO)?;
79 let result = match inner.get_mut().read(buf) {
80 Ok(n) => Ok(n),
81 Err(ioerr) if ioerr.kind() == ErrorKind::TimedOut => {
82 Err(std::io::Error::new(std::io::ErrorKind::WouldBlock, ioerr))
83 }
84 Err(ioerr) => Err(ioerr)?,
85 };
86 inner.get_mut().set_timeout(Self::FOREVER)?;
87 result
88 }) {
89 Ok(result) => return Poll::Ready(Ok(result?)),
90 Err(_would_block) => continue,
91 }
92 }
93 }
94
95 fn write(&self, buf: &[u8]) -> Result<()> {
97 let mut port = self.port.borrow_mut();
100 let mut idx = 0;
101 while idx < buf.len() {
102 match port.get_mut().write(&buf[idx..]) {
103 Ok(n) => idx += n,
104 Err(ioerr) if ioerr.kind() == ErrorKind::TimedOut => {
105 util::file::wait_timeout(
109 unsafe { BorrowedFd::borrow_raw(port.as_raw_fd()) },
111 rustix::event::PollFlags::OUT,
112 Duration::from_secs(5),
113 )?;
114 }
115 Err(ioerr) => return Err(ioerr).context("UART communication error"),
116 }
117 }
118
119 Ok(())
120 }
121}
122
123impl Uart for SerialPortUart {
124 fn get_baudrate(&self) -> Result<u32> {
126 let pseudo = self.pseudo_baud.get();
127 if pseudo == 0 {
128 self.port
129 .borrow()
130 .get_ref()
131 .baud_rate()
132 .context("getting baudrate")
133 } else {
134 Ok(pseudo)
135 }
136 }
137
138 fn set_baudrate(&self, baudrate: u32) -> Result<()> {
140 let pseudo = self.pseudo_baud.get();
141 if pseudo == 0 {
142 self.port
143 .borrow_mut()
144 .get_mut()
145 .set_baud_rate(baudrate)
146 .map_err(|_| UartError::InvalidSpeed(baudrate))?;
147 } else {
148 self.pseudo_baud.set(baudrate);
149 }
150 Ok(())
151 }
152
153 fn get_device_path(&self) -> Result<String> {
154 Ok(self.port_name.clone())
155 }
156
157 fn set_parity(&self, parity: Parity) -> Result<()> {
158 self.port.borrow_mut().get_mut().set_parity(parity)?;
159 Ok(())
160 }
161
162 fn clear_rx_buffer(&self) -> Result<()> {
164 self.port.borrow_mut().get_mut().clear(ClearBuffer::Input)?;
165
166 const TIMEOUT: Duration = Duration::from_millis(5);
176 let mut buf = [0u8; 256];
177 while self.read_timeout(&mut buf, TIMEOUT)? > 0 {}
178 Ok(())
179 }
180
181 fn set_break(&self, enable: bool) -> Result<()> {
182 let mut port = self.port.borrow_mut();
183 if enable {
184 port.get_mut().set_break()?;
185 } else {
186 port.get_mut().clear_break()?;
187 }
188 Ok(())
189 }
190}
191
192pub fn flock_serial(port: &TTYPort, port_name: &str) -> Result<()> {
195 let fd = unsafe { BorrowedFd::borrow_raw(port.as_raw_fd()) };
197 rustix::fs::flock(fd, rustix::fs::FlockOperation::NonBlockingLockExclusive)
198 .map_err(|_| UartError::OpenError(format!("Device {port_name} is locked")))?;
199 Ok(())
200}