From 5cae1fcaa9fec8473294f9e31708e37aa343837d Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Fri, 3 Mar 2017 18:22:16 +0100 Subject: [PATCH 1/5] Use `TCSETS2` ioctl instead of `tcsetattr` on Linux. The TCSETS2 uses the more modern ioctl that supports setting custom bitrates. The only loss here is the ability to specify `optional_actions`; compare https://fossies.org/dox/glibc-2.25/sysdeps_2unix_2bsd_2tcsetattr_8c_source.html As these are not used anyway, this is a net gain in functionality. Gated through conditional compilation to run only on Linux. --- src/posix/tty.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index f68bf3f..2fb4bb6 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -23,6 +23,10 @@ const O_NOCTTY: c_int = 0x00020000; #[cfg(not(any(target_os = "linux", target_os = "macos")))] const O_NOCTTY: c_int = 0; +// FIXME: This constant should move to the termios crate +#[cfg(target_os = "linux")] +const TCSETS2: c_int = -2144578518; + /// A TTY-based serial port implementation. /// @@ -184,9 +188,21 @@ impl SerialDevice for TTYPort { Ok(TTYSettings::new(termios)) } + #[cfg(target_os = "linux")] fn write_settings(&mut self, settings: &TTYSettings) -> ::Result<()> { - use self::termios::{tcsetattr,tcflush}; - use self::termios::{TCSANOW,TCIOFLUSH}; + let err = unsafe { ioctl::ioctl(self.fd, TCSETS2, &settings.termios) }; + + if err != 0 { + return Err(super::error::from_raw_os_error(err)); + } + Ok(()) + } + + #[cfg(not(target_os = "linux"))] + fn write_settings(&mut self, settings: &TTYSettings) -> ::Result<()> { + use self::termios::{tcsetattr, tcflush}; + use self::termios::{TCSANOW, TCIOFLUSH}; + // write settings to TTY if let Err(err) = tcsetattr(self.fd, TCSANOW, &settings.termios) { From d7f866f3492e3552a80bfcf38d1b1ff7aaf15706 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Fri, 3 Mar 2017 18:30:06 +0100 Subject: [PATCH 2/5] Allow specification of nonstandard bitrates on Linux. With support of the `TCSETS2` ioctl, custom bitrates are now possible on Linux. This commit adds support for these. --- src/posix/tty.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index 2fb4bb6..6b27544 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -9,7 +9,7 @@ use std::time::Duration; use std::os::unix::prelude::*; -use self::libc::{c_int,c_void,size_t}; +use self::libc::{c_int,c_uint,c_void,size_t}; use ::{SerialDevice,SerialPortSettings}; @@ -23,6 +23,10 @@ const O_NOCTTY: c_int = 0x00020000; #[cfg(not(any(target_os = "linux", target_os = "macos")))] const O_NOCTTY: c_int = 0; +// FIXME: This constant should move to the termios crate +#[cfg(target_os = "linux")] +const BOTHER: c_uint = 0x1000; + // FIXME: This constant should move to the termios crate #[cfg(target_os = "linux")] const TCSETS2: c_int = -2144578518; @@ -470,7 +474,24 @@ impl SerialPortSettings for TTYSettings { #[cfg(target_os = "linux")] ::BaudOther(4000000) => B4000000, - ::BaudOther(_) => return Err(super::error::from_raw_os_error(EINVAL)) + ::BaudOther(rate) => { + // Custom baud rates are only supported on Linux for now. + // Test this on other platforms and remove the early + // return for those that pass/support this feature + #[cfg(target_os = "linux")] + { + use self::termios::os::linux::CBAUD; + + self.termios.c_cflag &= !CBAUD; + self.termios.c_cflag |= BOTHER; + self.termios.c_ispeed = rate as c_uint; + self.termios.c_ospeed = rate as c_uint; + + return Ok(()); + } + + return Err(super::error::from_raw_os_error(EINVAL)); + } }; match cfsetspeed(&mut self.termios, baud) { From 42e2bcfecbb3a07a0f04fcc1ed711bb117a14c8b Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Sun, 5 Mar 2017 17:02:16 +0100 Subject: [PATCH 3/5] Allow baudrate retrieval from settings and test set/get. --- src/posix/tty.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index 6b27544..6130ef9 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -344,6 +344,15 @@ impl SerialPortSettings for TTYSettings { B3500000 => Some(::BaudOther(3500000)), #[cfg(target_os = "linux")] B4000000 => Some(::BaudOther(4000000)), + #[cfg(target_os = "linux")] + BOTHER => { + + if self.termios.c_ospeed != self.termios.c_ispeed { + return None; + } + + Some(::BaudOther(self.termios.c_ospeed as usize)) + } _ => None } @@ -598,6 +607,14 @@ mod tests { assert_eq!(settings.baud_rate(), Some(::Baud1200)); } + #[test] + fn tty_settings_sets_nonstandard_baud_rate() { + let mut settings = default_settings(); + + settings.set_baud_rate(::BaudOther(12345)).unwrap(); + assert_eq!(settings.baud_rate(), Some(::BaudOther(12345))); + } + #[test] fn tty_settings_sets_char_size() { let mut settings = default_settings(); From c0664fd722519fcfe9cbe53add4fc8990316f757 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Sun, 5 Mar 2017 19:25:40 +0100 Subject: [PATCH 4/5] Fixed broken constant for TCSETS2. --- src/posix/tty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index 6130ef9..e30cadf 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -29,7 +29,7 @@ const BOTHER: c_uint = 0x1000; // FIXME: This constant should move to the termios crate #[cfg(target_os = "linux")] -const TCSETS2: c_int = -2144578518; +const TCSETS2: c_int = 0x402c542b; /// A TTY-based serial port implementation. From d14e52b5c018b49d39f9799fffc2d73778fc83c1 Mon Sep 17 00:00:00 2001 From: Marc Brinkmann Date: Sun, 5 Mar 2017 16:46:01 +0100 Subject: [PATCH 5/5] Do not add unreachable code on linux when setting the baudrate. --- src/posix/tty.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/posix/tty.rs b/src/posix/tty.rs index e30cadf..71a1e42 100644 --- a/src/posix/tty.rs +++ b/src/posix/tty.rs @@ -414,7 +414,6 @@ impl SerialPortSettings for TTYSettings { } fn set_baud_rate(&mut self, baud_rate: ::BaudRate) -> ::Result<()> { - use self::libc::{EINVAL}; use self::termios::cfsetspeed; use self::termios::{B50,B75,B110,B134,B150,B200,B300,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400}; use self::termios::os::target::{B57600,B115200,B230400}; @@ -499,7 +498,11 @@ impl SerialPortSettings for TTYSettings { return Ok(()); } - return Err(super::error::from_raw_os_error(EINVAL)); + #[cfg(not(target_os = "linux"))] + { + use self::libc::EINVAL; + return Err(super::error::from_raw_os_error(EINVAL)); + } } };