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}