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 fn set_break(&self, enable: bool) -> Result<()> {
123 let mut port = self.port.borrow_mut();
124 if enable {
125 port.get_mut().set_break()?;
126 } else {
127 port.get_mut().clear_break()?;
128 }
129 Ok(())
130 }
131}
132
133impl Uart for SerialPortUart {
134 fn get_baudrate(&self) -> Result<u32> {
136 let pseudo = self.pseudo_baud.get();
137 if pseudo == 0 {
138 self.port
139 .borrow()
140 .get_ref()
141 .baud_rate()
142 .context("getting baudrate")
143 } else {
144 Ok(pseudo)
145 }
146 }
147
148 fn set_baudrate(&self, baudrate: u32) -> Result<()> {
150 let pseudo = self.pseudo_baud.get();
151 if pseudo == 0 {
152 self.port
153 .borrow_mut()
154 .get_mut()
155 .set_baud_rate(baudrate)
156 .map_err(|_| UartError::InvalidSpeed(baudrate))?;
157 } else {
158 self.pseudo_baud.set(baudrate);
159 }
160 Ok(())
161 }
162
163 fn get_device_path(&self) -> Result<String> {
164 Ok(self.port_name.clone())
165 }
166
167 fn set_parity(&self, parity: Parity) -> Result<()> {
168 self.port.borrow_mut().get_mut().set_parity(parity)?;
169 Ok(())
170 }
171
172 fn clear_rx_buffer(&self) -> Result<()> {
174 self.port.borrow_mut().get_mut().clear(ClearBuffer::Input)?;
175
176 const TIMEOUT: Duration = Duration::from_millis(5);
186 let mut buf = [0u8; 256];
187 while self.read_timeout(&mut buf, TIMEOUT)? > 0 {}
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}