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