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}