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}