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 open_baud = if cfg!(target_os = "macos") { 0 } else { baud };
55 let port = TTYPort::open(&serialport::new(port_name, open_baud).preserve_dtr_on_open())
56 .map_err(|e| UartError::OpenError(e.to_string()))?;
57 let _runtime_guard = crate::util::runtime().enter();
58 let port = AsyncFd::new(port)?;
59 flock_serial(port.get_ref(), port_name)?;
60 Ok(SerialPortUart {
61 port_name: port_name.to_string(),
62 port: RefCell::new(port),
63 pseudo_baud: Cell::new(baud),
64 multi_waker: MultiWaker::new(),
65 })
66 }
67}
68
69impl ConsoleDevice for SerialPortUart {
70 fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>> {
71 let mut port = self.port.borrow_mut();
72
73 loop {
74 let mut guard = ready!(
75 self.multi_waker
76 .poll_with(cx, |cx| port.poll_read_ready_mut(cx))
77 )?;
78
79 match guard.try_io(|inner| {
80 inner.get_mut().set_timeout(Duration::ZERO)?;
81 let result = match inner.get_mut().read(buf) {
82 Ok(n) => Ok(n),
83 Err(ioerr) if ioerr.kind() == ErrorKind::TimedOut => {
84 Err(std::io::Error::new(std::io::ErrorKind::WouldBlock, ioerr))
85 }
86 Err(ioerr) => Err(ioerr)?,
87 };
88 inner.get_mut().set_timeout(Self::FOREVER)?;
89 result
90 }) {
91 Ok(result) => return Poll::Ready(Ok(result?)),
92 Err(_would_block) => continue,
93 }
94 }
95 }
96
97 fn write(&self, buf: &[u8]) -> Result<()> {
99 let mut port = self.port.borrow_mut();
102 let mut idx = 0;
103 while idx < buf.len() {
104 match port.get_mut().write(&buf[idx..]) {
105 Ok(n) => idx += n,
106 Err(ioerr) if ioerr.kind() == ErrorKind::TimedOut => {
107 util::file::wait_timeout(
111 unsafe { BorrowedFd::borrow_raw(port.as_raw_fd()) },
113 rustix::event::PollFlags::OUT,
114 Duration::from_secs(5),
115 )?;
116 }
117 Err(ioerr) => return Err(ioerr).context("UART communication error"),
118 }
119 }
120
121 Ok(())
122 }
123}
124
125impl Uart for SerialPortUart {
126 fn get_baudrate(&self) -> Result<u32> {
128 let pseudo = self.pseudo_baud.get();
129 if pseudo == 0 {
130 self.port
131 .borrow()
132 .get_ref()
133 .baud_rate()
134 .context("getting baudrate")
135 } else {
136 Ok(pseudo)
137 }
138 }
139
140 fn set_baudrate(&self, baudrate: u32) -> Result<()> {
142 let pseudo = self.pseudo_baud.get();
143 if pseudo == 0 {
144 self.port
145 .borrow_mut()
146 .get_mut()
147 .set_baud_rate(baudrate)
148 .map_err(|_| UartError::InvalidSpeed(baudrate))?;
149 } else {
150 self.pseudo_baud.set(baudrate);
151 }
152 Ok(())
153 }
154
155 fn get_device_path(&self) -> Result<String> {
156 Ok(self.port_name.clone())
157 }
158
159 fn set_parity(&self, parity: Parity) -> Result<()> {
160 self.port.borrow_mut().get_mut().set_parity(parity)?;
161 Ok(())
162 }
163
164 fn clear_rx_buffer(&self) -> Result<()> {
166 self.port.borrow_mut().get_mut().clear(ClearBuffer::Input)?;
167
168 const TIMEOUT: Duration = Duration::from_millis(5);
178 let mut buf = [0u8; 256];
179 while self.read_timeout(&mut buf, TIMEOUT)? > 0 {}
180 Ok(())
181 }
182
183 fn set_break(&self, enable: bool) -> Result<()> {
184 let mut port = self.port.borrow_mut();
185 if enable {
186 port.get_mut().set_break()?;
187 } else {
188 port.get_mut().clear_break()?;
189 }
190 Ok(())
191 }
192
193 fn borrow_fd(&self) -> Result<BorrowedFd<'_>> {
194 let port = self.port.borrow();
195 let fd = unsafe { BorrowedFd::borrow_raw(port.as_raw_fd()) };
197 Ok(fd)
198 }
199}
200
201pub fn flock_serial(port: &TTYPort, port_name: &str) -> Result<()> {
204 let fd = unsafe { BorrowedFd::borrow_raw(port.as_raw_fd()) };
206 rustix::fs::flock(fd, rustix::fs::FlockOperation::NonBlockingLockExclusive)
207 .map_err(|_| UartError::OpenError(format!("Device {port_name} is locked")))?;
208 Ok(())
209}