summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryant Mairs <bryantmairs@google.com>2018-01-23 21:45:08 -0800
committerBryant Mairs <bryantmairs@google.com>2018-01-25 20:37:33 -0800
commit0abc8fef5b5e729a2e1bdc3460b9442519adaf9c (patch)
tree9f3b905fde14697633f8eec53baf4c1f8f517b7e
parentffe3e50e4d1cb8cfb71d665e72d540122a31a047 (diff)
downloadnix-0abc8fef5b5e729a2e1bdc3460b9442519adaf9c.zip
Support arbitrary baud rates on BSDs
-rw-r--r--CHANGELOG.md4
-rw-r--r--src/sys/termios.rs297
-rw-r--r--test/sys/test_termios.rs1
3 files changed, 251 insertions, 51 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c21ef19..9f742233 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -98,6 +98,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
([#814](https://github.com/nix-rust/nix/pull/814))
- Removed return type from `pause`.
([#829](https://github.com/nix-rust/nix/pull/829))
+- Changed the termios APIs to allow for using a `u32` instead of the `BaudRate`
+ enum on BSD platforms to support arbitrary baud rates. See the module docs for
+ `nix::sys::termios` for more details.
+ ([#843](https://github.com/nix-rust/nix/pull/843))
### Fixed
- Fix compilation and tests for OpenBSD targets
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index b768caa8..352dcb79 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -42,7 +42,125 @@
//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
//! termios.control_flags |= ControlFlags::CS5;
//! ```
-
+//!
+//! # Baud rates
+//!
+//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
+//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
+//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
+//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
+//! conventions:
+//!
+//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
+//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//!
+//! The most common use case of specifying a baud rate using the enum will work the same across
+//! platforms:
+//!
+//! ```rust
+//! # #[macro_use] extern crate nix;
+//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t = unsafe { Termios::default_uninit() };
+//! cfsetispeed(&mut t, BaudRate::B9600);
+//! cfsetospeed(&mut t, BaudRate::B9600);
+//! cfsetspeed(&mut t, BaudRate::B9600);
+//! # }
+//! ```
+//!
+//! Additionally round-tripping baud rates is consistent across platforms:
+//!
+//! ```rust
+//! # extern crate nix;
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t = unsafe { Termios::default_uninit() };
+//! # cfsetspeed(&mut t, BaudRate::B9600);
+//! let speed = cfgetispeed(&t);
+//! assert!(speed == cfgetospeed(&t));
+//! cfsetispeed(&mut t, speed);
+//! # }
+//! ```
+//!
+//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
+//!
+// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version
+#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
+ doc = " ```rust,ignore")]
+#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
+ doc = " ```rust")]
+//! # extern crate nix;
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t = unsafe { Termios::default_uninit() };
+//! # cfsetspeed(&mut t, BaudRate::B9600);
+//! assert!(cfgetispeed(&t) == BaudRate::B9600);
+//! assert!(cfgetospeed(&t) == BaudRate::B9600);
+//! # }
+//! ```
+//!
+//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
+//!
+// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version
+#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
+ doc = " ```rust")]
+#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
+ doc = " ```rust,ignore")]
+//! # extern crate nix;
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t = unsafe { Termios::default_uninit() };
+//! # cfsetspeed(&mut t, 9600u32);
+//! assert!(cfgetispeed(&t) == 9600u32);
+//! assert!(cfgetospeed(&t) == 9600u32);
+//! # }
+//! ```
+//!
+//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
+//!
+// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version
+#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
+ doc = " ```rust")]
+#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
+ doc = " ```rust,ignore")]
+//! # extern crate nix;
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t = unsafe { Termios::default_uninit() };
+//! # cfsetspeed(&mut t, 9600u32);
+//! assert!(cfgetispeed(&t) == BaudRate::B9600.into());
+//! assert!(u32::from(BaudRate::B9600) == 9600u32);
+//! # }
+//! ```
+//!
+//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
+//! by specifying baud rates directly using `u32`s:
+//!
+// FIXME: Replace `ignore` with `compile_fail` once 1.22 is the minimum support Rust version
+#![cfg_attr(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd"),
+ doc = " ```rust")]
+#![cfg_attr(not(any(target_os = "freebsd", target_os = "dragonfly", target_os = "ios",
+ target_os = "macos", target_os = "netbsd", target_os = "openbsd")),
+ doc = " ```rust,ignore")]
+//! # extern crate nix;
+//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t = unsafe { Termios::default_uninit() };
+//! cfsetispeed(&mut t, 9600u32);
+//! cfsetospeed(&mut t, 9600u32);
+//! cfsetspeed(&mut t, 9600u32);
+//! # }
+//! ```
use Result;
use errno::Errno;
use libc::{self, c_int, tcflag_t};
@@ -173,7 +291,10 @@ impl From<Termios> for libc::termios {
}
libc_enum!{
- /// Baud rates supported by the system
+ /// Baud rates supported by the system.
+ ///
+ /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
+ /// enum.
///
/// B0 is special and will disable the port.
#[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
@@ -354,6 +475,19 @@ impl From<libc::speed_t> for BaudRate {
}
}
+// TODO: Include `TryFrom<u32> for BaudRate` once that API stabilizes
+#[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+impl From<BaudRate> for u32 {
+ fn from(b: BaudRate) -> u32 {
+ b as u32
+ }
+}
+
// TODO: Add TCSASOFT, which will require treating this as a bitfield.
libc_enum! {
/// Specify when a port configuration change should occur.
@@ -761,22 +895,117 @@ libc_bitflags! {
}
}
-/// Get input baud rate (see
-/// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
-///
-/// `cfgetispeed()` extracts the input baud rate from the given Termios structure.
-pub fn cfgetispeed(termios: &Termios) -> BaudRate {
- let inner_termios = termios.get_libc_termios();
- unsafe { libc::cfgetispeed(&*inner_termios) }.into()
-}
+cfg_if!{
+ if #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))] {
+ /// Get input baud rate (see
+ /// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ ///
+ /// `cfgetispeed()` extracts the input baud rate from the given Termios structure.
+ pub fn cfgetispeed(termios: &Termios) -> u32 {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
+ }
-/// Get output baud rate (see
-/// [cfgetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
-///
-/// `cfgetospeed()` extracts the output baud rate from the given Termios structure.
-pub fn cfgetospeed(termios: &Termios) -> BaudRate {
- let inner_termios = termios.get_libc_termios();
- unsafe { libc::cfgetospeed(&*inner_termios) }.into()
+ /// Get output baud rate (see
+ /// [cfgetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ ///
+ /// `cfgetospeed()` extracts the output baud rate from the given Termios structure.
+ pub fn cfgetospeed(termios: &Termios) -> u32 {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
+ }
+
+ /// Set input baud rate (see
+ /// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ ///
+ /// `cfsetispeed()` sets the intput baud rate in the given Termios structure.
+ pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set output baud rate (see
+ /// [cfsetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ ///
+ /// `cfsetospeed()` sets the output baud rate in the given termios structure.
+ pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set both the input and output baud rates (see
+ /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
+ ///
+ /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
+ /// this is part of the 4.4BSD standard and not part of POSIX.
+ pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+ } else {
+ /// Get input baud rate (see
+ /// [cfgetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ ///
+ /// `cfgetispeed()` extracts the input baud rate from the given Termios structure.
+ pub fn cfgetispeed(termios: &Termios) -> BaudRate {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) }.into()
+ }
+
+ /// Get output baud rate (see
+ /// [cfgetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ ///
+ /// `cfgetospeed()` extracts the output baud rate from the given Termios structure.
+ pub fn cfgetospeed(termios: &Termios) -> BaudRate {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) }.into()
+ }
+
+ /// Set input baud rate (see
+ /// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ ///
+ /// `cfsetispeed()` sets the intput baud rate in the given Termios structure.
+ pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set output baud rate (see
+ /// [cfsetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ ///
+ /// `cfsetospeed()` sets the output baud rate in the given termios structure.
+ pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+
+ /// Set both the input and output baud rates (see
+ /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
+ ///
+ /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
+ /// this is part of the 4.4BSD standard and not part of POSIX.
+ pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
+ termios.update_wrapper();
+ Errno::result(res).map(drop)
+ }
+ }
}
/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
@@ -793,40 +1022,6 @@ pub fn cfmakeraw(termios: &mut Termios) {
termios.update_wrapper();
}
-/// Set input baud rate (see
-/// [cfsetispeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
-///
-/// `cfsetispeed()` sets the intput baud rate in the given Termios structure.
-pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
- let inner_termios = unsafe { termios.get_libc_termios_mut() };
- let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
- termios.update_wrapper();
- Errno::result(res).map(drop)
-}
-
-/// Set output baud rate (see
-/// [cfsetospeed(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
-///
-/// `cfsetospeed()` sets the output baud rate in the given termios structure.
-pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
- let inner_termios = unsafe { termios.get_libc_termios_mut() };
- let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
- termios.update_wrapper();
- Errno::result(res).map(drop)
-}
-
-/// Set both the input and output baud rates (see
-/// [termios(3)](http://man7.org/linux/man-pages/man3/termios.3.html)).
-///
-/// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
-/// this is part of the 4.4BSD standard and not part of POSIX.
-pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
- let inner_termios = unsafe { termios.get_libc_termios_mut() };
- let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
- termios.update_wrapper();
- Errno::result(res).map(drop)
-}
-
/// Return the configuration of a port
/// [tcgetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
///
diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs
index 903a5353..831fc18b 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -27,6 +27,7 @@ fn test_tcgetattr_pty() {
close(pty.master).expect("closing the master failed");
close(pty.slave).expect("closing the slave failed");
}
+
// Test tcgetattr on something that isn't a terminal
#[test]
fn test_tcgetattr_enotty() {