Skip to content

Commit

Permalink
Arbitrary baud rates for Linux
Browse files Browse the repository at this point in the history
[close #49]

Implements arbitrary baud rates for Linux by utilizing BOTHER and
c_ospeed/c_ispeed in the termios2 struct.
  • Loading branch information
dcuddeback committed Jun 20, 2018
1 parent a9a1ec6 commit 553810e
Show file tree
Hide file tree
Showing 3 changed files with 139 additions and 102 deletions.
27 changes: 20 additions & 7 deletions serial-unix/src/termios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ use std::os::unix::prelude::RawFd;
#[allow(non_camel_case_types)]
pub type termios = libc::termios;

#[derive(PartialEq, Eq)]
pub enum Speed {
Standard(libc::speed_t),
Custom(libc::speed_t),
}

pub fn read(fd: RawFd) -> core::Result<termios> {
let mut termios: termios = unsafe { mem::uninitialized() };

Expand Down Expand Up @@ -65,19 +71,26 @@ pub fn flush(fd: RawFd) -> core::Result<()> {
Ok(())
}

pub fn get_speed(termios: &termios) -> (libc::speed_t, libc::speed_t) {
pub fn get_speed(termios: &termios) -> (Speed, Speed) {
unsafe {
let ospeed = libc::cfgetospeed(termios);
let ispeed = libc::cfgetispeed(termios);
let ospeed = Speed::Standard(libc::cfgetospeed(termios));
let ispeed = Speed::Standard(libc::cfgetispeed(termios));

(ospeed, ispeed)
}
}

pub fn set_speed(termios: &mut termios, baud: libc::speed_t) -> core::Result<()> {
unsafe {
if libc::cfsetspeed(termios, baud) < 0 {
return Err(super::error::last_os_error());
pub fn set_speed(termios: &mut termios, speed: Speed) -> core::Result<()> {
use libc::EINVAL;

match speed {
Speed::Standard(baud) => unsafe {
if libc::cfsetspeed(termios, baud) < 0 {
return Err(super::error::last_os_error());
}
},
Speed::Custom(_) => {
return Err(super::error::from_raw_os_error(EINVAL));
}
}

Expand Down
32 changes: 27 additions & 5 deletions serial-unix/src/termios2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@ use std::os::unix::prelude::RawFd;
#[allow(non_camel_case_types)]
pub type termios = libc::termios2;

#[derive(PartialEq, Eq, Clone, Copy)]
pub enum Speed {
Standard(libc::speed_t),
Custom(libc::speed_t),
}

const IBSHIFT: usize = 16;
const BOTHER: libc::speed_t = 0x1000;

#[cfg(not(any(target_env = "musl",
target_env = "android")))]
Expand Down Expand Up @@ -111,25 +118,40 @@ pub fn flush(fd: RawFd) -> core::Result<()> {

// See tty_termios_baud_rate() and tty_termios_input_baud_rate() in drivers/tty/tty_baudrate.c in
// the Linux kernel source.
pub fn get_speed(termios: &termios) -> (libc::speed_t, libc::speed_t) {
pub fn get_speed(termios: &termios) -> (Speed, Speed) {
use libc::{CBAUD, B0};

let ospeed = termios.c_cflag & CBAUD;
let ospeed = match termios.c_cflag & CBAUD {
BOTHER => Speed::Custom(termios.c_ospeed),
speed => Speed::Standard(speed),
};

let ispeed = match termios.c_cflag >> IBSHIFT & CBAUD {
B0 => ospeed,
n => n,
BOTHER => Speed::Custom(termios.c_ispeed),
speed => Speed::Standard(speed),
};

(ospeed, ispeed)
}

// See tty_termios_baud_rate() and tty_termios_input_baud_rate() in drivers/tty/tty_baudrate.c in
// the Linux kernel source.
pub fn set_speed(termios: &mut termios, baud: libc::speed_t) -> core::Result<()> {
pub fn set_speed(termios: &mut termios, speed: Speed) -> core::Result<()> {
use libc::{CBAUD, B0};

termios.c_cflag &= !(CBAUD | CBAUD << IBSHIFT);
termios.c_cflag |= baud | B0 << IBSHIFT;
termios.c_cflag |= B0 << IBSHIFT;

match speed {
Speed::Standard(baud) => {
termios.c_cflag |= baud;
},
Speed::Custom(baud) => {
termios.c_cflag |= BOTHER;
termios.c_ospeed = baud;
},
}

Ok(())
}
182 changes: 92 additions & 90 deletions serial-unix/src/tty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,58 +323,61 @@ impl SerialPortSettings for TTYSettings {
}

match ospeed {
B50 => Some(core::BaudOther(50)),
B75 => Some(core::BaudOther(75)),
B110 => Some(core::Baud110),
B134 => Some(core::BaudOther(134)),
B150 => Some(core::BaudOther(150)),
B200 => Some(core::BaudOther(200)),
B300 => Some(core::Baud300),
B600 => Some(core::Baud600),
B1200 => Some(core::Baud1200),
B1800 => Some(core::BaudOther(1800)),
B2400 => Some(core::Baud2400),
B4800 => Some(core::Baud4800),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B7200 => Some(core::BaudOther(7200)),
B9600 => Some(core::Baud9600),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B14400 => Some(core::BaudOther(14400)),
B19200 => Some(core::Baud19200),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B28800 => Some(core::BaudOther(28800)),
B38400 => Some(core::Baud38400),
B57600 => Some(core::Baud57600),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B76800 => Some(core::BaudOther(76800)),
B115200 => Some(core::Baud115200),
B230400 => Some(core::BaudOther(230400)),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
B460800 => Some(core::BaudOther(460800)),
#[cfg(target_os = "linux")]
B500000 => Some(core::BaudOther(500000)),
#[cfg(target_os = "linux")]
B576000 => Some(core::BaudOther(576000)),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
B921600 => Some(core::BaudOther(921600)),
#[cfg(target_os = "linux")]
B1000000 => Some(core::BaudOther(1000000)),
#[cfg(target_os = "linux")]
B1152000 => Some(core::BaudOther(1152000)),
#[cfg(target_os = "linux")]
B1500000 => Some(core::BaudOther(1500000)),
#[cfg(target_os = "linux")]
B2000000 => Some(core::BaudOther(2000000)),
#[cfg(target_os = "linux")]
B2500000 => Some(core::BaudOther(2500000)),
#[cfg(target_os = "linux")]
B3000000 => Some(core::BaudOther(3000000)),
#[cfg(target_os = "linux")]
B3500000 => Some(core::BaudOther(3500000)),
#[cfg(target_os = "linux")]
B4000000 => Some(core::BaudOther(4000000)),

_ => None,
termios::Speed::Standard(baud) => match baud {
B50 => Some(core::BaudOther(50)),
B75 => Some(core::BaudOther(75)),
B110 => Some(core::Baud110),
B134 => Some(core::BaudOther(134)),
B150 => Some(core::BaudOther(150)),
B200 => Some(core::BaudOther(200)),
B300 => Some(core::Baud300),
B600 => Some(core::Baud600),
B1200 => Some(core::Baud1200),
B1800 => Some(core::BaudOther(1800)),
B2400 => Some(core::Baud2400),
B4800 => Some(core::Baud4800),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B7200 => Some(core::BaudOther(7200)),
B9600 => Some(core::Baud9600),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B14400 => Some(core::BaudOther(14400)),
B19200 => Some(core::Baud19200),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B28800 => Some(core::BaudOther(28800)),
B38400 => Some(core::Baud38400),
B57600 => Some(core::Baud57600),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
B76800 => Some(core::BaudOther(76800)),
B115200 => Some(core::Baud115200),
B230400 => Some(core::BaudOther(230400)),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
B460800 => Some(core::BaudOther(460800)),
#[cfg(target_os = "linux")]
B500000 => Some(core::BaudOther(500000)),
#[cfg(target_os = "linux")]
B576000 => Some(core::BaudOther(576000)),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
B921600 => Some(core::BaudOther(921600)),
#[cfg(target_os = "linux")]
B1000000 => Some(core::BaudOther(1000000)),
#[cfg(target_os = "linux")]
B1152000 => Some(core::BaudOther(1152000)),
#[cfg(target_os = "linux")]
B1500000 => Some(core::BaudOther(1500000)),
#[cfg(target_os = "linux")]
B2000000 => Some(core::BaudOther(2000000)),
#[cfg(target_os = "linux")]
B2500000 => Some(core::BaudOther(2500000)),
#[cfg(target_os = "linux")]
B3000000 => Some(core::BaudOther(3000000)),
#[cfg(target_os = "linux")]
B3500000 => Some(core::BaudOther(3500000)),
#[cfg(target_os = "linux")]
B4000000 => Some(core::BaudOther(4000000)),

_ => None,
},
termios::Speed::Custom(baud) => Some(core::BaudOther(baud as usize)),
}
}

Expand Down Expand Up @@ -434,7 +437,6 @@ impl SerialPortSettings for TTYSettings {
}

fn set_baud_rate(&mut self, baud_rate: core::BaudRate) -> core::Result<()> {
use libc::EINVAL;
use libc::{B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800, B9600, B19200, B38400};
use libc::{B57600, B115200, B230400};

Expand All @@ -450,62 +452,62 @@ impl SerialPortSettings for TTYSettings {
#[cfg(target_os = "openbsd")]
use libc::{B7200, B14400, B28800, B76800};

let baud = match baud_rate {
core::BaudOther(50) => B50,
core::BaudOther(75) => B75,
core::Baud110 => B110,
core::BaudOther(134) => B134,
core::BaudOther(150) => B150,
core::BaudOther(200) => B200,
core::Baud300 => B300,
core::Baud600 => B600,
core::Baud1200 => B1200,
core::BaudOther(1800) => B1800,
core::Baud2400 => B2400,
core::Baud4800 => B4800,
let speed = match baud_rate {
core::BaudOther(50) => termios::Speed::Standard(B50),
core::BaudOther(75) => termios::Speed::Standard(B75),
core::Baud110 => termios::Speed::Standard(B110),
core::BaudOther(134) => termios::Speed::Standard(B134),
core::BaudOther(150) => termios::Speed::Standard(B150),
core::BaudOther(200) => termios::Speed::Standard(B200),
core::Baud300 => termios::Speed::Standard(B300),
core::Baud600 => termios::Speed::Standard(B600),
core::Baud1200 => termios::Speed::Standard(B1200),
core::BaudOther(1800) => termios::Speed::Standard(B1800),
core::Baud2400 => termios::Speed::Standard(B2400),
core::Baud4800 => termios::Speed::Standard(B4800),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
core::BaudOther(7200) => B7200,
core::Baud9600 => B9600,
core::BaudOther(7200) => termios::Speed::Standard(B7200),
core::Baud9600 => termios::Speed::Standard(B9600),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
core::BaudOther(14400) => B14400,
core::Baud19200 => B19200,
core::BaudOther(14400) => termios::Speed::Standard(B14400),
core::Baud19200 => termios::Speed::Standard(B19200),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
core::BaudOther(28800) => B28800,
core::Baud38400 => B38400,
core::Baud57600 => B57600,
core::BaudOther(28800) => termios::Speed::Standard(B28800),
core::Baud38400 => termios::Speed::Standard(B38400),
core::Baud57600 => termios::Speed::Standard(B57600),
#[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "openbsd"))]
core::BaudOther(76800) => B76800,
core::Baud115200 => B115200,
core::BaudOther(230400) => B230400,
core::BaudOther(76800) => termios::Speed::Standard(B76800),
core::Baud115200 => termios::Speed::Standard(B115200),
core::BaudOther(230400) => termios::Speed::Standard(B230400),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
core::BaudOther(460800) => B460800,
core::BaudOther(460800) => termios::Speed::Standard(B460800),
#[cfg(target_os = "linux")]
core::BaudOther(500000) => B500000,
core::BaudOther(500000) => termios::Speed::Standard(B500000),
#[cfg(target_os = "linux")]
core::BaudOther(576000) => B576000,
core::BaudOther(576000) => termios::Speed::Standard(B576000),
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
core::BaudOther(921600) => B921600,
core::BaudOther(921600) => termios::Speed::Standard(B921600),
#[cfg(target_os = "linux")]
core::BaudOther(1000000) => B1000000,
core::BaudOther(1000000) => termios::Speed::Standard(B1000000),
#[cfg(target_os = "linux")]
core::BaudOther(1152000) => B1152000,
core::BaudOther(1152000) => termios::Speed::Standard(B1152000),
#[cfg(target_os = "linux")]
core::BaudOther(1500000) => B1500000,
core::BaudOther(1500000) => termios::Speed::Standard(B1500000),
#[cfg(target_os = "linux")]
core::BaudOther(2000000) => B2000000,
core::BaudOther(2000000) => termios::Speed::Standard(B2000000),
#[cfg(target_os = "linux")]
core::BaudOther(2500000) => B2500000,
core::BaudOther(2500000) => termios::Speed::Standard(B2500000),
#[cfg(target_os = "linux")]
core::BaudOther(3000000) => B3000000,
core::BaudOther(3000000) => termios::Speed::Standard(B3000000),
#[cfg(target_os = "linux")]
core::BaudOther(3500000) => B3500000,
core::BaudOther(3500000) => termios::Speed::Standard(B3500000),
#[cfg(target_os = "linux")]
core::BaudOther(4000000) => B4000000,
core::BaudOther(4000000) => termios::Speed::Standard(B4000000),

core::BaudOther(_) => return Err(super::error::from_raw_os_error(EINVAL)),
core::BaudOther(baud) => termios::Speed::Custom(baud as libc::speed_t),
};

try!(termios::set_speed(&mut self.termios, baud));
try!(termios::set_speed(&mut self.termios, speed));

Ok(())
}
Expand Down

0 comments on commit 553810e

Please sign in to comment.