• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::mem::MaybeUninit;
2 use std::os::unix::prelude::*;
3 use std::path::Path;
4 use std::time::{Duration, Instant};
5 use std::{io, mem};
6 
7 use nix::fcntl::{fcntl, OFlag};
8 use nix::{libc, unistd};
9 
10 use crate::posix::ioctl::{self, SerialLines};
11 use crate::posix::termios;
12 use crate::{
13     ClearBuffer, DataBits, Error, ErrorKind, FlowControl, Parity, Result, SerialPort,
14     SerialPortBuilder, StopBits,
15 };
16 
17 /// Convenience method for removing exclusive access from
18 /// a fd and closing it.
close(fd: RawFd)19 fn close(fd: RawFd) {
20     // remove exclusive access
21     let _ = ioctl::tiocnxcl(fd);
22 
23     // On Linux and BSD, we don't need to worry about return
24     // type as EBADF means the fd was never open or is already closed
25     //
26     // Linux and BSD guarantee that for any other error code the
27     // fd is already closed, though MacOSX does not.
28     //
29     // close() also should never be retried, and the error code
30     // in most cases in purely informative
31     let _ = unistd::close(fd);
32 }
33 
34 /// A serial port implementation for POSIX TTY ports
35 ///
36 /// The port will be closed when the value is dropped. This struct
37 /// should not be instantiated directly by using `TTYPort::open()`.
38 /// Instead, use the cross-platform `serialport::new()`. Example:
39 ///
40 /// ```no_run
41 /// let mut port = serialport::new("/dev/ttyS0", 115200).open().expect("Unable to open");
42 /// # let _ = &mut port;
43 /// ```
44 ///
45 /// Note: on macOS, when connecting to a pseudo-terminal (`pty` opened via
46 /// `posix_openpt`), the `baud_rate` should be set to 0; this will be used to
47 /// explicitly _skip_ an attempt to set the baud rate of the file descriptor
48 /// that would otherwise happen via an `ioctl` command.
49 ///
50 /// ```
51 /// use serialport::{TTYPort, SerialPort};
52 ///
53 /// let (mut master, mut slave) = TTYPort::pair().expect("Unable to create ptty pair");
54 /// # let _ = &mut master;
55 /// # let _ = &mut slave;
56 /// // ... elsewhere
57 /// let mut port = TTYPort::open(&serialport::new(slave.name().unwrap(), 0)).expect("Unable to open");
58 /// # let _ = &mut port;
59 /// ```
60 #[derive(Debug)]
61 pub struct TTYPort {
62     fd: OwnedFd,
63     timeout: Duration,
64     exclusive: bool,
65     port_name: Option<String>,
66     #[cfg(any(target_os = "ios", target_os = "macos"))]
67     baud_rate: u32,
68 }
69 
70 /// Specifies the duration of a transmission break
71 #[derive(Clone, Copy, Debug)]
72 pub enum BreakDuration {
73     /// 0.25-0.5s
74     Short,
75     /// Specifies a break duration that is platform-dependent
76     Arbitrary(std::num::NonZeroI32),
77 }
78 
79 impl TTYPort {
80     /// Opens a TTY device as a serial port.
81     ///
82     /// `path` should be the path to a TTY device, e.g., `/dev/ttyS0`.
83     ///
84     /// Ports are opened in exclusive mode by default. If this is undesirable
85     /// behavior, use `TTYPort::set_exclusive(false)`.
86     ///
87     /// If the port settings differ from the default settings, characters received
88     /// before the new settings become active may be garbled. To remove those
89     /// from the receive buffer, call `TTYPort::clear(ClearBuffer::Input)`.
90     ///
91     /// ## Errors
92     ///
93     /// * `NoDevice` if the device could not be opened. This could indicate that
94     ///    the device is already in use.
95     /// * `InvalidInput` if `path` is not a valid device name.
96     /// * `Io` for any other error while opening or initializing the device.
open(builder: &SerialPortBuilder) -> Result<TTYPort>97     pub fn open(builder: &SerialPortBuilder) -> Result<TTYPort> {
98         use nix::fcntl::FcntlArg::F_SETFL;
99         use nix::libc::{cfmakeraw, tcgetattr, tcsetattr};
100 
101         let path = Path::new(&builder.path);
102         let raw_fd = nix::fcntl::open(
103             path,
104             OFlag::O_RDWR | OFlag::O_NOCTTY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC,
105             nix::sys::stat::Mode::empty(),
106         )?;
107         let fd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
108 
109         // Try to claim exclusive access to the port. This is performed even
110         // if the port will later be set as non-exclusive, in order to respect
111         // other applications that may have an exclusive port lock.
112         ioctl::tiocexcl(fd.as_raw_fd())?;
113 
114         let mut termios = MaybeUninit::uninit();
115         nix::errno::Errno::result(unsafe { tcgetattr(fd.as_raw_fd(), termios.as_mut_ptr()) })?;
116         let mut termios = unsafe { termios.assume_init() };
117 
118         // setup TTY for binary serial port access
119         // Enable reading from the port and ignore all modem control lines
120         termios.c_cflag |= libc::CREAD | libc::CLOCAL;
121         // Enable raw mode which disables any implicit processing of the input or output data streams
122         // This also sets no timeout period and a read will block until at least one character is
123         // available.
124         unsafe { cfmakeraw(&mut termios) };
125 
126         // write settings to TTY
127         unsafe { tcsetattr(fd.as_raw_fd(), libc::TCSANOW, &termios) };
128 
129         // Read back settings from port and confirm they were applied correctly
130         let mut actual_termios = MaybeUninit::uninit();
131         unsafe { tcgetattr(fd.as_raw_fd(), actual_termios.as_mut_ptr()) };
132         let actual_termios = unsafe { actual_termios.assume_init() };
133 
134         if actual_termios.c_iflag != termios.c_iflag
135             || actual_termios.c_oflag != termios.c_oflag
136             || actual_termios.c_lflag != termios.c_lflag
137             || actual_termios.c_cflag != termios.c_cflag
138         {
139             return Err(Error::new(
140                 ErrorKind::Unknown,
141                 "Settings did not apply correctly",
142             ));
143         };
144 
145         #[cfg(any(target_os = "ios", target_os = "macos"))]
146         if builder.baud_rate > 0 {
147             unsafe { libc::tcflush(fd.as_raw_fd(), libc::TCIOFLUSH) };
148         }
149 
150         // clear O_NONBLOCK flag
151         fcntl(fd.as_raw_fd(), F_SETFL(nix::fcntl::OFlag::empty()))?;
152 
153         // Configure the low-level port settings
154         let mut termios = termios::get_termios(fd.as_raw_fd())?;
155         termios::set_parity(&mut termios, builder.parity);
156         termios::set_flow_control(&mut termios, builder.flow_control);
157         termios::set_data_bits(&mut termios, builder.data_bits);
158         termios::set_stop_bits(&mut termios, builder.stop_bits);
159         #[cfg(not(any(target_os = "ios", target_os = "macos")))]
160         termios::set_baud_rate(&mut termios, builder.baud_rate)?;
161         #[cfg(any(target_os = "ios", target_os = "macos"))]
162         termios::set_termios(fd.as_raw_fd(), &termios, builder.baud_rate)?;
163         #[cfg(not(any(target_os = "ios", target_os = "macos")))]
164         termios::set_termios(fd.as_raw_fd(), &termios)?;
165 
166         // Return the final port object
167         let mut port = TTYPort {
168             fd: fd,
169             timeout: builder.timeout,
170             exclusive: true,
171             port_name: Some(builder.path.clone()),
172             #[cfg(any(target_os = "ios", target_os = "macos"))]
173             baud_rate: builder.baud_rate,
174         };
175 
176         // Ignore setting DTR for pseudo terminals (indicated by baud_rate == 0).
177         if builder.baud_rate > 0 {
178             if let Some(dtr) = builder.dtr_on_open {
179                 port.write_data_terminal_ready(dtr)?;
180             }
181         }
182 
183         Ok(port)
184     }
185 
186     /// Returns the exclusivity of the port
187     ///
188     /// If a port is exclusive, then trying to open the same device path again
189     /// will fail.
exclusive(&self) -> bool190     pub fn exclusive(&self) -> bool {
191         self.exclusive
192     }
193 
194     /// Sets the exclusivity of the port
195     ///
196     /// If a port is exclusive, then trying to open the same device path again
197     /// will fail.
198     ///
199     /// See the man pages for the tiocexcl and tiocnxcl ioctl's for more details.
200     ///
201     /// ## Errors
202     ///
203     /// * `Io` for any error while setting exclusivity for the port.
set_exclusive(&mut self, exclusive: bool) -> Result<()>204     pub fn set_exclusive(&mut self, exclusive: bool) -> Result<()> {
205         let setting_result = if exclusive {
206             ioctl::tiocexcl(self.fd.as_raw_fd())
207         } else {
208             ioctl::tiocnxcl(self.fd.as_raw_fd())
209         };
210 
211         setting_result?;
212         self.exclusive = exclusive;
213         Ok(())
214     }
215 
set_pin(&mut self, pin: ioctl::SerialLines, level: bool) -> Result<()>216     fn set_pin(&mut self, pin: ioctl::SerialLines, level: bool) -> Result<()> {
217         if level {
218             ioctl::tiocmbis(self.fd.as_raw_fd(), pin)
219         } else {
220             ioctl::tiocmbic(self.fd.as_raw_fd(), pin)
221         }
222     }
223 
read_pin(&mut self, pin: ioctl::SerialLines) -> Result<bool>224     fn read_pin(&mut self, pin: ioctl::SerialLines) -> Result<bool> {
225         ioctl::tiocmget(self.fd.as_raw_fd()).map(|pins| pins.contains(pin))
226     }
227 
228     /// Create a pair of pseudo serial terminals
229     ///
230     /// ## Returns
231     /// Two connected `TTYPort` objects: `(master, slave)`
232     ///
233     /// ## Errors
234     /// Attempting any IO or parameter settings on the slave tty after the master
235     /// tty is closed will return errors.
236     ///
237     /// On some platforms manipulating the master port will fail and only
238     /// modifying the slave port is possible.
239     ///
240     /// ## Examples
241     ///
242     /// ```
243     /// use serialport::TTYPort;
244     ///
245     /// let (mut master, mut slave) = TTYPort::pair().unwrap();
246     ///
247     /// # let _ = &mut master;
248     /// # let _ = &mut slave;
249     /// ```
pair() -> Result<(Self, Self)>250     pub fn pair() -> Result<(Self, Self)> {
251         // Open the next free pty.
252         let next_pty_fd = nix::pty::posix_openpt(nix::fcntl::OFlag::O_RDWR)?;
253 
254         // Grant access to the associated slave pty
255         nix::pty::grantpt(&next_pty_fd)?;
256 
257         // Unlock the slave pty
258         nix::pty::unlockpt(&next_pty_fd)?;
259 
260         // Get the path of the attached slave ptty
261         #[cfg(not(any(
262             target_os = "linux",
263             target_os = "android",
264             target_os = "emscripten",
265             target_os = "fuchsia"
266         )))]
267         let ptty_name = unsafe { nix::pty::ptsname(&next_pty_fd)? };
268 
269         #[cfg(any(
270             target_os = "linux",
271             target_os = "android",
272             target_os = "emscripten",
273             target_os = "fuchsia"
274         ))]
275         let ptty_name = nix::pty::ptsname_r(&next_pty_fd)?;
276 
277         // Open the slave port
278         #[cfg(any(target_os = "ios", target_os = "macos"))]
279         let baud_rate = 9600;
280         let fd = nix::fcntl::open(
281             Path::new(&ptty_name),
282             OFlag::O_RDWR | OFlag::O_NOCTTY | OFlag::O_NONBLOCK,
283             nix::sys::stat::Mode::empty(),
284         )?;
285 
286         // Set the port to a raw state. Using these ports will not work without this.
287         let mut termios = MaybeUninit::uninit();
288         let res = unsafe { crate::posix::tty::libc::tcgetattr(fd, termios.as_mut_ptr()) };
289         if let Err(e) = nix::errno::Errno::result(res) {
290             close(fd);
291             return Err(e.into());
292         }
293         let mut termios = unsafe { termios.assume_init() };
294         unsafe { crate::posix::tty::libc::cfmakeraw(&mut termios) };
295         unsafe { crate::posix::tty::libc::tcsetattr(fd, libc::TCSANOW, &termios) };
296 
297         fcntl(
298             fd,
299             nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::OFlag::empty()),
300         )?;
301         let fd = unsafe { OwnedFd::from_raw_fd(fd) };
302 
303         let slave_tty = TTYPort {
304             fd,
305             timeout: Duration::from_millis(100),
306             exclusive: true,
307             port_name: Some(ptty_name),
308             #[cfg(any(target_os = "ios", target_os = "macos"))]
309             baud_rate,
310         };
311 
312         // Manually construct the master port here because the
313         // `tcgetattr()` doesn't work on Mac, Solaris, and maybe other
314         // BSDs when used on the master port.
315         let master_tty = TTYPort {
316             fd: unsafe { OwnedFd::from_raw_fd(next_pty_fd.into_raw_fd()) },
317             timeout: Duration::from_millis(100),
318             exclusive: true,
319             port_name: None,
320             #[cfg(any(target_os = "ios", target_os = "macos"))]
321             baud_rate,
322         };
323 
324         Ok((master_tty, slave_tty))
325     }
326 
327     /// Sends 0-valued bits over the port for a set duration
send_break(&self, duration: BreakDuration) -> Result<()>328     pub fn send_break(&self, duration: BreakDuration) -> Result<()> {
329         match duration {
330             BreakDuration::Short => nix::sys::termios::tcsendbreak(self.fd.as_fd(), 0),
331             BreakDuration::Arbitrary(n) => nix::sys::termios::tcsendbreak(self.fd.as_fd(), n.get()),
332         }
333         .map_err(|e| e.into())
334     }
335 
336     /// Attempts to clone the `SerialPort`. This allow you to write and read simultaneously from the
337     /// same serial connection. Please note that if you want a real asynchronous serial port you
338     /// should look at [mio-serial](https://crates.io/crates/mio-serial) or
339     /// [tokio-serial](https://crates.io/crates/tokio-serial).
340     ///
341     /// Also, you must be very careful when changing the settings of a cloned `SerialPort` : since
342     /// the settings are cached on a per object basis, trying to modify them from two different
343     /// objects can cause some nasty behavior.
344     ///
345     /// This is the same as `SerialPort::try_clone()` but returns the concrete type instead.
346     ///
347     /// # Errors
348     ///
349     /// This function returns an error if the serial port couldn't be cloned.
try_clone_native(&self) -> Result<TTYPort>350     pub fn try_clone_native(&self) -> Result<TTYPort> {
351         let fd_cloned: i32 = fcntl(
352             self.fd.as_raw_fd(),
353             nix::fcntl::F_DUPFD_CLOEXEC(self.fd.as_raw_fd()),
354         )?;
355         Ok(TTYPort {
356             fd: unsafe { OwnedFd::from_raw_fd(fd_cloned) },
357             exclusive: self.exclusive,
358             port_name: self.port_name.clone(),
359             timeout: self.timeout,
360             #[cfg(any(target_os = "ios", target_os = "macos"))]
361             baud_rate: self.baud_rate,
362         })
363     }
364 }
365 
366 impl AsRawFd for TTYPort {
as_raw_fd(&self) -> RawFd367     fn as_raw_fd(&self) -> RawFd {
368         self.fd.as_raw_fd()
369     }
370 }
371 
372 impl IntoRawFd for TTYPort {
into_raw_fd(self) -> RawFd373     fn into_raw_fd(self) -> RawFd {
374         // Pull just the file descriptor out. We also prevent the destructor
375         // from being run by calling `mem::forget`. If we didn't do this, the
376         // port would be closed, which would make `into_raw_fd` unusable.
377         let fd = self.fd.as_raw_fd();
378         mem::forget(self);
379         fd
380     }
381 }
382 
383 /// Get the baud speed for a port from its file descriptor
384 #[cfg(any(target_os = "ios", target_os = "macos"))]
get_termios_speed(fd: RawFd) -> u32385 fn get_termios_speed(fd: RawFd) -> u32 {
386     let mut termios = MaybeUninit::uninit();
387     let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
388     nix::errno::Errno::result(res).expect("Failed to get termios data");
389     let termios = unsafe { termios.assume_init() };
390     assert_eq!(termios.c_ospeed, termios.c_ispeed);
391     termios.c_ospeed as u32
392 }
393 
394 impl FromRawFd for TTYPort {
from_raw_fd(fd: RawFd) -> Self395     unsafe fn from_raw_fd(fd: RawFd) -> Self {
396         TTYPort {
397             fd: unsafe { OwnedFd::from_raw_fd(fd) },
398             timeout: Duration::from_millis(100),
399             exclusive: ioctl::tiocexcl(fd).is_ok(),
400             // It is not trivial to get the file path corresponding to a file descriptor.
401             // We'll punt on it and set it to `None` here.
402             port_name: None,
403             // It's not guaranteed that the baud rate in the `termios` struct is correct, as
404             // setting an arbitrary baud rate via the `iossiospeed` ioctl overrides that value,
405             // but extract that value anyways as a best-guess of the actual baud rate.
406             #[cfg(any(target_os = "ios", target_os = "macos"))]
407             baud_rate: get_termios_speed(fd),
408         }
409     }
410 }
411 
412 impl io::Read for TTYPort {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>413     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
414         if let Err(e) = super::poll::wait_read_fd(self.fd.as_fd(), self.timeout) {
415             return Err(io::Error::from(Error::from(e)));
416         }
417 
418         nix::unistd::read(self.fd.as_raw_fd(), buf).map_err(|e| io::Error::from(Error::from(e)))
419     }
420 }
421 
422 impl io::Write for TTYPort {
write(&mut self, buf: &[u8]) -> io::Result<usize>423     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
424         if let Err(e) = super::poll::wait_write_fd(self.fd.as_fd(), self.timeout) {
425             return Err(io::Error::from(Error::from(e)));
426         }
427 
428         nix::unistd::write(self.fd.as_fd(), buf).map_err(|e| io::Error::from(Error::from(e)))
429     }
430 
flush(&mut self) -> io::Result<()>431     fn flush(&mut self) -> io::Result<()> {
432         let timeout = Instant::now() + self.timeout;
433         loop {
434             return match nix::sys::termios::tcdrain(self.fd.as_fd()) {
435                 Ok(_) => Ok(()),
436                 Err(nix::errno::Errno::EINTR) => {
437                     // Retry flushing. But only up to the ports timeout for not retrying
438                     // indefinitely in case that it gets interrupted again.
439                     if Instant::now() < timeout {
440                         continue;
441                     } else {
442                         Err(io::Error::new(
443                             io::ErrorKind::TimedOut,
444                             "timeout for retrying flush reached",
445                         ))
446                     }
447                 }
448                 Err(_) => Err(io::Error::new(io::ErrorKind::Other, "flush failed")),
449             };
450         }
451     }
452 }
453 
454 impl SerialPort for TTYPort {
name(&self) -> Option<String>455     fn name(&self) -> Option<String> {
456         self.port_name.clone()
457     }
458 
459     /// Returns the port's baud rate
460     ///
461     /// On some platforms this will be the actual device baud rate, which may differ from the
462     /// desired baud rate.
463     #[cfg(any(
464         target_os = "android",
465         all(
466             target_os = "linux",
467             not(any(
468                 target_env = "musl",
469                 target_arch = "powerpc",
470                 target_arch = "powerpc64"
471             ))
472         )
473     ))]
baud_rate(&self) -> Result<u32>474     fn baud_rate(&self) -> Result<u32> {
475         let termios2 = ioctl::tcgets2(self.fd.as_raw_fd())?;
476 
477         assert!(termios2.c_ospeed == termios2.c_ispeed);
478 
479         Ok(termios2.c_ospeed)
480     }
481 
482     /// Returns the port's baud rate
483     ///
484     /// On some platforms this will be the actual device baud rate, which may differ from the
485     /// desired baud rate.
486     #[cfg(any(
487         target_os = "dragonfly",
488         target_os = "freebsd",
489         target_os = "netbsd",
490         target_os = "openbsd"
491     ))]
baud_rate(&self) -> Result<u32>492     fn baud_rate(&self) -> Result<u32> {
493         let termios = termios::get_termios(self.fd.as_raw_fd())?;
494 
495         let ospeed = unsafe { libc::cfgetospeed(&termios) };
496         let ispeed = unsafe { libc::cfgetispeed(&termios) };
497 
498         assert!(ospeed == ispeed);
499 
500         Ok(ospeed as u32)
501     }
502 
503     /// Returns the port's baud rate
504     ///
505     /// On some platforms this will be the actual device baud rate, which may differ from the
506     /// desired baud rate.
507     #[cfg(any(target_os = "ios", target_os = "macos"))]
baud_rate(&self) -> Result<u32>508     fn baud_rate(&self) -> Result<u32> {
509         Ok(self.baud_rate)
510     }
511 
512     /// Returns the port's baud rate
513     ///
514     /// On some platforms this will be the actual device baud rate, which may differ from the
515     /// desired baud rate.
516     #[cfg(all(
517         target_os = "linux",
518         any(
519             target_env = "musl",
520             target_arch = "powerpc",
521             target_arch = "powerpc64"
522         )
523     ))]
baud_rate(&self) -> Result<u32>524     fn baud_rate(&self) -> Result<u32> {
525         use self::libc::{
526             B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000,
527             B460800, B500000, B576000, B921600,
528         };
529         use self::libc::{
530             B110, B115200, B1200, B134, B150, B1800, B19200, B200, B230400, B2400, B300, B38400,
531             B4800, B50, B57600, B600, B75, B9600,
532         };
533 
534         let termios = termios::get_termios(self.fd.as_raw_fd())?;
535         let ospeed = unsafe { libc::cfgetospeed(&termios) };
536         let ispeed = unsafe { libc::cfgetispeed(&termios) };
537 
538         assert!(ospeed == ispeed);
539 
540         let res: u32 = match ospeed {
541             B50 => 50,
542             B75 => 75,
543             B110 => 110,
544             B134 => 134,
545             B150 => 150,
546             B200 => 200,
547             B300 => 300,
548             B600 => 600,
549             B1200 => 1200,
550             B1800 => 1800,
551             B2400 => 2400,
552             B4800 => 4800,
553             B9600 => 9600,
554             B19200 => 19_200,
555             B38400 => 38_400,
556             B57600 => 57_600,
557             B115200 => 115_200,
558             B230400 => 230_400,
559             B460800 => 460_800,
560             B500000 => 500_000,
561             B576000 => 576_000,
562             B921600 => 921_600,
563             B1000000 => 1_000_000,
564             B1152000 => 1_152_000,
565             B1500000 => 1_500_000,
566             B2000000 => 2_000_000,
567             B2500000 => 2_500_000,
568             B3000000 => 3_000_000,
569             B3500000 => 3_500_000,
570             B4000000 => 4_000_000,
571             _ => unreachable!(),
572         };
573 
574         Ok(res)
575     }
576 
data_bits(&self) -> Result<DataBits>577     fn data_bits(&self) -> Result<DataBits> {
578         let termios = termios::get_termios(self.fd.as_raw_fd())?;
579         match termios.c_cflag & libc::CSIZE {
580             libc::CS8 => Ok(DataBits::Eight),
581             libc::CS7 => Ok(DataBits::Seven),
582             libc::CS6 => Ok(DataBits::Six),
583             libc::CS5 => Ok(DataBits::Five),
584             _ => Err(Error::new(
585                 ErrorKind::Unknown,
586                 "Invalid data bits setting encountered",
587             )),
588         }
589     }
590 
flow_control(&self) -> Result<FlowControl>591     fn flow_control(&self) -> Result<FlowControl> {
592         let termios = termios::get_termios(self.fd.as_raw_fd())?;
593         if termios.c_cflag & libc::CRTSCTS == libc::CRTSCTS {
594             Ok(FlowControl::Hardware)
595         } else if termios.c_iflag & (libc::IXON | libc::IXOFF) == (libc::IXON | libc::IXOFF) {
596             Ok(FlowControl::Software)
597         } else {
598             Ok(FlowControl::None)
599         }
600     }
601 
parity(&self) -> Result<Parity>602     fn parity(&self) -> Result<Parity> {
603         let termios = termios::get_termios(self.fd.as_raw_fd())?;
604         if termios.c_cflag & libc::PARENB == libc::PARENB {
605             if termios.c_cflag & libc::PARODD == libc::PARODD {
606                 Ok(Parity::Odd)
607             } else {
608                 Ok(Parity::Even)
609             }
610         } else {
611             Ok(Parity::None)
612         }
613     }
614 
stop_bits(&self) -> Result<StopBits>615     fn stop_bits(&self) -> Result<StopBits> {
616         let termios = termios::get_termios(self.fd.as_raw_fd())?;
617         if termios.c_cflag & libc::CSTOPB == libc::CSTOPB {
618             Ok(StopBits::Two)
619         } else {
620             Ok(StopBits::One)
621         }
622     }
623 
timeout(&self) -> Duration624     fn timeout(&self) -> Duration {
625         self.timeout
626     }
627 
628     #[cfg(any(
629         target_os = "android",
630         target_os = "dragonfly",
631         target_os = "freebsd",
632         target_os = "netbsd",
633         target_os = "openbsd",
634         target_os = "linux"
635     ))]
set_baud_rate(&mut self, baud_rate: u32) -> Result<()>636     fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
637         let mut termios = termios::get_termios(self.fd.as_raw_fd())?;
638         termios::set_baud_rate(&mut termios, baud_rate)?;
639         termios::set_termios(self.fd.as_raw_fd(), &termios)
640     }
641 
642     // Mac OS needs special logic for setting arbitrary baud rates.
643     #[cfg(any(target_os = "ios", target_os = "macos"))]
set_baud_rate(&mut self, baud_rate: u32) -> Result<()>644     fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
645         ioctl::iossiospeed(self.fd.as_raw_fd(), &(baud_rate as libc::speed_t))?;
646         self.baud_rate = baud_rate;
647         Ok(())
648     }
649 
set_flow_control(&mut self, flow_control: FlowControl) -> Result<()>650     fn set_flow_control(&mut self, flow_control: FlowControl) -> Result<()> {
651         let mut termios = termios::get_termios(self.fd.as_raw_fd())?;
652         termios::set_flow_control(&mut termios, flow_control);
653         #[cfg(any(target_os = "ios", target_os = "macos"))]
654         return termios::set_termios(self.fd.as_raw_fd(), &termios, self.baud_rate);
655         #[cfg(not(any(target_os = "ios", target_os = "macos")))]
656         return termios::set_termios(self.fd.as_raw_fd(), &termios);
657     }
658 
set_parity(&mut self, parity: Parity) -> Result<()>659     fn set_parity(&mut self, parity: Parity) -> Result<()> {
660         let mut termios = termios::get_termios(self.fd.as_raw_fd())?;
661         termios::set_parity(&mut termios, parity);
662         #[cfg(any(target_os = "ios", target_os = "macos"))]
663         return termios::set_termios(self.fd.as_raw_fd(), &termios, self.baud_rate);
664         #[cfg(not(any(target_os = "ios", target_os = "macos")))]
665         return termios::set_termios(self.fd.as_raw_fd(), &termios);
666     }
667 
set_data_bits(&mut self, data_bits: DataBits) -> Result<()>668     fn set_data_bits(&mut self, data_bits: DataBits) -> Result<()> {
669         let mut termios = termios::get_termios(self.fd.as_raw_fd())?;
670         termios::set_data_bits(&mut termios, data_bits);
671         #[cfg(any(target_os = "ios", target_os = "macos"))]
672         return termios::set_termios(self.fd.as_raw_fd(), &termios, self.baud_rate);
673         #[cfg(not(any(target_os = "ios", target_os = "macos")))]
674         return termios::set_termios(self.fd.as_raw_fd(), &termios);
675     }
676 
set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()>677     fn set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()> {
678         let mut termios = termios::get_termios(self.fd.as_raw_fd())?;
679         termios::set_stop_bits(&mut termios, stop_bits);
680         #[cfg(any(target_os = "ios", target_os = "macos"))]
681         return termios::set_termios(self.fd.as_raw_fd(), &termios, self.baud_rate);
682         #[cfg(not(any(target_os = "ios", target_os = "macos")))]
683         return termios::set_termios(self.fd.as_raw_fd(), &termios);
684     }
685 
set_timeout(&mut self, timeout: Duration) -> Result<()>686     fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
687         self.timeout = timeout;
688         Ok(())
689     }
690 
write_request_to_send(&mut self, level: bool) -> Result<()>691     fn write_request_to_send(&mut self, level: bool) -> Result<()> {
692         self.set_pin(SerialLines::REQUEST_TO_SEND, level)
693     }
694 
write_data_terminal_ready(&mut self, level: bool) -> Result<()>695     fn write_data_terminal_ready(&mut self, level: bool) -> Result<()> {
696         self.set_pin(SerialLines::DATA_TERMINAL_READY, level)
697     }
698 
read_clear_to_send(&mut self) -> Result<bool>699     fn read_clear_to_send(&mut self) -> Result<bool> {
700         self.read_pin(SerialLines::CLEAR_TO_SEND)
701     }
702 
read_data_set_ready(&mut self) -> Result<bool>703     fn read_data_set_ready(&mut self) -> Result<bool> {
704         self.read_pin(SerialLines::DATA_SET_READY)
705     }
706 
read_ring_indicator(&mut self) -> Result<bool>707     fn read_ring_indicator(&mut self) -> Result<bool> {
708         self.read_pin(SerialLines::RING)
709     }
710 
read_carrier_detect(&mut self) -> Result<bool>711     fn read_carrier_detect(&mut self) -> Result<bool> {
712         self.read_pin(SerialLines::DATA_CARRIER_DETECT)
713     }
714 
bytes_to_read(&self) -> Result<u32>715     fn bytes_to_read(&self) -> Result<u32> {
716         ioctl::fionread(self.fd.as_raw_fd())
717     }
718 
bytes_to_write(&self) -> Result<u32>719     fn bytes_to_write(&self) -> Result<u32> {
720         ioctl::tiocoutq(self.fd.as_raw_fd())
721     }
722 
clear(&self, buffer_to_clear: ClearBuffer) -> Result<()>723     fn clear(&self, buffer_to_clear: ClearBuffer) -> Result<()> {
724         let buffer_id = match buffer_to_clear {
725             ClearBuffer::Input => libc::TCIFLUSH,
726             ClearBuffer::Output => libc::TCOFLUSH,
727             ClearBuffer::All => libc::TCIOFLUSH,
728         };
729 
730         let res = unsafe { nix::libc::tcflush(self.fd.as_raw_fd(), buffer_id) };
731 
732         nix::errno::Errno::result(res)
733             .map(|_| ())
734             .map_err(|e| e.into())
735     }
736 
try_clone(&self) -> Result<Box<dyn SerialPort>>737     fn try_clone(&self) -> Result<Box<dyn SerialPort>> {
738         match self.try_clone_native() {
739             Ok(p) => Ok(Box::new(p)),
740             Err(e) => Err(e),
741         }
742     }
743 
set_break(&self) -> Result<()>744     fn set_break(&self) -> Result<()> {
745         ioctl::tiocsbrk(self.fd.as_raw_fd())
746     }
747 
clear_break(&self) -> Result<()>748     fn clear_break(&self) -> Result<()> {
749         ioctl::tioccbrk(self.fd.as_raw_fd())
750     }
751 }
752 
753 #[test]
test_ttyport_into_raw_fd()754 fn test_ttyport_into_raw_fd() {
755     // `master` must be used here as Dropping it causes slave to be deleted by the OS.
756     // TODO: Convert this to a statement-level attribute once
757     //       https://github.com/rust-lang/rust/issues/15701 is on stable.
758     // FIXME: Create a mutex across all tests for using `TTYPort::pair()` as it's not threadsafe
759     #![allow(unused_variables)]
760     let (master, slave) = TTYPort::pair().expect("Unable to create ptty pair");
761 
762     // First test with the master
763     let master_fd = master.into_raw_fd();
764     let mut termios = MaybeUninit::uninit();
765     let res = unsafe { nix::libc::tcgetattr(master_fd, termios.as_mut_ptr()) };
766     if res != 0 {
767         close(master_fd);
768         panic!("tcgetattr on the master port failed");
769     }
770 
771     // And then the slave
772     let slave_fd = slave.into_raw_fd();
773     let res = unsafe { nix::libc::tcgetattr(slave_fd, termios.as_mut_ptr()) };
774     if res != 0 {
775         close(slave_fd);
776         panic!("tcgetattr on the master port failed");
777     }
778     close(master_fd);
779     close(slave_fd);
780 }
781