cat_dev/serial/underlying/
mod.rs

1mod sys;
2
3use crate::serial::underlying::sys::RawSyncSerialPort;
4use std::{
5	io::{
6		Error as IoError, ErrorKind as IoErrorKind, IoSlice, IoSliceMut, Read, Result as IoResult,
7		Write,
8	},
9	path::{Path, PathBuf},
10	time::Duration,
11};
12
13#[cfg(unix)]
14use crate::serial::underlying::sys::DEFAULT_TIMEOUT_MS;
15
16/// A serial port that you can interact with synchronously.
17#[derive(Debug)]
18pub struct SyncSerialPort {
19	inner: RawSyncSerialPort,
20}
21
22impl SyncSerialPort {
23	/// Get a list of available serial ports.
24	///
25	/// ## Errors
26	///
27	/// - If the platform is not supported.
28	/// - If we get an error from the OS listing ports.
29	pub fn available_ports() -> IoResult<Vec<PathBuf>> {
30		RawSyncSerialPort::enumerate()
31	}
32
33	/// Open and configure a serial port by path or name.
34	///
35	/// On Unix systems, the `name` parameter must be a path to a TTY device.
36	/// On Windows, it must be the name of a COM device, such as COM1, COM2, etc.
37	///
38	/// The library automatically uses the win32 device namespace on Windows,
39	/// so COM ports above COM9 are supported out of the box.
40	///
41	/// ## Errors
42	///
43	/// If we cannot open the
44	pub fn new(name: impl AsRef<Path>) -> IoResult<Self> {
45		Ok(Self {
46			inner: RawSyncSerialPort::new(name)?,
47		})
48	}
49
50	/// Try to clone the serial port handle.
51	///
52	/// The cloned object refers to the same serial port.
53	///
54	/// Mixing reads and writes on different handles to the same serial port
55	/// from different threads may lead to unexpect results. The data may end
56	/// up interleaved in unpredictable ways.
57	///
58	/// ## Errors
59	///
60	/// If we cannot clone the underlying file descriptor/handle.
61	pub fn try_clone(&self) -> IoResult<Self> {
62		Ok(Self {
63			inner: self.inner.try_clone()?,
64		})
65	}
66
67	/// Read bytes from the serial port.
68	///
69	/// This is identical to [`std::io::Read::read()`], except that this function
70	/// takes a const reference `&self`. This allows you to use the serial port
71	/// concurrently from multiple threads.
72	///
73	/// Note that there are no guarantees on which thread receives what data
74	/// when multiple threads are reading from the serial port. You should
75	/// normally limit yourself to a single reading thread and a single
76	/// writing thread.
77	///
78	/// ## Errors
79	///
80	/// If we get an error back from the OS.
81	pub fn read(&self, buff: &mut [u8]) -> IoResult<usize> {
82		self.inner.read(buff)
83	}
84
85	/// Read the exact number of bytes required to fill the buffer from the
86	/// serial port.
87	///
88	/// This will repeatedly call `read()` until the entire buffer is filled.
89	/// Errors of the type [`std::io::ErrorKind::Interrupted`] are silently
90	/// ignored. Any other errors (including timeouts) will be returned
91	/// immediately.
92	///
93	/// If this function returns an error, it may already have read some data
94	/// from the serial port into the provided buffer.
95	///
96	/// This function is identical to [`std::io::Read::read_exact()`], except
97	/// that this function takes a const reference `&self`. This allows you to
98	/// use the serial port concurrently from multiple threads.
99	///
100	/// Note that there are no guarantees on which thread receives what data when
101	/// multiple threads are reading from the serial port. You should normally
102	/// limit yourself to a single reading thread and a single writing thread.
103	///
104	/// ## Errors
105	///
106	/// If we get an error back from the OS.
107	pub fn read_exact(&self, buff: &mut [u8]) -> IoResult<()> {
108		let mut working_buff = buff;
109
110		while !working_buff.is_empty() {
111			match self.read(working_buff) {
112				Ok(0) => {
113					return Err(IoError::new(
114						IoErrorKind::UnexpectedEof,
115						"Failed to fill whole buffer",
116					))
117				}
118				Ok(read) => working_buff = &mut working_buff[read..],
119				Err(cause) => {
120					if cause.kind() == IoErrorKind::Interrupted {
121						continue;
122					}
123
124					return Err(cause);
125				}
126			}
127		}
128
129		Ok(())
130	}
131
132	/// If this implementation supports vectored reads.
133	#[must_use]
134	pub const fn can_read_vectored() -> bool {
135		RawSyncSerialPort::can_read_vectored()
136	}
137
138	/// Read bytes from the serial port into a slice of buffers.
139	///
140	/// This is identical to [`std::io::Read::read_vectored()`], except that this
141	/// function takes a const reference `&self`. This allows you to use the
142	/// serial port concurrently from multiple threads.
143	///
144	/// Note that there are no guarantees on which thread receives what data when
145	/// multiple threads are reading from the serial port. You should normally
146	/// limit yourself to a single reading thread and a single writing thread.
147	///
148	/// ## Errors
149	///
150	/// If we get an error back from the OS.
151	pub fn read_vectored(&self, buff: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
152		self.inner.read_vectored(buff)
153	}
154
155	/// Write bytes to the serial port.
156	///
157	/// This is identical to [`std::io::Write::write()`], except that this
158	/// function takes a const reference `&self`. This allows you to use the
159	/// serial port concurrently from multiple threads.
160	///
161	/// Note that data written to the same serial port from multiple threads may
162	/// end up interleaved at the receiving side. You should normally limit
163	/// yourself to a single reading thread and a single writing thread.
164	///
165	/// ## Errors
166	///
167	/// If we get an error back from the OS.
168	pub fn write(&self, buff: &[u8]) -> IoResult<usize> {
169		self.inner.write(buff)
170	}
171
172	/// Write all bytes to the serial port.
173	///
174	/// This will continue to call [`Self::write()`] until the entire buffer has
175	/// been written, or an I/O error occurs.
176	///
177	/// This is identical to [`std::io::Write::write_all()`], except that this
178	/// function takes a const reference `&self`. This allows you to use the
179	/// serial port concurrently from multiple threads.
180	///
181	/// Note that data written to the same serial port from multiple threads may
182	/// end up interleaved at the receiving side. You should normally limit
183	/// yourself to a single reading thread and a single writing thread.
184	///
185	/// ## Errors
186	///
187	/// If we get an error back from the OS.
188	pub fn write_all(&self, buff: &[u8]) -> IoResult<()> {
189		let mut working_buff = buff;
190
191		while !working_buff.is_empty() {
192			match self.write(working_buff) {
193				Ok(0) => {
194					return Err(IoError::new(
195						IoErrorKind::WriteZero,
196						"failed to write whole buffer",
197					))
198				}
199				Ok(n) => working_buff = &working_buff[n..],
200				Err(cause) => {
201					if cause.kind() == IoErrorKind::Interrupted {
202						continue;
203					}
204
205					return Err(cause);
206				}
207			}
208		}
209
210		Ok(())
211	}
212
213	/// If this implementation supports vectored writes.
214	#[must_use]
215	pub const fn can_write_vectored() -> bool {
216		RawSyncSerialPort::can_write_vectored()
217	}
218
219	/// Write bytes to the serial port from a slice of buffers.
220	///
221	/// This is identical to [`std::io::Write::write_vectored()`], except that
222	/// this function takes a const reference `&self`. This allows you to use
223	/// the serial port concurrently from multiple threads.
224	///
225	/// Note that data written to the same serial port from multiple threads may
226	/// end up interleaved at the receiving side. You should normally limit
227	/// yourself to a single reading thread and a single writing thread.
228	///
229	/// ## Errors
230	///
231	/// If we get an error back from the OS.
232	pub fn write_vectored(&self, buff: &[IoSlice<'_>]) -> IoResult<usize> {
233		self.inner.write_vectored(buff)
234	}
235
236	/// Flush all data queued to be written.
237	///
238	/// This will block until the OS buffer has been fully transmitted.
239	///
240	/// This is identical to [`std::io::Write::flush()`], except that this
241	/// function takes a const reference `&self`.
242	///
243	/// ## Errors
244	///
245	/// If we get an error back from the OS.
246	pub fn flush(&self) -> IoResult<()> {
247		self.inner.flush_output()
248	}
249
250	/// Get the read timeout for the serial port.
251	///
252	/// The timeout gotten by this function is an upper bound on individual calls
253	/// to [`std::io::Read::read()`]. Other platform specific time-outs may
254	/// trigger before this timeout does.
255	///
256	/// ## Errors
257	///
258	/// If we get an error back from the OS.
259	pub fn get_read_timeout(&self) -> IoResult<Duration> {
260		self.inner.get_read_timeout()
261	}
262
263	/// Get the write timeout for the serial port.
264	///
265	/// The timeout gotten by this function is an upper bound on individual calls
266	/// to [`std::io::Write::write()`]. Other platform specific time-outs may
267	/// trigger before this timeout does.
268	///
269	/// ## Errors
270	///
271	/// If we get an error back from the OS.
272	pub fn get_write_timeout(&self) -> IoResult<Duration> {
273		self.inner.get_write_timeout()
274	}
275
276	/// Set the read timeout for the serial port.
277	///
278	/// The timeout set by this function is an upper bound on individual calls
279	/// to [`std::io::Read::read()`]. Other platform specific time-outs may
280	/// trigger before this timeout does.
281	///
282	/// ## Errors
283	///
284	/// If we get an error back from the OS.
285	pub fn set_read_timeout(&mut self, new_timeout: Duration) -> IoResult<()> {
286		self.inner.set_read_timeout(new_timeout)
287	}
288
289	/// Set the read timeout for the serial port.
290	///
291	/// The timeout set by this function is an upper bound on individual calls
292	/// to [`std::io::Write::write()`]. Other platform specific time-outs may
293	/// trigger before this timeout does.
294	///
295	/// ## Errors
296	///
297	/// If we get an error back from the OS.
298	pub fn set_write_timeout(&mut self, new_timeout: Duration) -> IoResult<()> {
299		self.inner.set_write_timeout(new_timeout)
300	}
301
302	/// Discard the kernel input and output buffers for the serial port.
303	///
304	/// When you write to a serial port, the data may be put in a buffer
305	/// by the OS to be transmitted by the actual device later. Similarly, data
306	/// received on the device can be put in a buffer by the OS untill you read
307	/// it. This function clears both buffers: any untransmitted data and
308	/// received but unread data is discarded by the OS.
309	///
310	/// ## Errors
311	///
312	/// If we get an error back from the OS.
313	pub fn discard_buffers(&self) -> IoResult<()> {
314		self.inner.discard_buffers(true, true)
315	}
316
317	/// Discard the kernel input buffers for the serial port.
318	///
319	/// Data received on the device can be put in a buffer by the OS untill you
320	/// read it. This function clears that buffer: received but unread data
321	/// is discarded by the OS.
322	///
323	/// This is particularly useful when communicating with a device that only
324	/// responds to commands that you send to it. If you discard the input
325	/// buffer before sending the command, you discard any noise that may
326	/// have been received after the last command.
327	///
328	/// ## Errors
329	///
330	/// If we get an error back from the OS.
331	pub fn discard_input_buffer(&self) -> IoResult<()> {
332		self.inner.discard_buffers(true, false)
333	}
334
335	/// Discard the kernel output buffers for the serial port.
336	///
337	/// When you write to a serial port, the data is generally put in a buffer
338	/// by the OS to be transmitted by the actual device later. This function
339	/// clears that buffer: any untransmitted data is discarded by the OS.
340	///
341	/// ## Errors
342	///
343	/// If we get an error back from the OS.
344	pub fn discard_output_buffer(&self) -> IoResult<()> {
345		self.inner.discard_buffers(false, true)
346	}
347
348	/// Set the state of the Ready To Send line.
349	///
350	/// If hardware flow control is enabled on the serial port, it is platform
351	/// specific what will happen. The function may fail with an error or it
352	/// may silently be ignored. It may even succeed and interfere with the
353	/// flow control.
354	///
355	/// ## Errors
356	///
357	/// If we get an error back from the OS.
358	pub fn set_rts(&self, state: bool) -> IoResult<()> {
359		self.inner.set_rts(state)
360	}
361
362	/// Read the state of the Clear To Send line.
363	///
364	/// If hardware flow control is enabled on the serial port, it is platform
365	/// specific what will happen. The function may fail with an error, it may
366	/// return a bogus value, or it may return the actual state of the CTS line.
367	///
368	/// ## Errors
369	///
370	/// If we get an error back from the OS.
371	pub fn read_cts(&self) -> IoResult<bool> {
372		self.inner.read_cts()
373	}
374
375	/// Set the state of the Data Terminal Ready line.
376	///
377	/// If hardware flow control is enabled on the serial port, it is platform
378	/// specific what will happen. The function may fail with an error or it
379	/// may silently be ignored.
380	///
381	/// ## Errors
382	///
383	/// If we get an error back from the OS.
384	pub fn set_dtr(&self, state: bool) -> IoResult<()> {
385		self.inner.set_dtr(state)
386	}
387
388	/// Read the state of the Data Set Ready line.
389	///
390	/// If hardware flow control is enabled on the serial port, it is platform
391	/// specific what will happen. The function may fail with an error, it may
392	/// return a bogus value, or it may return the actual state of the DSR line.
393	///
394	/// ## Errors
395	///
396	/// If we get an error back from the OS.
397	pub fn read_dsr(&self) -> IoResult<bool> {
398		self.inner.read_dsr()
399	}
400
401	/// Read the state of the Ring Indicator line.
402	///
403	/// This line is also sometimes also called the RNG or RING line.
404	///
405	/// ## Errors
406	///
407	/// If we get an error back from the OS.
408	pub fn read_ri(&self) -> IoResult<bool> {
409		self.inner.read_ri()
410	}
411
412	/// Read the state of the Carrier Detect (CD) line.
413	///
414	/// This line is also called the Data Carrier Detect (DCD) line
415	/// or the Receive Line Signal Detect (RLSD) line.
416	///
417	/// ## Errors
418	///
419	/// If we get an error back from the OS.
420	pub fn read_cd(&self) -> IoResult<bool> {
421		self.inner.read_cd()
422	}
423}
424
425impl Read for SyncSerialPort {
426	fn read(&mut self, buff: &mut [u8]) -> IoResult<usize> {
427		SyncSerialPort::read(self, buff)
428	}
429	fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
430		SyncSerialPort::read_vectored(self, bufs)
431	}
432}
433
434impl Read for &'_ SyncSerialPort {
435	fn read(&mut self, buff: &mut [u8]) -> IoResult<usize> {
436		SyncSerialPort::read(self, buff)
437	}
438	fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
439		SyncSerialPort::read_vectored(self, bufs)
440	}
441}
442
443impl Write for SyncSerialPort {
444	fn write(&mut self, buff: &[u8]) -> IoResult<usize> {
445		SyncSerialPort::write(self, buff)
446	}
447
448	fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> IoResult<usize> {
449		SyncSerialPort::write_vectored(self, bufs)
450	}
451
452	fn flush(&mut self) -> IoResult<()> {
453		SyncSerialPort::flush(self)
454	}
455}
456
457impl Write for &'_ SyncSerialPort {
458	fn write(&mut self, buff: &[u8]) -> IoResult<usize> {
459		SyncSerialPort::write(self, buff)
460	}
461
462	fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> IoResult<usize> {
463		SyncSerialPort::write_vectored(self, bufs)
464	}
465
466	fn flush(&mut self) -> IoResult<()> {
467		SyncSerialPort::flush(self)
468	}
469}
470
471#[cfg(unix)]
472impl From<SyncSerialPort> for std::os::unix::io::OwnedFd {
473	fn from(value: SyncSerialPort) -> Self {
474		value.inner.fd.into()
475	}
476}
477
478#[cfg(unix)]
479impl From<std::os::unix::io::OwnedFd> for SyncSerialPort {
480	fn from(value: std::os::unix::io::OwnedFd) -> Self {
481		Self {
482			inner: RawSyncSerialPort {
483				fd: value.into(),
484				read_timeout_ms: DEFAULT_TIMEOUT_MS,
485				write_timeout_ms: DEFAULT_TIMEOUT_MS,
486			},
487		}
488	}
489}
490
491#[cfg(unix)]
492impl std::os::unix::io::AsFd for SyncSerialPort {
493	fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> {
494		self.inner.fd.as_fd()
495	}
496}
497
498#[cfg(unix)]
499impl std::os::unix::io::AsRawFd for SyncSerialPort {
500	fn as_raw_fd(&self) -> std::os::unix::prelude::RawFd {
501		self.inner.fd.as_raw_fd()
502	}
503}
504
505#[cfg(unix)]
506impl std::os::unix::io::IntoRawFd for SyncSerialPort {
507	fn into_raw_fd(self) -> std::os::unix::prelude::RawFd {
508		self.inner.fd.into_raw_fd()
509	}
510}
511
512#[cfg(unix)]
513impl std::os::unix::io::FromRawFd for SyncSerialPort {
514	unsafe fn from_raw_fd(fd: std::os::unix::prelude::RawFd) -> Self {
515		Self {
516			inner: RawSyncSerialPort {
517				fd: std::fs::File::from_raw_fd(fd),
518				read_timeout_ms: DEFAULT_TIMEOUT_MS,
519				write_timeout_ms: DEFAULT_TIMEOUT_MS,
520			},
521		}
522	}
523}
524
525#[cfg(target_os = "windows")]
526impl From<SyncSerialPort> for std::os::windows::io::OwnedHandle {
527	fn from(value: SyncSerialPort) -> Self {
528		value.inner.fd.into()
529	}
530}
531
532#[cfg(target_os = "windows")]
533impl From<std::os::windows::io::OwnedHandle> for SyncSerialPort {
534	fn from(value: std::os::windows::io::OwnedHandle) -> Self {
535		Self {
536			inner: RawSyncSerialPort { fd: value.into() },
537		}
538	}
539}
540
541#[cfg(target_os = "windows")]
542impl std::os::windows::io::AsHandle for SyncSerialPort {
543	fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> {
544		self.inner.fd.as_handle()
545	}
546}
547
548#[cfg(target_os = "windows")]
549impl std::os::windows::io::AsRawHandle for SyncSerialPort {
550	fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
551		self.inner.fd.as_raw_handle()
552	}
553}
554
555#[cfg(target_os = "windows")]
556impl std::os::windows::io::IntoRawHandle for SyncSerialPort {
557	fn into_raw_handle(self) -> std::os::windows::io::RawHandle {
558		self.inner.fd.into_raw_handle()
559	}
560}
561
562#[cfg(target_os = "windows")]
563impl std::os::windows::io::FromRawHandle for SyncSerialPort {
564	unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
565		Self {
566			inner: RawSyncSerialPort {
567				fd: std::fs::File::from_raw_handle(handle),
568			},
569		}
570	}
571}