//! 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](https://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: Termios = unsafe { std::mem::zeroed() }; //! 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::{ControlFlags, Termios}; //! # let mut termios: Termios = unsafe { std::mem::zeroed() }; //! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5; //! termios.control_flags |= ControlFlags::CS5; //! ``` //! //! # Baud rates //! //! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both //! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs //! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer //! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following //! conventions: //! //! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux //! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux //! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux //! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux //! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux //! //! The most common use case of specifying a baud rate using the enum will work the same across //! platforms: //! //! ```rust //! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios}; //! # fn main() { //! # let mut t: Termios = unsafe { std::mem::zeroed() }; //! cfsetispeed(&mut t, BaudRate::B9600).unwrap(); //! cfsetospeed(&mut t, BaudRate::B9600).unwrap(); //! cfsetspeed(&mut t, BaudRate::B9600).unwrap(); //! # } //! ``` //! //! Additionally round-tripping baud rates is consistent across platforms: //! //! ```rust //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios}; //! # fn main() { //! # let mut t: Termios = unsafe { std::mem::zeroed() }; //! # cfsetspeed(&mut t, BaudRate::B9600).unwrap(); //! let speed = cfgetispeed(&t); //! assert_eq!(speed, cfgetospeed(&t)); //! cfsetispeed(&mut t, speed).unwrap(); //! # } //! ``` //! //! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`: //! #![cfg_attr(bsd, doc = " ```rust,ignore")] #![cfg_attr(not(bsd), doc = " ```rust")] //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; //! # fn main() { //! # let mut t: Termios = unsafe { std::mem::zeroed() }; //! # cfsetspeed(&mut t, BaudRate::B9600); //! assert_eq!(cfgetispeed(&t), BaudRate::B9600); //! assert_eq!(cfgetospeed(&t), BaudRate::B9600); //! # } //! ``` //! //! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s: //! #![cfg_attr(bsd, doc = " ```rust")] #![cfg_attr(not(bsd), doc = " ```rust,ignore")] //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios}; //! # fn main() { //! # let mut t: Termios = unsafe { std::mem::zeroed() }; //! # cfsetspeed(&mut t, 9600u32); //! assert_eq!(cfgetispeed(&t), 9600u32); //! assert_eq!(cfgetospeed(&t), 9600u32); //! # } //! ``` //! //! It's trivial to convert from a `BaudRate` to a `u32` on BSDs: //! #![cfg_attr(bsd, doc = " ```rust")] #![cfg_attr(not(bsd), doc = " ```rust,ignore")] //! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios}; //! # fn main() { //! # let mut t: Termios = unsafe { std::mem::zeroed() }; //! # cfsetspeed(&mut t, 9600u32); //! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into()); //! assert_eq!(u32::from(BaudRate::B9600), 9600u32); //! # } //! ``` //! //! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support) //! by specifying baud rates directly using `u32`s: //! #![cfg_attr(bsd, doc = " ```rust")] #![cfg_attr(not(bsd), doc = " ```rust,ignore")] //! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios}; //! # fn main() { //! # let mut t: Termios = unsafe { std::mem::zeroed() }; //! cfsetispeed(&mut t, 9600u32); //! cfsetospeed(&mut t, 9600u32); //! cfsetspeed(&mut t, 9600u32); //! # } //! ``` usecrate::errno::Errno; usecrate::Result; use cfg_if::cfg_if; use libc::{self, c_int, tcflag_t}; use std::cell::{Ref, RefCell}; use std::convert::From; use std::mem; use std::os::unix::io::{AsFd, AsRawFd};
/// 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, Debug, Eq, PartialEq)] pubstruct 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; NCCS], /// Line discipline (see `termios.c_line` documentation) #[cfg(linux_android)] pub line_discipline: libc::cc_t, /// Line discipline (see `termios.c_line` documentation) #[cfg(target_os = "haiku")] pub line_discipline: libc::c_char,
}
impl Termios { /// Exposes an immutable reference to the underlying `libc::termios` data structure. /// /// This is not part of `nix`'s public API because it requires additional work to maintain type /// safety. pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
{ letmut 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; #[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
} self.inner.borrow()
}
/// 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. In this case it should also be /// paired with a call to `update_wrapper()` so that the wrapper-type and internal /// representation stay consistent. pub(crate) unsafefn get_libc_termios_mut(&mutself) -> *mut libc::termios {
{ letmut 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; #[cfg(any(linux_android, target_os = "haiku"))]
{
termios.c_line = self.line_discipline;
}
} self.inner.as_ptr()
}
/// Updates the wrapper values from the internal `libc::termios` data structure. pub(crate) fn update_wrapper(&mutself) { 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_retain(termios.c_cflag); self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag); self.control_chars = termios.c_cc; #[cfg(any(linux_android, target_os = "haiku"))]
{ self.line_discipline = termios.c_line;
}
}
}
#[cfg(bsd)] impl From<BaudRate> for u32 { fn from(b: BaudRate) -> u32 {
b as u32
}
}
#[cfg(target_os = "haiku")] impl From<BaudRate> for u8 { fn from(b: BaudRate) -> u8 {
b as u8
}
}
// 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)] #[non_exhaustive] pubenum 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,
}
}
libc_enum! { /// Specify a combination of the input and output buffers to flush /// /// Used as an argument to `tcflush()`. #[repr(i32)] #[non_exhaustive] pubenum 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,
}
}
libc_enum! { /// Specify how transmission flow should be altered /// /// Used as an argument to `tcflow()`. #[repr(i32)] #[non_exhaustive] pubenum 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,
}
}
cfg_if! { if#[cfg(bsd)] { /// Get input baud rate (see /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)). /// /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure. // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] pubfn cfgetispeed(termios: &Termios) -> u32 { let inner_termios = termios.get_libc_termios(); unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
}
/// Get output baud rate (see /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)). /// /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure. // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] pubfn cfgetospeed(termios: &Termios) -> u32 { let inner_termios = termios.get_libc_termios(); unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
}
/// Set input baud rate (see /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)). /// /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure. pubfn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> { let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set output baud rate (see /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)). /// /// `cfsetospeed()` sets the output baud rate in the given termios structure. pubfn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> { let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set both the input and output baud rates (see /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)). /// /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that /// this is part of the 4.4BSD standard and not part of POSIX. pubfn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> { let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
} else { use std::convert::TryInto;
/// Get input baud rate (see /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)). /// /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure. pubfn cfgetispeed(termios: &Termios) -> BaudRate { let inner_termios = termios.get_libc_termios(); unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
}
/// Get output baud rate (see /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)). /// /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure. pubfn cfgetospeed(termios: &Termios) -> BaudRate { let inner_termios = termios.get_libc_termios(); unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
}
/// Set input baud rate (see /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)). /// /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure. pubfn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> { let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set output baud rate (see /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)). /// /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure. pubfn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> { let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
/// Set both the input and output baud rates (see /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)). /// /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that /// this is part of the 4.4BSD standard and not part of POSIX. #[cfg(not(target_os = "haiku"))] pubfn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> { let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) };
termios.update_wrapper();
Errno::result(res).map(drop)
}
}
}
/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see /// [termios(3)](https://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. pubfn cfmakeraw(termios: &mut Termios) { let inner_termios = unsafe { termios.get_libc_termios_mut() }; unsafe {
libc::cfmakeraw(inner_termios);
}
termios.update_wrapper();
}
/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see /// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)). /// /// Note that this is a non-standard function, available on FreeBSD. #[cfg(target_os = "freebsd")] pubfn cfmakesane(termios: &mut Termios) { let inner_termios = unsafe { termios.get_libc_termios_mut() }; unsafe {
libc::cfmakesane(inner_termios);
}
termios.update_wrapper();
}
/// Return the configuration of a port /// [tcgetattr(3p)](https://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()`. pubfn tcgetattr<Fd: AsFd>(fd: Fd) -> Result<Termios> { letmut termios = mem::MaybeUninit::uninit();
let res = unsafe {
libc::tcgetattr(fd.as_fd().as_raw_fd(), termios.as_mut_ptr())
};
Errno::result(res)?;
unsafe { Ok(termios.assume_init().into()) }
}
/// Set the configuration for a terminal (see /// [tcsetattr(3p)](https://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. pubfn tcsetattr<Fd: AsFd>(
fd: Fd,
actions: SetArg,
termios: &Termios,
) -> Result<()> { let inner_termios = termios.get_libc_termios();
Errno::result(unsafe {
libc::tcsetattr(
fd.as_fd().as_raw_fd(),
actions as c_int,
&*inner_termios,
)
})
.map(drop)
}
/// Suspend or resume the transmission or reception of data (see /// [tcflow(3p)](https://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`. pubfn tcflow<Fd: AsFd>(fd: Fd, action: FlowArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflow(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Discard data in the output or input queue (see /// [tcflush(3p)](https://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`. pubfn tcflush<Fd: AsFd>(fd: Fd, action: FlushArg) -> Result<()> {
Errno::result(unsafe {
libc::tcflush(fd.as_fd().as_raw_fd(), action as c_int)
})
.map(drop)
}
/// Send a break for a specific duration (see /// [tcsendbreak(3p)](https://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. pubfn tcsendbreak<Fd: AsFd>(fd: Fd, duration: c_int) -> Result<()> {
Errno::result(unsafe {
libc::tcsendbreak(fd.as_fd().as_raw_fd(), duration)
})
.map(drop)
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.