diff --git a/.travis.yml b/.travis.yml
index f0e94c67c3..aa23815797 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -108,23 +108,6 @@ matrix:
       rust: nightly
 
   allow_failures:
-    # iOS is still being worked on, so for now don't block on compilation failures
-    - env: TARGET=aarch64-apple-ios DISABLE_TESTS=1
-      rust: 1.13.0
-      os: osx
-    - env: TARGET=armv7-apple-ios DISABLE_TESTS=1
-      rust: 1.13.0
-      os: osx
-    - env: TARGET=armv7s-apple-ios DISABLE_TESTS=1
-      rust: 1.13.0
-      os: osx
-    - env: TARGET=i386-apple-ios DISABLE_TESTS=1
-      rust: 1.13.0
-      os: osx
-    - env: TARGET=x86_64-apple-ios DISABLE_TESTS=1
-      rust: 1.13.0
-      os: osx
-
     # Planning to add these targets, but they can fail for now
     - env: TARGET=mips64-unknown-linux-gnuabi64
       rust: 1.13.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4ceae9c275..b2f82ef651 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,30 +21,32 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Added `nix::ptrace::{ptrace_get_data, ptrace_getsiginfo, ptrace_setsiginfo
   and nix::Error::UnsupportedOperation}`
   ([#614](https://github.com/nix-rust/nix/pull/614))
+- Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527))
 
 ### Changed
