cat_dev_serial/async_sys/mod.rs
1//! The OS specifical asynchronous serial port implementation.
2//!
3//! This module provides [`RawAsyncSerialPort`], a very thin wrapper around
4//! the serial port provided by the underlying implementation using tokio's
5//! an asynchronous file descriptor on unix, or on windows wrapping in a
6//! named pipe client so we interact with the port asynchronously safely.
7
8#[cfg(any(
9 target_os = "linux",
10 target_os = "freebsd",
11 target_os = "openbsd",
12 target_os = "netbsd",
13 target_os = "macos"
14))]
15mod unix;
16#[cfg(target_os = "windows")]
17mod windows;
18
19use tokio::io::{AsyncRead, AsyncWrite};
20#[cfg(any(
21 target_os = "linux",
22 target_os = "freebsd",
23 target_os = "openbsd",
24 target_os = "netbsd",
25 target_os = "macos"
26))]
27use unix::RawAsyncSerialPort;
28#[cfg(target_os = "windows")]
29use windows::RawAsyncSerialPort;
30
31use crate::SyncSerialPort;
32use std::{
33 io::{IoSlice, IoSliceMut, Result as IoResult},
34 path::{Path, PathBuf},
35 pin::Pin,
36 task::{Context, Poll},
37};
38use tokio::io::ReadBuf;
39use valuable::Valuable;
40
41/// An asynchronous serial port.
42#[derive(Debug, Valuable)]
43pub struct AsyncSerialPort {
44 inner: RawAsyncSerialPort,
45}
46
47impl AsyncSerialPort {
48 /// Get a list of available serial ports.
49 ///
50 /// ## Errors
51 ///
52 /// If your platform is unsupported, or an OS error occurs.
53 pub fn available_ports() -> IoResult<Vec<PathBuf>> {
54 SyncSerialPort::available_ports()
55 }
56
57 /// Open and configure a serial port by path or name.
58 ///
59 /// On Unix systems, the `name` parameter must be a path to a TTY device. On
60 /// Windows, it must be the name of a COM device, such as COM1, COM2, etc.
61 ///
62 /// The library automatically uses the win32 device namespace on Windows, so
63 /// COM ports above COM9 are supported out of the box.
64 ///
65 /// ## Errors
66 ///
67 /// If we cannot open, or configure the serial device at path.
68 pub fn new(path: impl AsRef<Path>) -> IoResult<Self> {
69 Ok(Self {
70 inner: RawAsyncSerialPort::new(SyncSerialPort::new(path)?)?,
71 })
72 }
73
74 /// Try to clone the serial port handle.
75 ///
76 /// The cloned object refers to the same serial port.
77 ///
78 /// Mixing reads and writes on different handles to the same serial port
79 /// from different threads may lead to unexpect results. The data may end
80 /// up interleaved in unpredictable ways.
81 ///
82 /// ## Errors
83 ///
84 /// If we cannot clone the underlying file descriptor.
85 pub fn try_clone(&self) -> IoResult<Self> {
86 let inner = self.inner.try_clone()?;
87 Ok(Self { inner })
88 }
89
90 /// Read bytes from the serial port.
91 ///
92 /// This is identical to
93 /// [`AsyncReadExt::read()`][tokio::io::AsyncReadExt::read], except that
94 /// this function takes a const reference `&self`. This allows you to use
95 /// the serial port concurrently from multiple tasks.
96 ///
97 /// Note that there are no guarantees about which task receives what data
98 /// when multiple tasks are reading from the serial port. You should normally
99 /// limit yourself to a single reading task and a single writing task.
100 ///
101 /// ## Errors
102 ///
103 /// If the underlying OS, or device throws an error.
104 pub async fn read(&self, buff: &mut [u8]) -> IoResult<usize> {
105 self.inner.read(buff).await
106 }
107
108 /// If this implementation supports vectored reads.
109 #[must_use]
110 pub const fn can_read_vectored() -> bool {
111 RawAsyncSerialPort::can_read_vectored()
112 }
113
114 /// Read bytes from the serial port into a slice of buffers.
115 ///
116 /// Note that there are no guarantees about which task receives what data
117 /// when multiple tasks are reading from the serial port. You should
118 /// normally limit yourself to a single reading task and a single writing
119 /// task.
120 ///
121 /// ## Errors
122 ///
123 /// If the underlying OS, or device throws an error.
124 pub async fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
125 self.inner.read_vectored(bufs).await
126 }
127
128 /// Write bytes to the serial port.
129 ///
130 /// This is identical to
131 /// [`AsyncWriteExt::write()`][tokio::io::AsyncWriteExt::write], except that
132 /// this function takes a const reference `&self`. This allows you to use the
133 /// serial port concurrently from multiple tasks.
134 ///
135 /// Note that data written to the same serial port from multiple tasks may
136 /// end up interleaved at the receiving side. You should normally limit
137 /// yourself to a single reading task and a single writing task.
138 ///
139 /// ## Errors
140 ///
141 /// If the underlying OS, or device throws an error.
142 pub async fn write(&self, buff: &[u8]) -> IoResult<usize> {
143 self.inner.write(buff).await
144 }
145
146 /// Write all bytes to the serial port.
147 ///
148 /// This will continue to call [`Self::write()`] until the entire buffer
149 /// has been written, or an I/O error occurs.
150 ///
151 /// This is identical to
152 /// [`AsyncWriteExt::write_all()`][tokio::io::AsyncWriteExt::write_all],
153 /// except that this function takes a const reference `&self`. This allows
154 /// you to use the serial port concurrently from multiple tasks.
155 ///
156 /// Note that data written to the same serial port from multiple tasks may
157 /// end up interleaved at the receiving side. You should normally limit
158 /// yourself to a single reading task and a single writing task.
159 ///
160 /// ## Errors
161 ///
162 /// If the underlying OS, or device throws an error.
163 pub async fn write_all(&self, buff: &[u8]) -> IoResult<()> {
164 let mut written = 0;
165 while written < buff.len() {
166 written += self.write(&buff[written..]).await?;
167 }
168 Ok(())
169 }
170
171 /// If this implementation supports vectored writes.
172 #[must_use]
173 pub const fn can_write_vectored() -> bool {
174 RawAsyncSerialPort::can_write_vectored()
175 }
176
177 /// Write bytes to the serial port from a slice of buffers.
178 ///
179 /// This is identical to
180 /// [`AsyncWriteExt::write_vectored()`][tokio::io::AsyncWriteExt::write_vectored],
181 /// except that this function takes a const reference `&self`. This allows
182 /// you to use the serial port concurrently from multiple tasks.
183 ///
184 /// Note that data written to the same serial port from multiple tasks may
185 /// end up interleaved at the receiving side. You should normally limit
186 /// yourself to a single reading task and a single writing task.
187 ///
188 /// ## Errors
189 ///
190 /// If the underlying OS, or device throws an error.
191 pub async fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> IoResult<usize> {
192 self.inner.write_vectored(bufs).await
193 }
194
195 /// Discard the kernel input and output buffers for the serial port.
196 ///
197 /// When you write to a serial port, the data may be put in a buffer by the
198 /// OS to be transmitted by the actual device later. Similarly, data received
199 /// on the device can be put in a buffer by the OS untill you read it. This
200 /// function clears both buffers: any untransmitted data and received but
201 /// unread data is discarded by the OS.
202 ///
203 /// ## Errors
204 ///
205 /// If the underlying OS, or device throws an error.
206 pub fn discard_buffers(&self) -> IoResult<()> {
207 self.inner.with_raw(SyncSerialPort::discard_buffers)
208 }
209
210 /// Discard the kernel input buffers for the serial port.
211 ///
212 /// Data received on the device can be put in a buffer by the OS untill
213 /// you read it. This function clears that buffer: received but unread
214 /// data is discarded by the OS.
215 ///
216 /// This is particularly useful when communicating with a device that only
217 /// responds to commands that you send to it. If you discard the input
218 /// buffer before sending the command, you discard any noise that may have
219 /// been received after the last command.
220 ///
221 /// ## Errors
222 ///
223 /// If the underlying OS, or device throws an error.
224 pub fn discard_input_buffer(&self) -> IoResult<()> {
225 self.inner.with_raw(SyncSerialPort::discard_input_buffer)
226 }
227
228 /// Discard the kernel output buffers for the serial port.
229 ///
230 /// When you write to a serial port, the data is generally put in a buffer
231 /// by the OS to be transmitted by the actual device later. This function
232 /// clears that buffer: any untransmitted data is discarded by the OS.
233 ///
234 /// ## Errors
235 ///
236 /// If the underlying OS, or device throws an error.
237 pub fn discard_output_buffer(&self) -> IoResult<()> {
238 self.inner.with_raw(SyncSerialPort::discard_output_buffer)
239 }
240
241 /// Set the state of the Ready To Send line.
242 ///
243 /// If hardware flow control is enabled on the serial port, it is platform
244 /// specific what will happen. The function may fail with an error or it
245 /// may silently be ignored. It may even succeed and interfere with the
246 /// flow control.
247 ///
248 /// ## Errors
249 ///
250 /// If the underlying OS, or device throws an error.
251 pub fn set_rts(&self, state: bool) -> IoResult<()> {
252 self.inner.with_raw(|raw| raw.set_rts(state))
253 }
254
255 /// Read the state of the Clear To Send line.
256 ///
257 /// If hardware flow control is enabled on the serial port, it is platform
258 /// specific what will happen. The function may fail with an error, it
259 /// may return a bogus value, or it may return the actual state of the CTS
260 /// line.
261 ///
262 /// ## Errors
263 ///
264 /// If the underlying OS, or device throws an error.
265 pub fn read_cts(&self) -> IoResult<bool> {
266 self.inner.with_raw(SyncSerialPort::read_cts)
267 }
268
269 /// Set the state of the Data Terminal Ready line.
270 ///
271 /// If hardware flow control is enabled on the serial port, it is platform
272 /// specific what will happen. The function may fail with an error or it
273 /// may silently be ignored.
274 ///
275 /// ## Errors
276 ///
277 /// If the underlying OS, or device throws an error.
278 pub fn set_dtr(&self, state: bool) -> IoResult<()> {
279 self.inner.with_raw(|raw| raw.set_dtr(state))
280 }
281
282 /// Read the state of the Data Set Ready line.
283 ///
284 /// If hardware flow control is enabled on the serial port, it is platform
285 /// specific what will happen. The function may fail with an error, it may
286 /// return a bogus value, or it may return the actual state of the DSR line.
287 ///
288 /// ## Errors
289 ///
290 /// If the underlying OS, or device throws an error.
291 pub fn read_dsr(&self) -> IoResult<bool> {
292 self.inner.with_raw(SyncSerialPort::read_dsr)
293 }
294
295 /// Read the state of the Ring Indicator line.
296 ///
297 /// This line is also sometimes also called the RNG or RING line.
298 ///
299 /// ## Errors
300 ///
301 /// If the underlying OS, or device throws an error.
302 pub fn read_ri(&self) -> IoResult<bool> {
303 self.inner.with_raw(SyncSerialPort::read_ri)
304 }
305
306 /// Read the state of the Carrier Detect (CD) line.
307 ///
308 /// This line is also called the Data Carrier Detect (DCD) line
309 /// or the Receive Line Signal Detect (RLSD) line.
310 ///
311 /// ## Errors
312 ///
313 /// If the underlying OS, or device throws an error.
314 pub fn read_cd(&self) -> IoResult<bool> {
315 self.inner.with_raw(SyncSerialPort::read_cd)
316 }
317}
318
319impl AsyncRead for AsyncSerialPort {
320 fn poll_read(
321 self: Pin<&mut Self>,
322 ctx: &mut Context<'_>,
323 buff: &mut ReadBuf<'_>,
324 ) -> Poll<IoResult<()>> {
325 self.get_mut().inner.poll_read(ctx, buff)
326 }
327}
328
329impl AsyncWrite for AsyncSerialPort {
330 fn poll_write(
331 self: Pin<&mut Self>,
332 ctx: &mut Context<'_>,
333 buff: &[u8],
334 ) -> Poll<IoResult<usize>> {
335 self.get_mut().inner.poll_write(ctx, buff)
336 }
337
338 fn poll_write_vectored(
339 self: Pin<&mut Self>,
340 ctx: &mut Context<'_>,
341 bufs: &[IoSlice<'_>],
342 ) -> Poll<IoResult<usize>> {
343 self.get_mut().inner.poll_write_vectored(ctx, bufs)
344 }
345
346 fn poll_flush(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<IoResult<()>> {
347 // We can't do `tcdrain()` asynchronously :(
348 Poll::Ready(Ok(()))
349 }
350
351 fn poll_shutdown(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<IoResult<()>> {
352 self.get_mut().inner.poll_shutdown(ctx)
353 }
354}