ferrous_serialport/lib.rs
1//! serialport-rs is a cross-platform serial port library.
2//!
3//! The goal of this library is to expose a cross-platform and platform-specific API for enumerating
4//! and using blocking I/O with serial ports. This library exposes a similar API to that provided
5//! by [Qt's `QSerialPort` library](https://doc.qt.io/qt-5/qserialport.html).
6//!
7//! # Feature Overview
8//!
9//! The library has been organized such that there is a high-level `SerialPort` trait that provides
10//! a cross-platform API for accessing serial ports. This is the preferred method of interacting
11//! with ports and as such is part of the `prelude`. The `open*()` and `available_ports()` functions
12//! in the root provide cross-platform functionality.
13//!
14//! For platform-specific functionaly, this crate is split into a `posix` and `windows` API with
15//! corresponding `TTYPort` and `COMPort` structs (that both implement the `SerialPort` trait).
16//! Using the platform-specific `open*()` functions will return the platform-specific port object
17//! which allows access to platform-specific functionality.
18
19#![deny(
20 missing_docs,
21 missing_debug_implementations,
22 missing_copy_implementations,
23 unused
24)]
25// Don't worry about needing to `unwrap()` or otherwise handle some results in
26// doc tests.
27#![doc(test(attr(allow(unused_must_use))))]
28
29use std::convert::From;
30use std::error::Error as StdError;
31use std::fmt;
32use std::io;
33use std::time::Duration;
34
35#[cfg(unix)]
36mod posix;
37#[cfg(unix)]
38pub use posix::{BreakDuration, TTYPort};
39
40#[cfg(windows)]
41mod windows;
42#[cfg(windows)]
43pub use windows::COMPort;
44
45/// A type for results generated by interacting with serial ports
46///
47/// The `Err` type is hard-wired to [`serialport::Error`](struct.Error.html).
48pub type Result<T> = std::result::Result<T, Error>;
49
50/// Categories of errors that can occur when interacting with serial ports
51///
52/// This list is intended to grow over time and it is not recommended to
53/// exhaustively match against it.
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum ErrorKind {
56 /// The device is not available.
57 ///
58 /// This could indicate that the device is in use by another process or was
59 /// disconnected while performing I/O.
60 NoDevice,
61
62 /// A parameter was incorrect.
63 InvalidInput,
64
65 /// An unknown error occurred.
66 Unknown,
67
68 /// An I/O error occurred.
69 ///
70 /// The type of I/O error is determined by the inner `io::ErrorKind`.
71 Io(io::ErrorKind),
72}
73
74/// An error type for serial port operations
75#[derive(Debug)]
76pub struct Error {
77 /// The kind of error this is
78 pub kind: ErrorKind,
79 /// A description of the error suitable for end-users
80 pub description: String,
81}
82
83impl Error {
84 /// Instantiates a new error
85 pub fn new<T: Into<String>>(kind: ErrorKind, description: T) -> Self {
86 Error {
87 kind,
88 description: description.into(),
89 }
90 }
91
92 /// Returns the corresponding `ErrorKind` for this error.
93 pub fn kind(&self) -> ErrorKind {
94 self.kind
95 }
96}
97
98impl fmt::Display for Error {
99 fn fmt(&self, fmt: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
100 fmt.write_str(&self.description)
101 }
102}
103
104impl StdError for Error {
105 fn description(&self) -> &str {
106 &self.description
107 }
108}
109
110impl From<io::Error> for Error {
111 fn from(io_error: io::Error) -> Error {
112 Error::new(ErrorKind::Io(io_error.kind()), format!("{}", io_error))
113 }
114}
115
116impl From<Error> for io::Error {
117 fn from(error: Error) -> io::Error {
118 let kind = match error.kind {
119 ErrorKind::NoDevice => io::ErrorKind::NotFound,
120 ErrorKind::InvalidInput => io::ErrorKind::InvalidInput,
121 ErrorKind::Unknown => io::ErrorKind::Other,
122 ErrorKind::Io(kind) => kind,
123 };
124
125 io::Error::new(kind, error.description)
126 }
127}
128
129/// Number of bits per character
130#[derive(Debug, Copy, Clone, PartialEq, Eq)]
131pub enum DataBits {
132 /// 5 bits per character
133 Five,
134
135 /// 6 bits per character
136 Six,
137
138 /// 7 bits per character
139 Seven,
140
141 /// 8 bits per character
142 Eight,
143}
144
145/// Parity checking modes
146///
147/// When parity checking is enabled (`Odd` or `Even`) an extra bit is transmitted with
148/// each character. The value of the parity bit is arranged so that the number of 1 bits in the
149/// character (including the parity bit) is an even number (`Even`) or an odd number
150/// (`Odd`).
151///
152/// Parity checking is disabled by setting `None`, in which case parity bits are not
153/// transmitted.
154#[derive(Debug, Copy, Clone, PartialEq, Eq)]
155pub enum Parity {
156 /// No parity bit.
157 None,
158
159 /// Parity bit sets odd number of 1 bits.
160 Odd,
161
162 /// Parity bit sets even number of 1 bits.
163 Even,
164}
165
166/// Number of stop bits
167///
168/// Stop bits are transmitted after every character.
169#[derive(Debug, Copy, Clone, PartialEq, Eq)]
170pub enum StopBits {
171 /// One stop bit.
172 One,
173
174 /// Two stop bits.
175 Two,
176}
177
178/// Flow control modes
179#[derive(Debug, Copy, Clone, PartialEq, Eq)]
180pub enum FlowControl {
181 /// No flow control.
182 None,
183
184 /// Flow control using XON/XOFF bytes.
185 Software,
186
187 /// Flow control using RTS/CTS signals.
188 Hardware,
189}
190
191/// Specifies which buffer or buffers to purge when calling [`clear`]
192///
193/// [`clear`]: trait.SerialPort.html#tymethod.clear
194#[derive(Debug, Copy, Clone, PartialEq, Eq)]
195pub enum ClearBuffer {
196 /// Specify to clear data received but not read
197 Input,
198 /// Specify to clear data written but not yet transmitted
199 Output,
200 /// Specify to clear both data received and data not yet transmitted
201 All,
202}
203
204/// A struct containing all serial port settings
205#[derive(Debug, Clone, PartialEq, Eq)]
206pub struct SerialPortBuilder {
207 /// The port name, usually the device path
208 path: String,
209 /// The baud rate in symbols-per-second
210 baud_rate: u32,
211 /// Number of bits used to represent a character sent on the line
212 data_bits: DataBits,
213 /// The type of signalling to use for controlling data transfer
214 flow_control: FlowControl,
215 /// The type of parity to use for error checking
216 parity: Parity,
217 /// Number of bits to use to signal the end of a character
218 stop_bits: StopBits,
219 /// Amount of time to wait to receive data before timing out
220 timeout: Duration,
221}
222
223impl SerialPortBuilder {
224 /// Set the path to the serial port
225 pub fn path<'a>(mut self, path: impl Into<std::borrow::Cow<'a, str>>) -> Self {
226 self.path = path.into().as_ref().to_owned();
227 self
228 }
229
230 /// Set the baud rate in symbols-per-second
231 pub fn baud_rate(mut self, baud_rate: u32) -> Self {
232 self.baud_rate = baud_rate;
233 self
234 }
235
236 /// Set the number of bits used to represent a character sent on the line
237 pub fn data_bits(mut self, data_bits: DataBits) -> Self {
238 self.data_bits = data_bits;
239 self
240 }
241
242 /// Set the type of signalling to use for controlling data transfer
243 pub fn flow_control(mut self, flow_control: FlowControl) -> Self {
244 self.flow_control = flow_control;
245 self
246 }
247
248 /// Set the type of parity to use for error checking
249 pub fn parity(mut self, parity: Parity) -> Self {
250 self.parity = parity;
251 self
252 }
253
254 /// Set the number of bits to use to signal the end of a character
255 pub fn stop_bits(mut self, stop_bits: StopBits) -> Self {
256 self.stop_bits = stop_bits;
257 self
258 }
259
260 /// Set the amount of time to wait to receive data before timing out
261 pub fn timeout(mut self, timeout: Duration) -> Self {
262 self.timeout = timeout;
263 self
264 }
265
266 /// Open a cross-platform interface to the port with the specified settings
267 pub fn open(self) -> Result<Box<dyn SerialPort>> {
268 #[cfg(unix)]
269 return posix::TTYPort::open(&self).map(|p| Box::new(p) as Box<dyn SerialPort>);
270
271 #[cfg(windows)]
272 return windows::COMPort::open(&self).map(|p| Box::new(p) as Box<dyn SerialPort>);
273
274 #[cfg(not(any(unix, windows)))]
275 Err(Error::new(
276 ErrorKind::Unknown,
277 "open() not implemented for platform",
278 ))
279 }
280
281 /// Open a platform-specific interface to the port with the specified settings
282 #[cfg(unix)]
283 pub fn open_native(self) -> Result<TTYPort> {
284 posix::TTYPort::open(&self)
285 }
286
287 /// Open a platform-specific interface to the port with the specified settings
288 #[cfg(windows)]
289 pub fn open_native(self) -> Result<COMPort> {
290 windows::COMPort::open(&self)
291 }
292}
293
294/// A trait for serial port devices
295///
296/// This trait is all that's necessary to implement a new serial port driver
297/// for a new platform.
298pub trait SerialPort: Send + io::Read + io::Write {
299 // Port settings getters
300
301 /// Returns the name of this port if it exists.
302 ///
303 /// This name may not be the canonical device name and instead be shorthand.
304 /// Additionally it may not exist for virtual ports.
305 fn name(&self) -> Option<String>;
306
307 /// Returns the current baud rate.
308 ///
309 /// This may return a value different from the last specified baud rate depending on the
310 /// platform as some will return the actual device baud rate rather than the last specified
311 /// baud rate.
312 fn baud_rate(&self) -> Result<u32>;
313
314 /// Returns the character size.
315 ///
316 /// This function returns `None` if the character size could not be determined. This may occur
317 /// if the hardware is in an uninitialized state or is using a non-standard character size.
318 /// Setting a baud rate with `set_char_size()` should initialize the character size to a
319 /// supported value.
320 fn data_bits(&self) -> Result<DataBits>;
321
322 /// Returns the flow control mode.
323 ///
324 /// This function returns `None` if the flow control mode could not be determined. This may
325 /// occur if the hardware is in an uninitialized state or is using an unsupported flow control
326 /// mode. Setting a flow control mode with `set_flow_control()` should initialize the flow
327 /// control mode to a supported value.
328 fn flow_control(&self) -> Result<FlowControl>;
329
330 /// Returns the parity-checking mode.
331 ///
332 /// This function returns `None` if the parity mode could not be determined. This may occur if
333 /// the hardware is in an uninitialized state or is using a non-standard parity mode. Setting
334 /// a parity mode with `set_parity()` should initialize the parity mode to a supported value.
335 fn parity(&self) -> Result<Parity>;
336
337 /// Returns the number of stop bits.
338 ///
339 /// This function returns `None` if the number of stop bits could not be determined. This may
340 /// occur if the hardware is in an uninitialized state or is using an unsupported stop bit
341 /// configuration. Setting the number of stop bits with `set_stop-bits()` should initialize the
342 /// stop bits to a supported value.
343 fn stop_bits(&self) -> Result<StopBits>;
344
345 /// Returns the current timeout.
346 fn timeout(&self) -> Duration;
347
348 // Port settings setters
349
350 /// Sets the baud rate.
351 ///
352 /// ## Errors
353 ///
354 /// If the implementation does not support the requested baud rate, this function may return an
355 /// `InvalidInput` error. Even if the baud rate is accepted by `set_baud_rate()`, it may not be
356 /// supported by the underlying hardware.
357 fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()>;
358
359 /// Sets the character size.
360 fn set_data_bits(&mut self, data_bits: DataBits) -> Result<()>;
361
362 /// Sets the flow control mode.
363 fn set_flow_control(&mut self, flow_control: FlowControl) -> Result<()>;
364
365 /// Sets the parity-checking mode.
366 fn set_parity(&mut self, parity: Parity) -> Result<()>;
367
368 /// Sets the number of stop bits.
369 fn set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()>;
370
371 /// Sets the timeout for future I/O operations.
372 fn set_timeout(&mut self, timeout: Duration) -> Result<()>;
373
374 // Functions for setting non-data control signal pins
375
376 /// Sets the state of the RTS (Request To Send) control signal.
377 ///
378 /// Setting a value of `true` asserts the RTS control signal. `false` clears the signal.
379 ///
380 /// ## Errors
381 ///
382 /// This function returns an error if the RTS control signal could not be set to the desired
383 /// state on the underlying hardware:
384 ///
385 /// * `NoDevice` if the device was disconnected.
386 /// * `Io` for any other type of I/O error.
387 fn write_request_to_send(&mut self, level: bool) -> Result<()>;
388
389 /// Writes to the Data Terminal Ready pin
390 ///
391 /// Setting a value of `true` asserts the DTR control signal. `false` clears the signal.
392 ///
393 /// ## Errors
394 ///
395 /// This function returns an error if the DTR control signal could not be set to the desired
396 /// state on the underlying hardware:
397 ///
398 /// * `NoDevice` if the device was disconnected.
399 /// * `Io` for any other type of I/O error.
400 fn write_data_terminal_ready(&mut self, level: bool) -> Result<()>;
401
402 // Functions for reading additional pins
403
404 /// Reads the state of the CTS (Clear To Send) control signal.
405 ///
406 /// This function returns a boolean that indicates whether the CTS control signal is asserted.
407 ///
408 /// ## Errors
409 ///
410 /// This function returns an error if the state of the CTS control signal could not be read
411 /// from the underlying hardware:
412 ///
413 /// * `NoDevice` if the device was disconnected.
414 /// * `Io` for any other type of I/O error.
415 fn read_clear_to_send(&mut self) -> Result<bool>;
416
417 /// Reads the state of the Data Set Ready control signal.
418 ///
419 /// This function returns a boolean that indicates whether the DSR control signal is asserted.
420 ///
421 /// ## Errors
422 ///
423 /// This function returns an error if the state of the DSR control signal could not be read
424 /// from the underlying hardware:
425 ///
426 /// * `NoDevice` if the device was disconnected.
427 /// * `Io` for any other type of I/O error.
428 fn read_data_set_ready(&mut self) -> Result<bool>;
429
430 /// Reads the state of the Ring Indicator control signal.
431 ///
432 /// This function returns a boolean that indicates whether the RI control signal is asserted.
433 ///
434 /// ## Errors
435 ///
436 /// This function returns an error if the state of the RI control signal could not be read from
437 /// the underlying hardware:
438 ///
439 /// * `NoDevice` if the device was disconnected.
440 /// * `Io` for any other type of I/O error.
441 fn read_ring_indicator(&mut self) -> Result<bool>;
442
443 /// Reads the state of the Carrier Detect control signal.
444 ///
445 /// This function returns a boolean that indicates whether the CD control signal is asserted.
446 ///
447 /// ## Errors
448 ///
449 /// This function returns an error if the state of the CD control signal could not be read from
450 /// the underlying hardware:
451 ///
452 /// * `NoDevice` if the device was disconnected.
453 /// * `Io` for any other type of I/O error.
454 fn read_carrier_detect(&mut self) -> Result<bool>;
455
456 /// Gets the number of bytes available to be read from the input buffer.
457 ///
458 /// # Errors
459 ///
460 /// This function may return the following errors:
461 ///
462 /// * `NoDevice` if the device was disconnected.
463 /// * `Io` for any other type of I/O error.
464 fn bytes_to_read(&self) -> Result<u32>;
465
466 /// Get the number of bytes written to the output buffer, awaiting transmission.
467 ///
468 /// # Errors
469 ///
470 /// This function may return the following errors:
471 ///
472 /// * `NoDevice` if the device was disconnected.
473 /// * `Io` for any other type of I/O error.
474 fn bytes_to_write(&self) -> Result<u32>;
475
476 /// Discards all bytes from the serial driver's input buffer and/or output buffer.
477 ///
478 /// # Errors
479 ///
480 /// This function may return the following errors:
481 ///
482 /// * `NoDevice` if the device was disconnected.
483 /// * `Io` for any other type of I/O error.
484 fn clear(&self, buffer_to_clear: ClearBuffer) -> Result<()>;
485
486 // Misc methods
487
488 /// Attempts to clone the `SerialPort`. This allow you to write and read simultaneously from the
489 /// same serial connection. Please note that if you want a real asynchronous serial port you
490 /// should look at [mio-serial](https://crates.io/crates/mio-serial) or
491 /// [tokio-serial](https://crates.io/crates/tokio-serial).
492 ///
493 /// Also, you must be very carefull when changing the settings of a cloned `SerialPort` : since
494 /// the settings are cached on a per object basis, trying to modify them from two different
495 /// objects can cause some nasty behavior.
496 ///
497 /// # Errors
498 ///
499 /// This function returns an error if the serial port couldn't be cloned.
500 fn try_clone(&self) -> Result<Box<dyn SerialPort>>;
501
502 /// Start transmitting a break
503 fn set_break(&self) -> Result<()>;
504
505 /// Stop transmitting a break
506 fn clear_break(&self) -> Result<()>;
507}
508
509/// Contains all possible USB information about a `SerialPort`
510#[derive(Debug, Clone, PartialEq, Eq)]
511pub struct UsbPortInfo {
512 /// Vendor ID
513 pub vid: u16,
514 /// Product ID
515 pub pid: u16,
516 /// Serial number (arbitrary string)
517 pub serial_number: Option<String>,
518 /// Manufacturer (arbitrary string)
519 pub manufacturer: Option<String>,
520 /// Product name (arbitrary string)
521 pub product: Option<String>,
522}
523
524/// The physical type of a `SerialPort`
525#[derive(Debug, Clone, PartialEq, Eq)]
526pub enum SerialPortType {
527 /// The serial port is connected via USB
528 UsbPort(UsbPortInfo),
529 /// The serial port is connected via PCI (permanent port)
530 PciPort,
531 /// The serial port is connected via Bluetooth
532 BluetoothPort,
533 /// It can't be determined how the serial port is connected
534 Unknown,
535}
536
537/// A device-independent implementation of serial port information
538#[derive(Debug, Clone, PartialEq, Eq)]
539pub struct SerialPortInfo {
540 /// The short name of the serial port
541 pub port_name: String,
542 /// The hardware device type that exposes this port
543 pub port_type: SerialPortType,
544}
545
546/// Construct a builder of `SerialPort` objects
547///
548/// `SerialPort` objects are built using the Builder pattern through the `new` function. The
549/// resultant `SerialPortBuilder` object can be copied, reconfigured, and saved making working with
550/// multiple serial ports a little easier.
551///
552/// To open a new serial port:
553/// ```fail
554/// serialport::new("/dev/ttyUSB0", 9600).open().expect("Failed to open port");
555/// ```
556pub fn new<'a>(path: impl Into<std::borrow::Cow<'a, str>>, baud_rate: u32) -> SerialPortBuilder {
557 SerialPortBuilder {
558 path: path.into().into_owned(),
559 baud_rate,
560 data_bits: DataBits::Eight,
561 flow_control: FlowControl::None,
562 parity: Parity::None,
563 stop_bits: StopBits::One,
564 timeout: Duration::from_millis(0),
565 }
566}
567
568/// Returns a list of all serial ports on system
569///
570/// It is not guaranteed that these ports exist or are available even if they're
571/// returned by this function.
572pub fn available_ports() -> Result<Vec<SerialPortInfo>> {
573 #[cfg(unix)]
574 return crate::posix::available_ports();
575
576 #[cfg(windows)]
577 return crate::windows::available_ports();
578
579 #[cfg(not(any(unix, windows)))]
580 Err(Error::new(
581 ErrorKind::Unknown,
582 "available_ports() not implemented for platform",
583 ))
584}