-- Changed ioctl! write to take argument by value instead as pointer.
-  If you need a pointer as argument, use ioctl! write buf.
+- Changed `ioctl!(write ...)` to take argument by value instead as pointer.
+  If you need a pointer as argument, use `ioctl!(write buf ...)`.
   ([#626](https://github.com/nix-rust/nix/pull/626))
 - Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe.
   ([#559](https://github.com/nix-rust/nix/pull/559))
-- Minimum supported Rust version is now 1.13
+- Minimum supported Rust version is now 1.13.
 - Removed `revents` argument from `PollFd::new()` as it's an output argument and
   will be overwritten regardless of value.
   ([#542](https://github.com/nix-rust/nix/pull/542))
 - Changed type signature of `sys::select::FdSet::contains` to make `self`
   immutable ([#564](https://github.com/nix-rust/nix/pull/564))
-- Changed type of `sched::sched_setaffinity`'s `pid` argument to `pid_t`
-- Introduced wrapper types for gid_t, pid_t, and uid_t as Gid, Pid, and Uid
+- Introduced wrapper types for `gid_t`, `pid_t`, and `uid_t` as `Gid`, `Pid`, and `Uid`
   respectively. Various functions have been changed to use these new types as
   arguments. ([#629](https://github.com/nix-rust/nix/pull/629))
-- Promoted all Android targets to Tier 2 support
+- Fixed compilation on all Android and iOS targets ([#527](https://github.com/nix-rust/nix/pull/527))
+  and promoted them to Tier 2 support.
 - `nix::sys::statfs::{statfs,fstatfs}` uses statfs definition from `libc::statfs` instead of own linux specific type `nix::sys::Statfs`.
   Also file system type constants like `nix::sys::statfs::ADFS_SUPER_MAGIC` were removed in favor of the libc equivalent.
   ([#561](https://github.com/nix-rust/nix/pull/561))
+- Revised the termios API including additional tests and documentation and exposed it on iOS. ([#527](https://github.com/nix-rust/nix/pull/527))
 
 ### Removed
-- Removed io::Error from nix::Error and conversion from nix::Error to Errno
+- Removed `io::Error` from `nix::Error` and the conversion from `nix::Error` to `Errno`
   ([#614](https://github.com/nix-rust/nix/pull/614))
 
 ### Fixed
@@ -62,6 +64,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
   only available on x86, x86-64, and ARM, and also not on Android.
 - Fixed `sys::socket::sendmsg` with zero entry `cmsgs` parameter.
   ([#623](https://github.com/nix-rust/nix/pull/623))
+- Multiple constants related to the termios API have now been properly defined for
+  all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527))
 
 ## [0.8.1] 2017-04-16
 
@@ -75,7 +79,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Added `::nix::sys::termios::BaudRate` enum to provide portable baudrate
   values. ([#518](https://github.com/nix-rust/nix/pull/518))
 - Added a new `WaitStatus::PtraceEvent` to support ptrace events on Linux
-  and Android ([([#438](https://github.com/nix-rust/nix/pull/438))
+  and Android ([#438](https://github.com/nix-rust/nix/pull/438))
 - Added support for POSIX AIO
   ([#483](https://github.com/nix-rust/nix/pull/483))
   ([#506](https://github.com/nix-rust/nix/pull/506))
@@ -111,7 +115,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Added `ppoll` in `::nix::poll`
   ([#520](https://github.com/nix-rust/nix/pull/520))
 - Added support for getting and setting pipe size with fcntl(2) on Linux
-  ([#540](https://github.com/nix-rust/nix/pull/540)
+  ([#540](https://github.com/nix-rust/nix/pull/540))
 
 ### Changed
 - `::nix::sys::termios::{cfgetispeed, cfsetispeed, cfgetospeed, cfsetospeed}`
diff --git a/README.md b/README.md
index 8cda561a1a..4ccc47857e 100644
--- a/README.md
+++ b/README.md
@@ -50,41 +50,41 @@ The following targets are all supported by nix on Rust 1.13.0 or newer (unless
 otherwise noted):
 
 Tier 1:
-  * i686-unknown-linux-gnu
-  * x86_64-unknown-linux-gnu
-  * i686-apple-darwin
-  * x86_64-apple-darwin
   * aarch64-unknown-linux-gnu
-  * armv7-unknown-linux-gnueabihf
   * arm-unknown-linux-gnueabi
-  * x86_64-unknown-freebsd
+  * armv7-unknown-linux-gnueabihf
+  * i686-apple-darwin
+  * i686-unknown-linux-gnu
+  * i686-unknown-linux-musl
+  * mips-unknown-linux-gnu
+  * mipsel-unknown-linux-gnu
   * powerpc-unknown-linux-gnu
   * powerpc64-unknown-linux-gnu
   * powerpc64le-unknown-linux-gnu
-  * mips-unknown-linux-gnu
-  * mipsel-unknown-linux-gnu
-  * i686-unknown-linux-musl
+  * x86_64-apple-darwin
+  * x86_64-unknown-freebsd
+  * x86_64-unknown-linux-gnu
   * x86_64-unknown-linux-musl
 
 Tier 2:
-  * i686-unknown-freebsd
-  * x86_64-unknown-netbsd
+  * aarch64-apple-ios
   * aarch64-linux-android
   * arm-linux-androideabi
+  * armv7-apple-ios
   * armv7-linux-androideabi
+  * armv7s-apple-ios
+  * i386-apple-ios
   * i686-linux-android (requires Rust >= 1.18)
+  * i686-unknown-freebsd
+  * x86_64-apple-ios
   * x86_64-linux-android (requires Rust >= 1.18)
+  * x86_64-unknown-netbsd
 
 Tier 3:
-  * aarch64-apple-ios
   * arm-unknown-linux-musleabi (requires Rust >= 1.14)
-  * armv7-apple-ios
-  * armv7s-apple-ios
-  * i386-apple-ios
   * mips64-unknown-linux-gnuabi64
   * mips64el-unknown-linux-gnuabi64
   * s390x-unknown-linux-gnu
-  * x86_64-apple-ios
 
 ## Usage
 
diff --git a/src/macros.rs b/src/macros.rs
index b8707d0b7e..d8284f71ec 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -239,6 +239,184 @@ macro_rules! libc_bitflags {
     };
 }
 
+/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using
+/// values from the `libc` crate. This macro supports both `pub` and private `enum`s.
+///
+/// The `libc` crate must be in scope with the name `libc`.
+///
+/// # Example
+/// ```
+/// libc_enum!{
+///     pub enum ProtFlags {
+///         PROT_NONE,
+///         PROT_READ,
+///         PROT_WRITE,
+///         PROT_EXEC,
+///         #[cfg(any(target_os = "linux", target_os = "android"))]
+///         PROT_GROWSDOWN,
+///         #[cfg(any(target_os = "linux", target_os = "android"))]
+///         PROT_GROWSUP,
+///     }
+/// }
+/// ```
+macro_rules! libc_enum {
+    // (non-pub) Exit rule.
+    (@make_enum
+        {
+            name: $BitFlags:ident,
+            attrs: [$($attrs:tt)*],
+            entries: [$($entries:tt)*],
+        }
+    ) => {
+        $($attrs)*
+        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+        enum $BitFlags {
+            $($entries)*
+        }
+    };
+
+    // (pub) Exit rule.
+    (@make_enum
+        {
+            pub,
+            name: $BitFlags:ident,
+            attrs: [$($attrs:tt)*],
+            entries: [$($entries:tt)*],
+        }
+    ) => {
+        $($attrs)*
+        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+        pub enum $BitFlags {
+            $($entries)*
+        }
+    };
+
+    // (non-pub) Done accumulating.
+    (@accumulate_entries
+        {
+            name: $BitFlags:ident,
+            attrs: $attrs:tt,
+        },
+        $entries:tt;
+    ) => {
+        libc_enum! {
+            @make_enum
+            {
+                name: $BitFlags,
+                attrs: $attrs,
+                entries: $entries,
+            }
+        }
+    };
+
+    // (pub) Done accumulating.
+    (@accumulate_entries
+        {
+            pub,
+            name: $BitFlags:ident,
+            attrs: $attrs:tt,
+        },
+        $entries:tt;
+    ) => {
+        libc_enum! {
+            @make_enum
+            {
+                pub,
+                name: $BitFlags,
+                attrs: $attrs,
+                entries: $entries,
+            }
+        }
+    };
+
+    // Munch an attr.
+    (@accumulate_entries
+        $prefix:tt,
+        [$($entries:tt)*];
+        #[$attr:meta] $($tail:tt)*
+    ) => {
+        libc_enum! {
+            @accumulate_entries
+            $prefix,
+            [
+                $($entries)*
+                #[$attr]
+            ];
+            $($tail)*
+        }
+    };
+
+    // Munch last ident if not followed by a comma.
+    (@accumulate_entries
+        $prefix:tt,
+        [$($entries:tt)*];
+        $entry:ident
+    ) => {
+        libc_enum! {
+            @accumulate_entries
+            $prefix,
+            [
+                $($entries)*
+                $entry = libc::$entry,
+            ];
+        }
+    };
+
+    // Munch an ident; covers terminating comma case.
+    (@accumulate_entries
+        $prefix:tt,
+        [$($entries:tt)*];
+        $entry:ident, $($tail:tt)*
+    ) => {
+        libc_enum! {
+            @accumulate_entries
+            $prefix,
+            [
+                $($entries)*
+                $entry = libc::$entry,
+            ];
+            $($tail)*
+        }
+    };
+
+    // (non-pub) Entry rule.
+    (
+        $(#[$attr:meta])*
+        enum $BitFlags:ident {
+            $($vals:tt)*
+        }
+    ) => {
+        libc_enum! {
+            @accumulate_entries
+            {
+                name: $BitFlags,
+                attrs: [$(#[$attr])*],
+            },
+            [];
+            $($vals)*
+        }
+    };
+
+    // (pub) Entry rule.
+    (
+        $(#[$attr:meta])*
+        pub enum $BitFlags:ident {
+            $($vals:tt)*
+        }
+    ) => {
+        libc_enum! {
+            @accumulate_entries
+            {
+                pub,
+                name: $BitFlags,
+                attrs: [$(#[$attr])*],
+            },
+            [];
+            $($vals)*
+        }
+    };
+}
+
 /// A Rust version of the familiar C `offset_of` macro.  It returns the byte
 /// offset of `field` within struct `ty`
 macro_rules! offset_of {
diff --git a/src/pty.rs b/src/pty.rs
index c450e0588b..88c9cc14e2 100644
--- a/src/pty.rs
+++ b/src/pty.rs
@@ -188,23 +188,57 @@ pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
 pub fn openpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(winsize: T, termios: U) -> Result<OpenptyResult> {
     use std::ptr;
 
-    let mut slave: libc::c_int = -1;
-    let mut master: libc::c_int = -1;
-    let c_termios = match termios.into() {
-        Some(termios) => termios as *const Termios,
-        None => ptr::null() as *const Termios,
-    };
-    let c_winsize = match winsize.into() {
-        Some(ws) => ws as *const Winsize,
-        None => ptr::null() as *const Winsize,
-    };
-    let ret = unsafe {
-        libc::openpty(
-            &mut master as *mut libc::c_int,
-            &mut slave as *mut libc::c_int,
-            ptr::null_mut(),
-            c_termios as *mut libc::termios,
-            c_winsize as *mut Winsize)
+    let mut slave: libc::c_int = unsafe { mem::uninitialized() };
+    let mut master: libc::c_int = unsafe { mem::uninitialized() };
+    let ret = {
+        match (termios.into(), winsize.into()) {
+            (Some(termios), Some(winsize)) => {
+                let inner_termios = termios.get_libc_termios();
+                unsafe {
+                    libc::openpty(
+                        &mut master,
+                        &mut slave,
+                        ptr::null_mut(),
+                        &*inner_termios as *const libc::termios as *mut _,
+                        winsize as *const Winsize as *mut _,
+                    )
+                }
+            }
+            (None, Some(winsize)) => {
+                unsafe {
+                    libc::openpty(
+                        &mut master,
+                        &mut slave,
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        winsize as *const Winsize as *mut _,
+                    )
+                }
+            }
+            (Some(termios), None) => {
+                let inner_termios = termios.get_libc_termios();
+                unsafe {
+                    libc::openpty(
+                        &mut master,
+                        &mut slave,
+                        ptr::null_mut(),
+                        &*inner_termios as *const libc::termios as *mut _,
+                        ptr::null_mut(),
+                    )
+                }
+            }
+            (None, None) => {
+                unsafe {
+                    libc::openpty(
+                        &mut master,
+                        &mut slave,
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                        ptr::null_mut(),
+                    )
+                }
+            }
+        }
     };
 
     Errno::result(ret)?;
diff --git a/src/sys/mod.rs b/src/sys/mod.rs
index 783b83642b..627f769718 100644
--- a/src/sys/mod.rs
+++ b/src/sys/mod.rs
@@ -38,7 +38,6 @@ pub mod syscall;
 #[cfg(any(target_os = "linux"))]
 pub mod reboot;
 
-#[cfg(not(target_os = "ios"))]
 pub mod termios;
 
 #[cfg(any(target_os = "linux", target_os = "android"))]
diff --git a/src/sys/termios.rs b/src/sys/termios.rs
index 0f8bd6c9e2..8651a84da6 100644
--- a/src/sys/termios.rs
+++ b/src/sys/termios.rs
@@ -1,647 +1,895 @@
+//! An interface for controlling asynchronous communication ports
+//!
+//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
+//! underlying types are all implemented in libc for most platforms and either wrapped in safer
+//! types here or exported directly.
+//!
+//! If you are unfamiliar with the `termios` API, you should first read the
+//! [API documentation](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
+//! then come back to understand how `nix` safely wraps it.
+//!
+//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
+//! As this interface is not used with high-bandwidth information, this should be fine in most
+//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
+//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
+//! This means that when crossing the FFI interface to the underlying C library, data is first
+//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
+//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
+//! relatively small across all platforms (on the order of 32-64 bytes).
+//!
+//! The following examples highlight some of the API use cases such that users coming from using C
+//! or reading the standard documentation will understand how to use the safe API exposed here.
+//!
+//! Example disabling processing of the end-of-file control character:
+//!
+//! ```
+//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
+//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
+//! # let mut termios = unsafe { Termios::default_uninit() };
+//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
+//! ```
+//!
+//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
+//! an interface for working with bitfields that is similar to working with the raw unsigned
+//! integer types but offers type safety because of the internal checking that values will always
+//! be a valid combination of the defined flags.
+//!
+//! An example showing some of the basic operations for interacting with the control flags:
+//!
+//! ```
+//! # use self::nix::sys::termios::{CS5, CSIZE, Termios};
+//! # let mut termios = unsafe { Termios::default_uninit() };
+//! termios.control_flags & CSIZE == CS5;
+//! termios.control_flags |= CS5;
+//! ```
+
 use {Errno, Result};
-use libc::c_int;
+use libc::{self, c_int, tcflag_t};
+use std::cell::{Ref, RefCell};
+use std::convert::From;
 use std::mem;
 use std::os::unix::io::RawFd;
 
-pub use self::ffi::consts::*;
-pub use self::ffi::consts::SetArg::*;
-pub use self::ffi::consts::FlushArg::*;
-pub use self::ffi::consts::FlowArg::*;
-
-mod ffi {
-    pub use self::consts::*;
-
-    #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "linux"))]
-    mod non_android {
-        use super::consts::*;
-        use libc::c_int;
-
-        // `Termios` contains bitflags which are not considered
-        // `foreign-function-safe` by the compiler.
-        #[allow(improper_ctypes)]
-        extern {
-            pub fn cfgetispeed(termios: *const Termios) -> speed_t;
-            pub fn cfgetospeed(termios: *const Termios) -> speed_t;
-            pub fn cfsetispeed(termios: *mut Termios, speed: speed_t) -> c_int;
-            pub fn cfsetospeed(termios: *mut Termios, speed: speed_t) -> c_int;
-            pub fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int;
-            pub fn tcsetattr(fd: c_int,
-                             optional_actions: c_int,
-                             termios: *const Termios) -> c_int;
-            pub fn tcdrain(fd: c_int) -> c_int;
-            pub fn tcflow(fd: c_int, action: c_int) -> c_int;
-            pub fn tcflush(fd: c_int, action: c_int) -> c_int;
-            pub fn tcsendbreak(fd: c_int, duration: c_int) -> c_int;
-        }
-    }
-
-    #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd", target_os = "linux"))]
-    pub use self::non_android::*;
-
-    // On Android before 5.0, Bionic directly inline these to ioctl() calls.
-    #[cfg(all(target_os = "android", not(target_arch = "mips")))]
-    mod android {
-        use libc;
-        use libc::c_int;
-        use super::consts::*;
-
-        const TCGETS: c_int = 0x5401;
-        const TCSBRK: c_int = 0x5409;
-        const TCXONC: c_int = 0x540a;
-        const TCFLSH: c_int = 0x540b;
-        const TCSBRKP: c_int = 0x5425;
+use ::unistd::Pid;
+
+/// Stores settings for the termios API
+///
+/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
+/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
+/// an open port using `tcgetattr()`.
+#[derive(Clone)]
+pub struct Termios {
+    inner: RefCell<libc::termios>,
+    /// Input mode flags (see `termios.c_iflag` documentation)
+    pub input_flags: InputFlags,
+    /// Output mode flags (see `termios.c_oflag` documentation)
+    pub output_flags: OutputFlags,
+    /// Control mode flags (see `termios.c_cflag` documentation)
+    pub control_flags: ControlFlags,
+    /// Local mode flags (see `termios.c_lflag` documentation)
+    pub local_flags: LocalFlags,
+    /// Control characters (see `termios.c_cc` documentation)
+    pub control_chars: [libc::cc_t; libc::NCCS],
+}
 
-        pub unsafe fn cfgetispeed(termios: *const Termios) -> speed_t {
-            ((*termios).c_cflag & CBAUD).bits() as speed_t
-        }
-        pub unsafe fn cfgetospeed(termios: *const Termios) -> speed_t {
-            ((*termios).c_cflag & CBAUD).bits() as speed_t
-        }
-        pub unsafe fn cfsetispeed(termios: *mut Termios, speed: speed_t) -> c_int {
-            (*termios).c_cflag.remove(CBAUD);
-            (*termios).c_cflag.insert(ControlFlags::from_bits_truncate(speed) & CBAUD);
-            0
-        }
-        pub unsafe fn cfsetospeed(termios: *mut Termios, speed: speed_t) -> c_int {
-            (*termios).c_cflag.remove(CBAUD);
-            (*termios).c_cflag.insert(ControlFlags::from_bits_truncate(speed) & CBAUD);
-            0
-        }
-        pub unsafe fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int {
-            libc::ioctl(fd, TCGETS, termios)
-        }
-        pub unsafe fn tcsetattr(fd: c_int,
-                                optional_actions: c_int,
-                                termios: *const Termios) -> c_int {
-            libc::ioctl(fd, optional_actions, termios)
-        }
-        pub unsafe fn tcdrain(fd: c_int) -> c_int {
-            libc::ioctl(fd, TCSBRK, 1)
-        }
-        pub unsafe fn tcflow(fd: c_int, action: c_int) -> c_int {
-            libc::ioctl(fd, TCXONC, action)
-        }
-        pub unsafe fn tcflush(fd: c_int, action: c_int) -> c_int {
-            libc::ioctl(fd, TCFLSH, action)
-        }
-        pub unsafe fn tcsendbreak(fd: c_int, duration: c_int) -> c_int {
-            libc::ioctl(fd, TCSBRKP, duration)
+impl Termios {
+    /// Exposes an immutable reference to the underlying `libc::termios` data structure.
+    ///
+    /// This can be used for interfacing with other FFI functions like:
+    ///
+    /// ```rust
+    /// # extern crate libc;
+    /// # extern crate nix;
+    /// # fn main() {
+    /// # use nix::sys::termios::Termios;
+    /// # let mut termios = unsafe { Termios::default_uninit() };
+    /// let inner_termios = termios.get_libc_termios();
+    /// unsafe { libc::cfgetispeed(&*inner_termios) };
+    /// # }
+    /// ```
+    ///
+    /// There is no public API exposed for functions that modify the underlying `libc::termios`
+    /// data because it requires additional work to maintain type safety.
+    // FIXME: Switch this over to use pub(crate)
+    #[doc(hidden)]
+    pub fn get_libc_termios(&self) -> Ref<libc::termios> {
+        {
+            let mut termios = self.inner.borrow_mut();
+            termios.c_iflag = self.input_flags.bits();
+            termios.c_oflag = self.output_flags.bits();
+            termios.c_cflag = self.control_flags.bits();
+            termios.c_lflag = self.local_flags.bits();
+            termios.c_cc = self.control_chars;
         }
+        self.inner.borrow()
     }
 
-    #[cfg(target_os = "android")]
-    pub use self::android::*;
-
-
-    #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd"))]
-    pub mod consts {
-
-        use libc;
-
-        use libc::c_int;
-
-        pub type tcflag_t = libc::tcflag_t;
-        pub type cc_t = libc::cc_t;
-        pub type speed_t = libc::speed_t;
-
-        #[repr(C)]
-        #[derive(Clone, Copy)]
-        pub struct Termios {
-            pub c_iflag: InputFlags,
-            pub c_oflag: OutputFlags,
-            pub c_cflag: ControlFlags,
-            pub c_lflag: LocalFlags,
-            pub c_cc: [cc_t; NCCS],
-            pub c_ispeed: speed_t,
-            pub c_ospeed: speed_t
-        }
-
-        #[derive(Clone, Copy, Debug, PartialEq)]
-        pub enum BaudRate {
-            B0,
-            B50,
-            B75,
-            B110,
-            B134,
-            B150,
-            B200,
-            B300,
-            B600,
-            B1200,
-            B1800,
-            B2400,
-            B4800,
-            B9600,
-            B19200,
-            B38400,
-            B7200,
-            B14400,
-            B28800,
-            B57600,
-            B76800,
-            B115200,
-            B230400,
-            #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
-            B460800,
-            #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
-            B921600,
-        }
-
-        impl From<speed_t> for BaudRate {
-            fn from(s: speed_t) -> BaudRate {
-
-                use libc::{
-                    B0, B50, B75, B110, B134, B150,
-                    B200, B300, B600, B1200, B1800, B2400,
-                    B4800, B9600, B19200, B38400,
-                    B7200, B14400, B28800, B57600,
-                    B76800, B115200, B230400};
-
-                #[cfg(target_os = "freebsd")]
-                use libc::{B460800, B921600};
-
-                match s {
-                    B0 => BaudRate::B0,
-                    B50 => BaudRate::B50,
-                    B75 => BaudRate::B75,
-                    B110 => BaudRate::B110,
-                    B134 => BaudRate::B134,
-                    B150 => BaudRate::B150,
-                    B200 => BaudRate::B200,
-                    B300 => BaudRate::B300,
-                    B600 => BaudRate::B600,
-                    B1200 => BaudRate::B1200,
-                    B1800 => BaudRate::B1800,
-                    B2400 => BaudRate::B2400,
-                    B4800 => BaudRate::B4800,
-                    B9600 => BaudRate::B9600,
-                    B19200 => BaudRate::B19200,
-                    B38400 => BaudRate::B38400,
-                    B7200 => BaudRate::B7200,
-                    B14400 => BaudRate::B14400,
-                    B28800 => BaudRate::B28800,
-                    B57600 => BaudRate::B57600,
-                    B76800 => BaudRate::B76800,
-                    B115200 => BaudRate::B115200,
-                    B230400 => BaudRate::B230400,
-                    #[cfg(target_os = "freebsd")]
-                    B460800 => BaudRate::B460800,
-                    #[cfg(target_os = "freebsd")]
-                    B921600 => BaudRate::B921600,
-                    b @ _ => unreachable!("Invalid baud constant: {}", b),
-                }
-            }
-        }
-
-        pub const VEOF: usize     = 0;
-        pub const VEOL: usize     = 1;
-        pub const VEOL2: usize    = 2;
-        pub const VERASE: usize   = 3;
-        pub const VWERASE: usize  = 4;
-        pub const VKILL: usize    = 5;
-        pub const VREPRINT: usize = 6;
-        pub const VINTR: usize    = 8;
-        pub const VQUIT: usize    = 9;
-        pub const VSUSP: usize    = 10;
-        pub const VDSUSP: usize   = 11;
-        pub const VSTART: usize   = 12;
-        pub const VSTOP: usize    = 13;
-        pub const VLNEXT: usize   = 14;
-        pub const VDISCARD: usize = 15;
-        pub const VMIN: usize     = 16;
-        pub const VTIME: usize    = 17;
-        pub const VSTATUS: usize  = 18;
-        pub const NCCS: usize     = 20;
-
-        bitflags! {
-            pub struct InputFlags: tcflag_t {
-                const IGNBRK  = 0x00000001;
-                const BRKINT  = 0x00000002;
-                const IGNPAR  = 0x00000004;
-                const PARMRK  = 0x00000008;
-                const INPCK   = 0x00000010;
-                const ISTRIP  = 0x00000020;
-                const INLCR   = 0x00000040;
-                const IGNCR   = 0x00000080;
-                const ICRNL   = 0x00000100;
-                const IXON    = 0x00000200;
-                const IXOFF   = 0x00000400;
-                const IXANY   = 0x00000800;
-                const IMAXBEL = 0x00002000;
-                #[cfg(not(target_os = "dragonfly"))]
-                const IUTF8   = 0x00004000;
-            }
-        }
-
-        bitflags! {
-            pub struct OutputFlags: tcflag_t {
-                const OPOST  = 0x00000001;
-                const ONLCR  = 0x00000002;
-                const OXTABS = 0x00000004;
-                const ONOEOT = 0x00000008;
-            }
-        }
-
-        bitflags! {
-            pub struct ControlFlags: tcflag_t {
-                const CIGNORE    = 0x00000001;
-                const CSIZE      = 0x00000300;
-                const CS5        = 0x00000000;
-                const CS6        = 0x00000100;
-                const CS7        = 0x00000200;
-                const CS8        = 0x00000300;
-                const CSTOPB     = 0x00000400;
-                const CREAD      = 0x00000800;
-                const PARENB     = 0x00001000;
-                const PARODD     = 0x00002000;
-                const HUPCL      = 0x00004000;
-                const CLOCAL     = 0x00008000;
-                const CCTS_OFLOW = 0x00010000;
-                const CRTSCTS    = 0x00030000;
-                const CRTS_IFLOW = 0x00020000;
-                const CDTR_IFLOW = 0x00040000;
-                const CDSR_OFLOW = 0x00080000;
-                const CCAR_OFLOW = 0x00100000;
-                const MDMBUF     = 0x00100000;
-            }
+    /// Exposes the inner `libc::termios` datastore within `Termios`.
+    ///
+    /// This is unsafe because if this is used to modify the inner libc::termios struct, it will not
+    /// automatically update the safe wrapper type around it. Therefore we disable docs to
+    /// effectively limit its use to nix internals. In this case it should also be paired with a
+    /// call to `update_wrapper()` so that the wrapper-type and internal representation stay
+    /// consistent.
+    unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
+        {
+            let mut termios = self.inner.borrow_mut();
+            termios.c_iflag = self.input_flags.bits();
+            termios.c_oflag = self.output_flags.bits();
+            termios.c_cflag = self.control_flags.bits();
+            termios.c_lflag = self.local_flags.bits();
+            termios.c_cc = self.control_chars;
         }
+        self.inner.as_ptr()
+    }
 
-        bitflags! {
-            pub struct LocalFlags: tcflag_t {
-                const ECHOKE     = 0x00000001;
-                const ECHOE      = 0x00000002;
-                const ECHOK      = 0x00000004;
-                const ECHO       = 0x00000008;
-                const ECHONL     = 0x00000010;
-                const ECHOPRT    = 0x00000020;
-                const ECHOCTL    = 0x00000040;
-                const ISIG       = 0x00000080;
-                const ICANON     = 0x00000100;
-                const ALTWERASE  = 0x00000200;
-                const IEXTEN     = 0x00000400;
-                const EXTPROC    = 0x00000800;
-                const TOSTOP     = 0x00400000;
-                const FLUSHO     = 0x00800000;
-                const NOKERNINFO = 0x02000000;
-                const PENDIN     = 0x20000000;
-                const NOFLSH     = 0x80000000;
-            }
+    /// Allows for easily creating new Termios structs that will be overwritten with real data.
+    ///
+    /// This should only be used when the inner libc::termios struct will be overwritten before it's
+    /// read.
+    // FIXME: Switch this over to use pub(crate)
+    #[doc(hidden)]
+    pub unsafe fn default_uninit() -> Self {
+        Termios {
+            inner: RefCell::new(mem::uninitialized()),
+            input_flags: InputFlags::empty(),
+            output_flags: OutputFlags::empty(),
+            control_flags: ControlFlags::empty(),
+            local_flags: LocalFlags::empty(),
+            control_chars: [0 as libc::cc_t; NCCS],
         }
+    }
 
-        pub const NL0: c_int  = 0x00000000;
-        pub const NL1: c_int  = 0x00000100;
-        pub const NL2: c_int  = 0x00000200;
-        pub const NL3: c_int  = 0x00000300;
-        pub const TAB0: c_int = 0x00000000;
-        pub const TAB1: c_int = 0x00000400;
-        pub const TAB2: c_int = 0x00000800;
-        pub const TAB3: c_int = 0x00000004;
-        pub const CR0: c_int  = 0x00000000;
-        pub const CR1: c_int  = 0x00001000;
-        pub const CR2: c_int  = 0x00002000;
-        pub const CR3: c_int  = 0x00003000;
-        pub const FF0: c_int  = 0x00000000;
-        pub const FF1: c_int  = 0x00004000;
-        pub const BS0: c_int  = 0x00000000;
-        pub const BS1: c_int  = 0x00008000;
-        pub const VT0: c_int  = 0x00000000;
-        pub const VT1: c_int  = 0x00010000;
-
-        // XXX: We're using `repr(C)` because `c_int` doesn't work here.
-        // See https://github.com/rust-lang/rust/issues/10374.
-        #[derive(Clone, Copy)]
-        #[repr(C)]
-        pub enum SetArg {
-            TCSANOW   = 0,
-            TCSADRAIN = 1,
-            TCSAFLUSH = 2,
-            TCSASOFT  = 16,
-        }
+    /// Updates the wrapper values from the internal `libc::termios` data structure.
+    #[doc(hidden)]
+    pub fn update_wrapper(&mut self) {
+        let termios = *self.inner.borrow_mut();
+        self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
+        self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
+        self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
+        self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
+        self.control_chars = termios.c_cc;
+    }
+}
 
-        // XXX: We're using `repr(C)` because `c_int` doesn't work here.
-        // See https://github.com/rust-lang/rust/issues/10374.
-        #[derive(Clone, Copy)]
-        #[repr(C)]
-        pub enum FlushArg {
-            TCIFLUSH  = 1,
-            TCOFLUSH  = 2,
-            TCIOFLUSH = 3,
+impl From<libc::termios> for Termios {
+    fn from(termios: libc::termios) -> Self {
+        Termios {
+            inner: RefCell::new(termios),
+            input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
+            output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
+            control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
+            local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
+            control_chars: termios.c_cc,
         }
+    }
+}
 
-        // XXX: We're using `repr(C)` because `c_int` doesn't work here.
-        // See https://github.com/rust-lang/rust/issues/10374.
-        #[derive(Clone, Copy)]
-        #[repr(C)]
-        pub enum FlowArg {
-            TCOOFF = 1,
-            TCOON  = 2,
-            TCIOFF = 3,
-            TCION  = 4,
-        }
+impl From<Termios> for libc::termios {
+    fn from(termios: Termios) -> Self {
+        termios.inner.into_inner()
     }
+}
 
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    pub mod consts {
-        use libc::{c_int, c_uint, c_uchar};
-
-        pub type tcflag_t = c_uint;
-        pub type cc_t = c_uchar;
-        pub type speed_t = c_uint;
-
-        #[repr(C)]
-        #[derive(Clone, Copy)]
-        pub struct Termios {
-            pub c_iflag: InputFlags,
-            pub c_oflag: OutputFlags,
-            pub c_cflag: ControlFlags,
-            pub c_lflag: LocalFlags,
-            pub c_line: cc_t,
-            pub c_cc: [cc_t; NCCS],
-            pub c_ispeed: speed_t,
-            pub c_ospeed: speed_t
-        }
+libc_enum!{
+    /// Baud rates supported by the system
+    ///
+    /// B0 is special and will disable the port.
+    #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
+    #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64")), repr(u32))]
+    pub enum BaudRate {
+        B0,
+        B50,
+        B75,
+        B110,
+        B134,
+        B150,
+        B200,
+        B300,
+        B600,
+        B1200,
+        B1800,
+        B2400,
+        B4800,
+        #[cfg(any(target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"))]
+        B7200,
+        B9600,
+        #[cfg(any(target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"))]
+        B14400,
+        B19200,
+        #[cfg(any(target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"))]
+        B28800,
+        B38400,
+        B57600,
+        #[cfg(any(target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"))]
+        B76800,
+        B115200,
+        B230400,
+        #[cfg(any(target_os = "android",
+                target_os = "freebsd",
+                target_os = "linux",
+                taget_os = "netbsd"))]
+        B460800,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B500000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B576000,
+        #[cfg(any(target_os = "android",
+                target_os = "freebsd",
+                target_os = "linux",
+                taget_os = "netbsd"))]
+        B921600,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B1000000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B1152000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B1500000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B2000000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B2500000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B3000000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B3500000,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        B4000000,
+    }
+}
 
-        #[derive(Clone, Copy, Debug, PartialEq)]
-        pub enum BaudRate {
-            B0,
-            B50,
-            B75,
-            B110,
-            B134,
-            B150,
-            B200,
-            B300,
-            B600,
-            B1200,
-            B1800,
-            B2400,
-            B4800,
-            B9600,
-            B19200,
-            B38400,
-            B57600,
-            B115200,
-            B230400,
-            B460800,
-            B500000,
-            B576000,
-            B921600,
-            B1000000,
-            B1152000,
-            B1500000,
-            B2000000,
-            B2500000,
-            B3000000,
-            B3500000,
-            B4000000,
+impl From<libc::speed_t> for BaudRate {
+    fn from(s: libc::speed_t) -> BaudRate {
+
+        use libc::{B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800,
+                   B9600, B19200, B38400, B57600, B115200, B230400};
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000,
+                   B3500000, B4000000};
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        use libc::{B7200, B14400, B28800, B76800};
+        #[cfg(any(target_os = "android",
+                  target_os = "freebsd",
+                  target_os = "linux",
+                  target_os = "netbsd"))]
+        use libc::{B460800, B921600};
+
+        match s {
+            B0 => BaudRate::B0,
+            B50 => BaudRate::B50,
+            B75 => BaudRate::B75,
+            B110 => BaudRate::B110,
+            B134 => BaudRate::B134,
+            B150 => BaudRate::B150,
+            B200 => BaudRate::B200,
+            B300 => BaudRate::B300,
+            B600 => BaudRate::B600,
+            B1200 => BaudRate::B1200,
+            B1800 => BaudRate::B1800,
+            B2400 => BaudRate::B2400,
+            B4800 => BaudRate::B4800,
+            #[cfg(any(target_os = "dragonfly",
+                      target_os = "freebsd",
+                      target_os = "macos",
+                      target_os = "netbsd",
+                      target_os = "openbsd"))]
+            B7200 => BaudRate::B7200,
+            B9600 => BaudRate::B9600,
+            #[cfg(any(target_os = "dragonfly",
+                      target_os = "freebsd",
+                      target_os = "macos",
+                      target_os = "netbsd",
+                      target_os = "openbsd"))]
+            B14400 => BaudRate::B14400,
+            B19200 => BaudRate::B19200,
+            #[cfg(any(target_os = "dragonfly",
+                      target_os = "freebsd",
+                      target_os = "macos",
+                      target_os = "netbsd",
+                      target_os = "openbsd"))]
+            B28800 => BaudRate::B28800,
+            B38400 => BaudRate::B38400,
+            B57600 => BaudRate::B57600,
+            #[cfg(any(target_os = "dragonfly",
+                      target_os = "freebsd",
+                      target_os = "macos",
+                      target_os = "netbsd",
+                      target_os = "openbsd"))]
+            B76800 => BaudRate::B76800,
+            B115200 => BaudRate::B115200,
+            B230400 => BaudRate::B230400,
+            #[cfg(any(target_os = "android",
+                      target_os = "freebsd",
+                      target_os = "linux",
+                      taget_os = "netbsd"))]
+            B460800 => BaudRate::B460800,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B500000 => BaudRate::B500000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B576000 => BaudRate::B576000,
+            #[cfg(any(target_os = "android",
+                      target_os = "freebsd",
+                      target_os = "linux",
+                      taget_os = "netbsd"))]
+            B921600 => BaudRate::B921600,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B1000000 => BaudRate::B1000000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B1152000 => BaudRate::B1152000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B1500000 => BaudRate::B1500000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B2000000 => BaudRate::B2000000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B2500000 => BaudRate::B2500000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B3000000 => BaudRate::B3000000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B3500000 => BaudRate::B3500000,
+            #[cfg(any(target_os = "android", target_os = "linux"))]
+            B4000000 => BaudRate::B4000000,
+            b @ _ => unreachable!("Invalid baud constant: {}", b),
         }
+    }
+}
 
-        impl From<speed_t> for BaudRate {
-            fn from(s: speed_t) -> BaudRate {
-
-                use libc::{
-                    B0, B50, B75, B110, B134, B150,
-                    B200, B300, B600, B1200, B1800, B2400,
-                    B4800, B9600, B19200, B38400, B57600,
-                    B115200, B230400, B460800, B500000,
-                    B576000, B921600, B1000000, B1152000,
-                    B1500000, B2000000, B2500000, B3000000,
-                    B3500000, B4000000};
-
-                match s {
-                    B0 => BaudRate::B0,
-                    B50 => BaudRate::B50,
-                    B75 => BaudRate::B75,
-                    B110 => BaudRate::B110,
-                    B134 => BaudRate::B134,
-                    B150 => BaudRate::B150,
-                    B200 => BaudRate::B200,
-                    B300 => BaudRate::B300,
-                    B600 => BaudRate::B600,
-                    B1200 => BaudRate::B1200,
-                    B1800 => BaudRate::B1800,
-                    B2400 => BaudRate::B2400,
-                    B4800 => BaudRate::B4800,
-                    B9600 => BaudRate::B9600,
-                    B19200 => BaudRate::B19200,
-                    B38400 => BaudRate::B38400,
-                    B57600 => BaudRate::B57600,
-                    B115200 => BaudRate::B115200,
-                    B230400 => BaudRate::B230400,
-                    B460800 => BaudRate::B460800,
-                    B500000 => BaudRate::B500000,
-                    B576000 => BaudRate::B576000,
-                    B921600 => BaudRate::B921600,
-                    B1000000 => BaudRate::B1000000,
-                    B1152000 => BaudRate::B1152000,
-                    B1500000 => BaudRate::B1500000,
-                    B2000000 => BaudRate::B2000000,
-                    B2500000 => BaudRate::B2500000,
-                    B3000000 => BaudRate::B3000000,
-                    B3500000 => BaudRate::B3500000,
-                    B4000000 => BaudRate::B4000000,
-                    b @ _ => unreachable!("Invalid baud constant: {}", b),
-                }
-            }
-        }
+// TODO: Add TCSASOFT, which will require treating this as a bitfield.
+libc_enum! {
+    /// Specify when a port configuration change should occur.
+    ///
+    /// Used as an argument to `tcsetattr()`
+    #[repr(i32)]
+    pub enum SetArg {
+        /// The change will occur immediately
+        TCSANOW,
+        /// The change occurs after all output has been written
+        TCSADRAIN,
+        /// Same as `TCSADRAIN`, but will also flush the input buffer
+        TCSAFLUSH,
+    }
+}
 
-        pub const VEOF: usize     = 4;
-        pub const VEOL: usize     = 11;
-        pub const VEOL2: usize    = 16;
-        pub const VERASE: usize   = 2;
-        pub const VWERASE: usize  = 14;
-        pub const VKILL: usize    = 3;
-        pub const VREPRINT: usize = 12;
-        pub const VINTR: usize    = 0;
-        pub const VQUIT: usize    = 1;
-        pub const VSUSP: usize    = 10;
-        pub const VSTART: usize   = 8;
-        pub const VSTOP: usize    = 9;
-        pub const VLNEXT: usize   = 15;
-        pub const VDISCARD: usize = 13;
-        pub const VMIN: usize     = 6;
-        pub const VTIME: usize    = 5;
-        pub const NCCS: usize     = 32;
-
-        bitflags! {
-            pub struct InputFlags: tcflag_t {
-                const IGNBRK  = 0x00000001;
-                const BRKINT  = 0x00000002;
-                const IGNPAR  = 0x00000004;
-                const PARMRK  = 0x00000008;
-                const INPCK   = 0x00000010;
-                const ISTRIP  = 0x00000020;
-                const INLCR   = 0x00000040;
-                const IGNCR   = 0x00000080;
-                const ICRNL   = 0x00000100;
-                const IXON    = 0x00000400;
-                const IXOFF   = 0x00001000;
-                const IXANY   = 0x00000800;
-                const IMAXBEL = 0x00002000;
-                const IUTF8   = 0x00004000;
-            }
-        }
+libc_enum! {
+    /// Specify a combination of the input and output buffers to flush
+    ///
+    /// Used as an argument to `tcflush()`.
+    #[repr(i32)]
+    pub enum FlushArg {
+        /// Flush data that was received but not read
+        TCIFLUSH,
+        /// Flush data written but not transmitted
+        TCOFLUSH,
+        /// Flush both received data not read and written data not transmitted
+        TCIOFLUSH,
+    }
+}
 
-        bitflags! {
-            pub struct OutputFlags: tcflag_t {
-                const OPOST  = 0x00000001;
-                const ONLCR  = 0x00000004;
-            }
-        }
+libc_enum! {
+    /// Specify how transmission flow should be altered
+    ///
+    /// Used as an argument to `tcflow()`.
+    #[repr(i32)]
+    pub enum FlowArg {
+        /// Suspend transmission
+        TCOOFF,
+        /// Resume transmission
+        TCOON,
+        /// Transmit a STOP character, which should disable a connected terminal device
+        TCIOFF,
+        /// Transmit a START character, which should re-enable a connected terminal device
+        TCION,
+    }
+}
 
-        bitflags! {
-            pub struct ControlFlags: tcflag_t {
-                const CSIZE      = 0x00000030;
-                const CS5        = 0x00000000;
-                const CS6        = 0x00000010;
-                const CS7        = 0x00000020;
-                const CS8        = 0x00000030;
-                const CSTOPB     = 0x00000040;
-                const CREAD      = 0x00000080;
-                const PARENB     = 0x00000100;
-                const PARODD     = 0x00000200;
-                const HUPCL      = 0x00000400;
-                const CLOCAL     = 0x00000800;
-                const CRTSCTS    = 0x80000000;
-                #[cfg(target_os = "android")]
-                const CBAUD      = 0o0010017;
-            }
-        }
+// TODO: Make this usable directly as a slice index.
+libc_enum! {
+    /// Indices into the `termios.c_cc` array for special characters.
+    #[repr(usize)]
+    pub enum SpecialCharacterIndices {
+        VDISCARD,
+        #[cfg(any(target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"))]
+        VDSUSP,
+        VEOF,
+        VEOL,
+        VEOL2,
+        VERASE,
+        #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+        VERASE2,
+        VINTR,
+        VKILL,
+        VLNEXT,
+        VMIN,
+        VQUIT,
+        VREPRINT,
+        VSTART,
+        #[cfg(any(target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "macos",
+                target_os = "netbsd",
+                target_os = "openbsd"))]
+        VSTATUS,
+        VSTOP,
+        VSUSP,
+        #[cfg(target_os = "linux")]
+        VSWTC,
+        #[cfg(target_os = "haiku")]
+        VSWTCH,
+        VTIME,
+        VWERASE,
+        #[cfg(target_os = "dragonfly")]
+        VCHECKPT,
+    }
+}
 
-        bitflags! {
-            pub struct LocalFlags: tcflag_t {
-                const ECHOKE     = 0x00000800;
-                const ECHOE      = 0x00000010;
-                const ECHOK      = 0x00000020;
-                const ECHO       = 0x00000008;
-                const ECHONL     = 0x00000040;
-                const ECHOPRT    = 0x00000400;
-                const ECHOCTL    = 0x00000200;
-                const ISIG       = 0x00000001;
-                const ICANON     = 0x00000002;
-                const IEXTEN     = 0x00008000;
-                const EXTPROC    = 0x00010000;
-                const TOSTOP     = 0x00000100;
-                const FLUSHO     = 0x00001000;
-                const PENDIN     = 0x00004000;
-                const NOFLSH     = 0x00000080;
-            }
-        }
+pub use libc::NCCS;
+#[cfg(any(target_os = "dragonfly",
+          target_os = "freebsd",
+          target_os = "linux",
+          target_os = "macos",
+          target_os = "netbsd",
+          target_os = "openbsd"))]
+pub use libc::_POSIX_VDISABLE;
+
+libc_bitflags! {
+    /// Flags for configuring the input mode of a terminal
+    pub flags InputFlags: tcflag_t {
+        IGNBRK,
+        BRKINT,
+        IGNPAR,
+        PARMRK,
+        INPCK,
+        ISTRIP,
+        INLCR,
+        IGNCR,
+        ICRNL,
+        IXON,
+        IXOFF,
+        IXANY,
+        IMAXBEL,
+        #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+        IUTF8,
+    }
+}
 
-        pub const NL0: c_int  = 0x00000000;
-        pub const NL1: c_int  = 0x00000100;
-        pub const TAB0: c_int = 0x00000000;
-        pub const TAB1: c_int = 0x00000800;
-        pub const TAB2: c_int = 0x00001000;
-        pub const TAB3: c_int = 0x00001800;
-        pub const CR0: c_int  = 0x00000000;
-        pub const CR1: c_int  = 0x00000200;
-        pub const CR2: c_int  = 0x00000400;
-        pub const CR3: c_int  = 0x00000600;
-        pub const FF0: c_int  = 0x00000000;
-        pub const FF1: c_int  = 0x00008000;
-        pub const BS0: c_int  = 0x00000000;
-        pub const BS1: c_int  = 0x00002000;
-        pub const VT0: c_int  = 0x00000000;
-        pub const VT1: c_int  = 0x00004000;
-
-        // XXX: We're using `repr(C)` because `c_int` doesn't work here.
-        // See https://github.com/rust-lang/rust/issues/10374.
-        #[derive(Clone, Copy)]
-        #[repr(C)]
-        pub enum SetArg {
-            TCSANOW   = 0,
-            TCSADRAIN = 1,
-            TCSAFLUSH = 2,
-        }
+libc_bitflags! {
+    /// Flags for configuring the output mode of a terminal
+    pub flags OutputFlags: tcflag_t {
+        OPOST,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "linux",
+                  target_os = "openbsd"))]
+        OLCUC,
+        ONLCR,
+        OCRNL as tcflag_t,
+        ONOCR as tcflag_t,
+        ONLRET as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        OFILL as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        OFDEL as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        NL0 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        NL1 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        CR0 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        CR1 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        CR2 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        CR3 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "freebsd",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        TAB0 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        TAB1 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        TAB2 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "freebsd",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        TAB3 as tcflag_t,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        XTABS,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        BS0 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        BS1 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        VT0 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        VT1 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        FF0 as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        FF1 as tcflag_t,
+        #[cfg(any(target_os = "freebsd",
+                  target_os = "dragonfly",
+                  target_os = "ios",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        OXTABS,
+        #[cfg(any(target_os = "freebsd",
+                  target_os = "dragonfly",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        ONOEOT as tcflag_t,
+
+        // Bitmasks for use with OutputFlags to select specific settings
+        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+        // is resolved.
+
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        NLDLY as tcflag_t, // FIXME: Datatype needs to be corrected in libc for mac
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        CRDLY as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "freebsd",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        TABDLY as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        BSDLY as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        VTDLY as tcflag_t,
+        #[cfg(any(target_os = "android",
+                  target_os = "haiku",
+                  target_os = "ios",
+                  target_os = "linux",
+                  target_os = "macos"))]
+        FFDLY as tcflag_t,
+    }
+}
 
-        // XXX: We're using `repr(C)` because `c_int` doesn't work here.
-        // See https://github.com/rust-lang/rust/issues/10374.
-        #[derive(Clone, Copy)]
-        #[repr(C)]
-        pub enum FlushArg {
-            TCIFLUSH  = 0,
-            TCOFLUSH  = 1,
-            TCIOFLUSH = 2,
-        }
+libc_bitflags! {
+    /// Flags for setting the control mode of a terminal
+    pub flags ControlFlags: tcflag_t {
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd",
+                  target_os = "ios",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        CIGNORE,
+        CS5,
+        CS6,
+        CS7,
+        CS8,
+        CSTOPB,
+        CREAD,
+        PARENB,
+        PARODD,
+        HUPCL,
+        CLOCAL,
+        CRTSCTS,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        CBAUD,
+        #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
+        CMSPAR,
+        #[cfg(any(target_os = "android",
+                  all(target_os = "linux",
+                      not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
+        CIBAUD,
+        #[cfg(any(target_os = "android", target_os = "linux"))]
+        CBAUDEX,
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        MDMBUF,
+        #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+        CHWFLOW,
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        CCTS_OFLOW,
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        CRTS_IFLOW,
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd"))]
+        CDTR_IFLOW,
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd"))]
+        CDSR_OFLOW,
+        #[cfg(any(target_os = "dragonfly",
+                  target_os = "freebsd"))]
+        CCAR_OFLOW,
+
+        // Bitmasks for use with ControlFlags to select specific settings
+        // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+        // is resolved.
+
+        CSIZE,
+    }
+}
 
-        // XXX: We're using `repr(C)` because `c_int` doesn't work here.
-        // See https://github.com/rust-lang/rust/issues/10374.
-        #[derive(Clone, Copy)]
-        #[repr(C)]
-        pub enum FlowArg {
-            TCOOFF = 0,
-            TCOON  = 1,
-            TCIOFF = 2,
-            TCION  = 3,
-        }
+libc_bitflags! {
+    /// Flags for setting any local modes
+    pub flags LocalFlags: tcflag_t {
+        ECHOKE,
+        ECHOE,
+        ECHOK,
+        ECHO,
+        ECHONL,
+        ECHOPRT,
+        ECHOCTL,
+        ISIG,
+        ICANON,
+        #[cfg(any(target_os = "freebsd",
+                  target_os = "dragonfly",
+                  target_os = "ios",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        ALTWERASE,
+        IEXTEN,
+        EXTPROC,
+        TOSTOP,
+        FLUSHO,
+        #[cfg(any(target_os = "freebsd",
+                  target_os = "dragonfly",
+                  target_os = "ios",
+                  target_os = "macos",
+                  target_os = "netbsd",
+                  target_os = "openbsd"))]
+        NOKERNINFO,
+        PENDIN,
+        NOFLSH,
     }
 }
 
+/// 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 {
-    unsafe {
-        ffi::cfgetispeed(termios).into()
-    }
+    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()
+}
+
+/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
+/// [termios(3)](http://man7.org/linux/man-pages/man3/termios.3.html)).
+///
+/// `cfmakeraw()` configures the termios structure such that input is available character-by-
+/// character, echoing is disabled, and all special input and output processing is disabled. Note
+/// that this is a non-standard function, but is available on Linux and BSDs.
+pub fn cfmakeraw(termios: &mut Termios) {
+    let inner_termios = unsafe { termios.get_libc_termios_mut() };
     unsafe {
-        ffi::cfgetospeed(termios).into()
+        libc::cfmakeraw(inner_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<()> {
-    Errno::result(unsafe {
-        ffi::cfsetispeed(termios, baud as speed_t)
-    }).map(drop)
+    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<()> {
-    Errno::result(unsafe {
-        ffi::cfsetospeed(termios, baud as speed_t)
-    }).map(drop)
+    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)).
+///
+/// `tcgetattr()` returns a Termios structure with the current configuration for a port. Modifying
+/// this structure *will not* reconfigure the port, instead the modifications should be done to
+/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
 pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
-    let mut termios = unsafe { mem::uninitialized() };
+    let mut termios: libc::termios = unsafe { mem::uninitialized() };
 
-    let res = unsafe {
-        ffi::tcgetattr(fd, &mut termios)
-    };
+    let res = unsafe { libc::tcgetattr(fd, &mut termios) };
 
     try!(Errno::result(res));
 
-    Ok(termios)
+    Ok(termios.into())
 }
 
-pub fn tcsetattr(fd: RawFd,
-                 actions: SetArg,
-                 termios: &Termios) -> Result<()> {
-    Errno::result(unsafe {
-        ffi::tcsetattr(fd, actions as c_int, termios)
-    }).map(drop)
+/// Set the configuration for a terminal (see
+/// [tcsetattr(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
+///
+/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
+/// takes affect at a time specified by `actions`. Note that this function may return success if
+/// *any* of the parameters were successfully set, not only if all were set successfully.
+pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> {
+    let inner_termios = termios.get_libc_termios();
+    Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop)
 }
 
+/// Block until all output data is written (see
+/// [tcdrain(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
 pub fn tcdrain(fd: RawFd) -> Result<()> {
-    Errno::result(unsafe {
-        ffi::tcdrain(fd)
-    }).map(drop)
+    Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
 }
 
+/// Suspend or resume the transmission or reception of data (see
+/// [tcflow(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
+///
+/// `tcflow()` suspends of resumes the transmission or reception of data for the given port
+/// depending on the value of `action`.
 pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> {
-    Errno::result(unsafe {
-        ffi::tcflow(fd, action as c_int)
-    }).map(drop)
+    Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop)
 }
 
+/// Discard data in the output or input queue (see
+/// [tcflush(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
+///
+/// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both
+/// depending on the value of `action`.
 pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> {
-    Errno::result(unsafe {
-        ffi::tcflush(fd, action as c_int)
-    }).map(drop)
+    Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop)
+}
+
+/// Send a break for a specific duration (see
+/// [tcsendbreak(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
+///
+/// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream
+/// of zero-valued bits for an implementation-defined duration.
+pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> {
+    Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop)
 }
 
-pub fn tcsendbreak(fd: RawFd, action: c_int) -> Result<()> {
-    Errno::result(unsafe {
-        ffi::tcsendbreak(fd, action)
-    }).map(drop)
+/// Get the session controlled by the given terminal (see
+/// [tcgetsid(3)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
+pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
+    let res = unsafe { libc::tcgetsid(fd) };
+
+    Errno::result(res).map(Pid::from_raw)
 }
diff --git a/test/sys/test_termios.rs b/test/sys/test_termios.rs
index a41304d73f..308cf62c2b 100644
--- a/test/sys/test_termios.rs
+++ b/test/sys/test_termios.rs
@@ -1,6 +1,18 @@
+use std::os::unix::prelude::*;
+
+use nix::{Error, fcntl, unistd};
 use nix::errno::Errno;
-use nix::sys::termios;
-use nix::{Error, unistd};
+use nix::pty::openpty;
+use nix::sys::termios::{self, ECHO, OPOST, OCRNL, Termios, tcgetattr};
+use nix::unistd::{read, write, close};
+
+/// Helper function analogous to std::io::Write::write_all, but for `RawFd`s
+fn write_all(f: RawFd, buf: &[u8]) {
+    let mut len = 0;
+    while len < buf.len() {
+        len += write(f, &buf[len..]).unwrap();
+    }
+}
 
 #[test]
 fn test_tcgetattr() {
@@ -19,3 +31,82 @@ fn test_tcgetattr() {
         }
     }
 }
+
+// Test modifying output flags
+#[test]
+fn test_output_flags() {
+    // Open one pty to get attributes for the second one
+    let mut termios = {
+        let pty = openpty(None, None).unwrap();
+        assert!(pty.master > 0);
+        assert!(pty.slave > 0);
+        let termios = tcgetattr(pty.master).unwrap();
+        termios
+    };
+
+    // Make sure postprocessing '\r' isn't specified by default or this test is useless.
+    assert!(!termios.output_flags.contains(OPOST | OCRNL));
+
+    // Specify that '\r' characters should be transformed to '\n'
+    // OPOST is specified to enable post-processing
+    termios.output_flags.insert(OPOST | OCRNL);
+
+    // Open a pty
+    let pty = openpty(None, &termios).unwrap();
+    assert!(pty.master > 0);
+    assert!(pty.slave > 0);
+
+    // Write into the master
+    let string = "foofoofoo\r";
+    write_all(pty.master, string.as_bytes());
+
+    // Read from the slave verifying that the output has been properly transformed
+    let mut buf = [0u8; 10];
+    ::read_exact(pty.slave, &mut buf);
+    let transformed_string = "foofoofoo\n";
+    assert_eq!(&buf, transformed_string.as_bytes());
+}
+
+// Test modifying local flags
+#[test]
+fn test_local_flags() {
+    // Open one pty to get attributes for the second one
+    let mut termios = {
+        let pty = openpty(None, None).unwrap();
+        assert!(pty.master > 0);
+        assert!(pty.slave > 0);
+        let termios = tcgetattr(pty.master).unwrap();
+        termios
+    };
+
+    // Make sure echo is specified by default or this test is useless.
+    assert!(termios.local_flags.contains(ECHO));
+
+    // Disable local echo
+    termios.local_flags.remove(ECHO);
+
+    // Open a new pty with our modified termios settings
+    let pty = openpty(None, &termios).unwrap();
+    assert!(pty.master > 0);
+    assert!(pty.slave > 0);
+
+    // Set the master is in nonblocking mode or reading will never return.
+    let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
+    let new_flags = fcntl::OFlag::from_bits(flags).unwrap() | fcntl::O_NONBLOCK;
+    fcntl::fcntl(pty.master, fcntl::F_SETFL(new_flags)).unwrap();
+
+    // Write into the master
+    let string = "foofoofoo\r";
+    write_all(pty.master, string.as_bytes());
+
+    // Try to read from the master, which should not have anything as echoing was disabled.
+    let mut buf = [0u8; 10];
+    let read = read(pty.master, &mut buf).unwrap_err();
+    assert_eq!(read, Error::Sys(Errno::EAGAIN));
+}
+
+#[test]
+fn test_cfmakeraw() {
+    let mut termios = unsafe { Termios::default_uninit() };
+    termios::cfmakeraw(&mut termios);
+}
diff --git a/test/test_pty.rs b/test/test_pty.rs
index 8528caf75a..53e94724ac 100644
--- a/test/test_pty.rs
+++ b/test/test_pty.rs
@@ -139,7 +139,8 @@ fn test_openpty_with_termios() {
         close(pty.slave).unwrap();
         termios
     };
-    termios.c_oflag &= !ONLCR;
+    // Make sure newlines are not transformed so the data is preserved when sent.
+    termios.output_flags.remove(ONLCR);
 
     let pty = openpty(None, &termios).unwrap();
     // Must be valid file descriptors