Skip to content

Commit

Permalink
Merge #843
Browse files Browse the repository at this point in the history
843: Support arbitrary baud rates on BSDs r=Susurrus a=Susurrus

Closes #842
  • Loading branch information
bors[bot] committed Jan 26, 2018
2 parents ffe3e50 + b94ea23 commit 1580ffe
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 51 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
291 changes: 240 additions & 51 deletions src/sys/termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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))]
Expand Down Expand Up @@ -354,6 +475,13 @@ impl From<libc::speed_t> for BaudRate {
}
}

// TODO: Include `TryFrom<u32> for BaudRate` once that API stabilizes
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.
Expand Down Expand Up @@ -761,22 +889,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)](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<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)](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)
}
}
}

/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
Expand All @@ -793,40 +1016,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)).
///
Expand Down
1 change: 1 addition & 0 deletions test/sys/test_termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down

0 comments on commit 1580ffe

Please sign in to comment.