dmx_termios/lib.rs
1//! The `termios` crate provides Rust bindings for the POSIX termios API that is implemented on
2//! Unix operating systems. The termios API is defined in the [IEEE Std 1003.1 ("POSIX.1")
3//! specification](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html).
4//!
5//! ## Getting Started
6//!
7//! The termios API operates on file descriptors that are associated with terminal devices, e.g.,
8//! `/dev/tty*`. When used with other file descriptors, termios functions return an error. All
9//! functions that are part of the POSIX standard are included in the `termios` crate. Where file
10//! descriptors are expected, the type `std::os::unix::io::RawFd` is used, and integer error codes
11//! are translated to `std::io::Result`.
12//!
13//! A major feature of the termios API is configuring a terminal device's parameters. The POSIX
14//! standard defines a `termios` structure that contains the parameters and several functions for
15//! manipulating the parameters. The `termios` crate defines a safe constructor that returns a
16//! [`Termios`](struct.Termios.html) struct populated with the parameters of an open terminal
17//! device:
18//!
19//! ```no_run
20//! use termios::*;
21//! # let fd = 1;
22//! let mut termios = Termios::from_fd(fd).unwrap();
23//! ```
24//!
25//! The [`Termios`](struct.Termios.html) struct provides access to the fields defined in the POSIX
26//! standard (`c_iflag`, `c_oflag`, `c_cflag`, `c_lflag`, and `c_cc`):
27//!
28//! ```no_run
29//! # use termios::*;
30//! # let fd = 1;
31//! # let mut termios = Termios::from_fd(fd).unwrap();
32//! termios.c_cflag |= CREAD | CLOCAL;
33//! termios.c_lflag &= !(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN);
34//! termios.c_oflag &= !OPOST;
35//! termios.c_iflag &= !(INLCR | IGNCR | ICRNL | IGNBRK);
36//!
37//! termios.c_cc[VMIN] = 0;
38//! termios.c_cc[VTIME] = 0;
39//! ```
40//!
41//! The [`Termios`](struct.Termios.html) struct can also be manipulated using any of the standard
42//! termios API functions:
43//!
44//! ```no_run
45//! # use termios::*;
46//! # let fd = 1;
47//! # let mut termios = Termios::from_fd(fd).unwrap();
48//! cfgetispeed(&termios);
49//! cfgetospeed(&termios);
50//! cfsetispeed(&mut termios, B9600).unwrap();
51//! cfsetospeed(&mut termios, B9600).unwrap();
52//! tcsetattr(fd, TCSANOW, &termios).unwrap();
53//! ```
54//!
55//! ## Portability
56//!
57//! The `termios` crate is organized in a way to help write portable code, while also allowing
58//! access to OS-specific functionality when necessary.
59//!
60//! The crate root contains types, constants, and function definitions that are common across Unix
61//! operating systems. Most of the definitions in the crate root are from the POSIX standard;
62//! however, support for the standard may differ across operating systems. A couple functions in
63//! the crate root are not part of the POSIX standard, but are included in the crate root because
64//! they are widely available across Unix operating systems.
65//!
66//! To write portable code, import the `termios` crate and use only the definitions from the crate
67//! root.
68//!
69//! ### OS-Specific Extensions
70//!
71//! Each operating system may define extensions to the POSIX API. To make it clear when code
72//! depends on OS-specific definitions, any non-standard definitions are exported in the
73//! `termios::os` module. Programs that depend on OS-specific functionality must explicity opt-in.
74//! When writing portable code that depends on OS-specific definitions, it will often be necessary
75//! to use `#[cfg(...)]` attributes to support alternative implementations. The following is an
76//! example of a portable function that sets the maximum speed on a `Termios` struct.
77//!
78//! ```no_run
79//! use std::io;
80//! use termios::{Termios,cfsetspeed};
81//!
82//! #[cfg(target_os = "linux")]
83//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
84//! cfsetspeed(termios, termios::os::linux::B4000000)
85//! }
86//!
87//! #[cfg(target_os = "macos")]
88//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
89//! cfsetspeed(termios, termios::os::macos::B230400)
90//! }
91//!
92//! #[cfg(target_os = "freebsd")]
93//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
94//! cfsetspeed(termios, termios::os::freebsd::B921600)
95//! }
96//!
97//! #[cfg(target_os = "openbsd")]
98//! fn set_fastest_speed(termios: &mut Termios) -> io::Result<()> {
99//! cfsetspeed(termios, termios::os::openbsd::B921600)
100//! }
101//!
102//! # let fd = 1;
103//! let mut termios = Termios::from_fd(fd).unwrap();
104//! set_fastest_speed(&mut termios).unwrap();
105//! ```
106
107extern crate libc;
108extern crate ioctl_rs as ioctl;
109
110
111use std::io;
112use std::mem;
113use std::ops::{Deref,DerefMut};
114use std::os::unix::io::RawFd;
115
116use libc::{c_int,pid_t};
117
118pub use ::os::target::{cc_t,speed_t,tcflag_t}; // types
119pub use ::os::target::{VEOF,VEOL,VERASE,VINTR,VKILL,VMIN,VQUIT,VSTART,VSTOP,VSUSP,VTIME}; // c_cc subscripts
120pub use ::os::target::{BRKINT,ICRNL,IGNBRK,IGNCR,IGNPAR,INLCR,INPCK,ISTRIP,IXANY,IXOFF,IXON,PARMRK}; // input modes
121pub use ::os::target::{OPOST,ONLCR,OCRNL,TAB3,ONOCR,ONLRET}; // output modes
122pub use ::os::target::{B0,B50,B75,B110,B134,B150,B200,B300,B600,B1200,B1800,B2400,B4800,B9600,B19200,B38400}; // baud rate selection
123pub use ::os::target::{CSIZE,CS5,CS6,CS7,CS8,CSTOPB,CREAD,PARENB,PARODD,HUPCL,CLOCAL}; // control modes
124pub use ::os::target::{ECHO,ECHOE,ECHOK,ECHONL,ICANON,IEXTEN,ISIG,NOFLSH,TOSTOP}; // local modes
125pub use ::os::target::{TCSANOW,TCSADRAIN,TCSAFLUSH}; // attribute selection
126pub use ::os::target::{TCIFLUSH,TCIOFLUSH,TCOFLUSH,TCIOFF,TCION,TCOOFF,TCOON}; // line control
127
128pub mod ffi;
129pub mod os;
130
131
132/// Unix terminal I/O control structure.
133///
134/// The `Termios` structure is a thin wrapper for the OS-specific `termios` struct. The only safe
135/// way to obtain a `Termios` structure is to fill one from a file descriptor with
136/// [`Termios::from_fd()`](#method.from_fd), after which it can be treated just like the POSIX
137/// `termios` struct. It provides access to the standard fields of the `termios` struct (`c_iflag`,
138/// `c_oflag`, `c_cflag`, `c_lflag`, and `c_cc`) through the `Deref` and `DerefMut` traits.
139///
140/// ## Example
141///
142/// The following is an example of how one might setup a file descriptor for a serial port:
143///
144/// ```no_run
145/// use std::io;
146/// use std::os::unix::io::RawFd;
147///
148/// fn setup_serial(fd: RawFd) -> io::Result<()> {
149/// use termios::*;
150///
151/// let mut termios = try!(Termios::from_fd(fd));
152///
153/// termios.c_cflag |= CREAD | CLOCAL;
154/// termios.c_lflag &= !(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG | IEXTEN);
155/// termios.c_oflag &= !OPOST;
156/// termios.c_iflag &= !(INLCR | IGNCR | ICRNL | IGNBRK);
157///
158/// termios.c_cc[VMIN] = 0;
159/// termios.c_cc[VTIME] = 0;
160///
161/// try!(cfsetspeed(&mut termios, B9600));
162/// try!(tcsetattr(fd, TCSANOW, &mut termios));
163///
164/// Ok(())
165/// }
166/// ```
167#[derive(Debug,Copy,Clone,Eq,PartialEq)]
168pub struct Termios {
169 inner: ::os::target::termios
170}
171
172impl Termios {
173 /// Creates a `Termios` structure based on the current settings of a file descriptor.
174 ///
175 /// `fd` must be an open file descriptor for a terminal device.
176 #[cfg(not(target_os = "linux"))]
177 pub fn from_fd(fd: RawFd) -> io::Result<Self> {
178 let mut termios = unsafe { mem::uninitialized() };
179
180 match tcgetattr(fd, &mut termios) {
181 Ok(_) => Ok(termios),
182 Err(err) => Err(err)
183 }
184 }
185
186 /// Creates a `Termios` structure based on the current settings of a file descriptor.
187 ///
188 /// `fd` must be an open file descriptor for a terminal device.
189 #[cfg(target_os = "linux")]
190 pub fn from_fd(fd: RawFd) -> io::Result<Self> {
191 let mut termios: Termios = unsafe { mem::uninitialized() };
192
193 // on Linux, we use an ioctl to get the correct data structure
194 let err = unsafe { ioctl::ioctl(fd, os::linux::TCGETS2,
195 &mut termios.inner) };
196
197 if err != 0 {
198 return Err(io::Error::last_os_error())
199 }
200
201 Ok(termios)
202 }
203
204
205 fn inner(&self) -> &::os::target::termios {
206 &self.inner
207 }
208
209 fn inner_mut(&mut self) -> &mut ::os::target::termios {
210 &mut self.inner
211 }
212}
213
214impl Deref for Termios {
215 type Target = ::os::target::termios;
216
217 fn deref(&self) -> &::os::target::termios {
218 self.inner()
219 }
220}
221
222impl DerefMut for Termios {
223 fn deref_mut(&mut self) -> &mut ::os::target::termios {
224 self.inner_mut()
225 }
226}
227
228
229/// Gets the input baud rate stored in a `Termios` structure.
230///
231/// # Examples
232///
233/// ```
234/// # use std::mem;
235/// # use termios::{Termios,B9600,cfsetispeed,cfgetispeed};
236/// # let mut termios = unsafe { mem::uninitialized() };
237/// cfsetispeed(&mut termios, B9600).unwrap();
238/// assert_eq!(cfgetispeed(&termios), B9600);
239/// ```
240pub fn cfgetispeed(termios: &Termios) -> speed_t {
241 unsafe { ffi::cfgetispeed(termios.inner()) }
242}
243
244/// Gets the output baud rate stored in a `Termios` structure.
245///
246/// # Examples
247///
248/// ```
249/// # use std::mem;
250/// # use termios::{Termios,B9600,cfsetospeed,cfgetospeed};
251/// # let mut termios = unsafe { mem::uninitialized() };
252/// cfsetospeed(&mut termios, B9600).unwrap();
253/// assert_eq!(cfgetospeed(&termios), B9600);
254/// ```
255pub fn cfgetospeed(termios: &Termios) -> speed_t {
256 unsafe { ffi::cfgetospeed(termios.inner()) }
257}
258
259/// Sets the input baud rate.
260///
261/// This function only sets the necessary values on the given `Termios` structure. The settings are
262/// applied by a subsequent call to [`tcsetattr()`](fn.tcsetattr.html).
263///
264/// # Parameters
265///
266/// * `termios` should be a mutable reference to a `Termios` structure.
267/// * `speed` should be one of the baud rate constants:
268/// - `B0`
269/// - `B50`
270/// - `B75`
271/// - `B110`
272/// - `B134`
273/// - `B150`
274/// - `B200`
275/// - `B300`
276/// - `B600`
277/// - `B1200`
278/// - `B1800`
279/// - `B2400`
280/// - `B4800`
281/// - `B9600`
282/// - `B19200`
283/// - `B38400`
284/// - any OS-specific baud rate defined in [`termios::os`](os/index.html).
285///
286/// A value of `B0` for `speed` sets the input baud rate to be the same as the output baud rate.
287///
288/// # Examples
289///
290/// ```
291/// # use std::mem;
292/// # use termios::{Termios,B9600,cfsetispeed,cfgetispeed};
293/// # let mut termios = unsafe { mem::uninitialized() };
294/// cfsetispeed(&mut termios, B9600).unwrap();
295/// assert_eq!(cfgetispeed(&termios), B9600);
296/// ```
297pub fn cfsetispeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> {
298 io_result(unsafe { ffi::cfsetispeed(termios.inner_mut(), speed) })
299}
300
301/// Sets the output baud rate.
302///
303/// This function only sets the necessary values on the given `Termios` structure. The settings are
304/// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html).
305///
306/// # Parameters
307///
308/// * `termios` should be a mutable reference to a `Termios` structure.
309/// * `speed` should be one of the baud rate constants:
310/// - `B0` (hang up)
311/// - `B50`
312/// - `B75`
313/// - `B110`
314/// - `B134`
315/// - `B150`
316/// - `B200`
317/// - `B300`
318/// - `B600`
319/// - `B1200`
320/// - `B1800`
321/// - `B2400`
322/// - `B4800`
323/// - `B9600`
324/// - `B19200`
325/// - `B38400`
326/// - any OS-specific baud rate defined in [`termios::os`](os/index.html).
327///
328/// A value of `B0` for `speed` deasserts the modem control lines when applied with
329/// [`tcsetattr()`](fn.tcsetattr.html). This normally has the effect of disconnecting the line.
330///
331/// # Examples
332///
333/// ```
334/// # use std::mem;
335/// # use termios::{Termios,B9600,cfsetospeed,cfgetospeed};
336/// # let mut termios = unsafe { mem::uninitialized() };
337/// cfsetospeed(&mut termios, B9600).unwrap();
338/// assert_eq!(cfgetospeed(&termios), B9600);
339/// ```
340pub fn cfsetospeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> {
341 io_result(unsafe { ffi::cfsetospeed(termios.inner_mut(), speed) })
342}
343
344/// Sets input and output baud rates.
345///
346/// This function only sets the necessary values on the given `Termios` structure. The settings are
347/// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html).
348///
349/// # Parameters
350///
351/// * `termios` should be a mutable reference to a `Termios` structure.
352/// * `speed` should be one of the baud rate constants:
353/// - `B0`
354/// - `B50`
355/// - `B75`
356/// - `B110`
357/// - `B134`
358/// - `B150`
359/// - `B200`
360/// - `B300`
361/// - `B600`
362/// - `B1200`
363/// - `B1800`
364/// - `B2400`
365/// - `B4800`
366/// - `B9600`
367/// - `B19200`
368/// - `B38400`
369/// - any OS-specific baud rate defined in [`termios::os`](os/index.html).
370///
371/// # Examples
372///
373/// ```
374/// # use std::mem;
375/// # use termios::{Termios,B9600,cfsetspeed,cfgetispeed,cfgetospeed};
376/// # let mut termios = unsafe { mem::uninitialized() };
377/// cfsetspeed(&mut termios, B9600).unwrap();
378/// assert_eq!(cfgetispeed(&termios), B9600);
379/// assert_eq!(cfgetospeed(&termios), B9600);
380/// ```
381///
382/// # Portability
383///
384/// This function is not part of the IEEE Std 1003.1 ("POSIX.1") specification, but it is available
385/// on Linux, BSD, and OS X.
386pub fn cfsetspeed(termios: &mut Termios, speed: speed_t) -> io::Result<()> {
387 io_result(unsafe { ffi::cfsetspeed(termios.inner_mut(), speed) })
388}
389
390/// Sets flags to disable all input and output processing.
391///
392/// This function only sets the necessary values on the given `Termios` structure. The settings are
393/// applied on a successful call to [`tcsetattr()`](fn.tcsetattr.html).
394///
395/// # Portability
396///
397/// This function is not part of the IEEE Std 1003.1 ("POSIX.1") specification, but it is available
398/// on Linux, BSD, and OS X.
399pub fn cfmakeraw(termios: &mut Termios) {
400 unsafe { ffi::cfmakeraw(termios.inner_mut()) };
401}
402
403/// Blocks until all output written to the file descriptor is transmitted.
404///
405/// # Parameters
406///
407/// * `fd` should be an open file descriptor associated with a terminal.
408pub fn tcdrain(fd: RawFd) -> io::Result<()> {
409 io_result(unsafe { ffi::tcdrain(fd) })
410}
411
412/// Suspends or restarts transmission or reception of data.
413///
414/// # Parameters
415///
416/// * `fd` should be an open file descriptor associated with a terminal.
417/// * `action` should be one of the following constants:
418/// - `TCOOFF` suspends output.
419/// - `TCOON` restarts output.
420/// - `TCIOFF` transmits a STOP character, intended to cause the remote device to stop
421/// transmitting.
422/// - `TCION` transmits a START character, intended to cause the remote device to resume
423/// transmitting.
424pub fn tcflow(fd: RawFd, action: c_int) -> io::Result<()> {
425 io_result(unsafe { ffi::tcflow(fd, action) })
426}
427
428/// Discards data waiting in the terminal device's buffers.
429///
430/// `tcflush()` discards data that has been written to the device by an application but has not yet
431/// been transmitted by the hardware or data that has been received by the hardware but has not yet
432/// been read by an application.
433///
434/// # Parameters
435///
436/// * `fd` should be an open file descriptor associated with a terminal.
437/// * `queue_selector` should be one of:
438/// - `TCIFLUSH` to discard data received but not read.
439/// - `TCOFLUSH` to discard data written but not transmitted.
440/// - `TCIOFLUSH` to discard both data received but not read and data written but not
441/// transmitted.
442pub fn tcflush(fd: RawFd, queue_selector: c_int) -> io::Result<()> {
443 io_result(unsafe { ffi::tcflush(fd, queue_selector) })
444}
445
446/// Populates a `Termios` structure with parameters associated with a terminal.
447///
448/// Upon successful completion, the `Termios` structure referred to by the `termios` parameter will
449/// contain the parameters associated with the terminal device referred to by `fd`.
450///
451/// # Parameters
452///
453/// * `fd` should be an open file descriptor associated with a terminal.
454/// * `termios` should be a mutable reference to the `Termios` structure that will hold the
455/// terminal device's parameters.
456pub fn tcgetattr(fd: RawFd, termios: &mut Termios) -> io::Result<()> {
457 io_result(unsafe { ffi::tcgetattr(fd, termios.inner_mut()) })
458}
459
460/// Sets a terminal device's parameters.
461///
462/// `tcsetattr()` returns successfully if it was able to perform any of the requested actions, even
463/// if other requested actions could not be performed. It will set all attributes that the
464/// implementation supports and leave others unchanged. The `Termios` structure will not be updated
465/// to reflect the changes that were applied.
466///
467/// In order to determine which parameters were applied to the terminal device, an application
468/// should use [`tcgetattr()`](fn.tcgetattr.html) to obtain the latest state of the terminal
469/// device. In particular, when attempting to change baud rates, [`tcgetattr()`](fn.tcgetattr.html)
470/// can be used to determine which baud rates were actually selected.
471///
472/// If none of the requested actions could be performed, then `tcsetattr()` returns an error.
473///
474/// # Parameters
475///
476/// * `fd` should be an open file descriptor associated with a terminal.
477/// * `action` should be one of the constants:
478/// - `TCSANOW` applies the change immediately.
479/// - `TCSADRAIN` applies the change after all output previously written to `fd` is transmitted.
480/// This mode should be used when changing parameters that affect output.
481/// - `TCSAFLUSH` applies the change after all output previously written to `fd` is transmitted.
482/// All data received but not read is discarded before applying the change.
483/// * `termios` should be a mutable reference to a `Termios` structure containing the parameters to
484/// apply to the terminal device.
485pub fn tcsetattr(fd: RawFd, action: c_int, termios: &Termios) -> io::Result<()> {
486 io_result(unsafe { ffi::tcsetattr(fd, action, termios.inner()) })
487}
488
489/// Returns the process group ID of the controlling terminal's session.
490///
491/// # Parameters
492///
493/// * `fd` should be an open file descriptor associated with a controlling terminal.
494pub fn tcgetsid(fd: RawFd) -> pid_t {
495 unsafe { ffi::tcgetsid(fd) }
496}
497
498/// Transmits data to generate a break condition.
499///
500/// If the terminal device is using asynchronous data transmission, `tcsendbreak()` transmits a
501/// continuous stream of zero bits for a specific duration.
502///
503/// # Parameters
504///
505/// * `fd` should be an open file descriptor associated with a terminal.
506/// * `duration` controls the duration of the transmitted zero bits. A value of 0 causes a
507/// transmission between 0.25 and 0.5 seconds. A value other than 0 causes a transmission for an
508/// implementation-defined period of time.
509pub fn tcsendbreak(fd: RawFd, duration: c_int) -> io::Result<()> {
510 io_result(unsafe { ffi::tcsendbreak(fd, duration) })
511}
512
513
514#[inline]
515fn io_result(result: c_int) -> io::Result<()> {
516 match result {
517 0 => Ok(()),
518 _ => Err(io::Error::last_os_error())
519 }
520}