• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // A set of helper functions for working with the `termios` and `termios2` structs
2 use cfg_if::cfg_if;
3 
4 use crate::{DataBits, FlowControl, Parity, Result, StopBits};
5 use nix::libc;
6 
7 use std::os::unix::prelude::*;
8 
9 cfg_if! {
10     if #[cfg(any(
11         target_os = "dragonfly",
12         target_os = "freebsd",
13         target_os = "ios",
14         target_os = "macos",
15         target_os = "netbsd",
16         target_os = "openbsd",
17         all(
18             target_os = "linux",
19             any(
20                 target_env = "musl",
21                 target_arch = "powerpc",
22                 target_arch = "powerpc64"
23             )
24         )
25     ))] {
26         pub(crate) type Termios = libc::termios;
27     } else if #[cfg(any(
28         target_os = "android",
29         all(
30             target_os = "linux",
31             not(any(
32                 target_env = "musl",
33                 target_arch = "powerpc",
34                 target_arch = "powerpc64"
35             ))
36         )
37     ))] {
38         pub(crate) type Termios = libc::termios2;
39     } else {
40         compile_error!("Unsupported platform. See crate documentation for supported platforms");
41     }
42 }
43 
44 // The termios struct isn't used for storing the baud rate, but it can be affected by other
45 // calls in this lib to the IOSSIOSPEED ioctl. So whenever we get this struct, make sure to
46 // reset the input & output baud rates to a safe default. This is accounted for by the
47 // corresponding set_termios that is mac-specific and always calls IOSSIOSPEED.
48 #[cfg(any(target_os = "ios", target_os = "macos",))]
get_termios(fd: RawFd) -> Result<Termios>49 pub(crate) fn get_termios(fd: RawFd) -> Result<Termios> {
50     use std::mem::MaybeUninit;
51 
52     let mut termios = MaybeUninit::uninit();
53     let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
54     nix::errno::Errno::result(res)?;
55     let mut termios = unsafe { termios.assume_init() };
56     termios.c_ispeed = self::libc::B9600;
57     termios.c_ospeed = self::libc::B9600;
58     Ok(termios)
59 }
60 
61 #[cfg(any(
62     target_os = "dragonfly",
63     target_os = "freebsd",
64     target_os = "netbsd",
65     target_os = "openbsd",
66     all(
67         target_os = "linux",
68         any(
69             target_env = "musl",
70             target_arch = "powerpc",
71             target_arch = "powerpc64"
72         )
73     )
74 ))]
get_termios(fd: RawFd) -> Result<Termios>75 pub(crate) fn get_termios(fd: RawFd) -> Result<Termios> {
76     use std::mem::MaybeUninit;
77 
78     let mut termios = MaybeUninit::uninit();
79     let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
80     nix::errno::Errno::result(res)?;
81     unsafe { Ok(termios.assume_init()) }
82 }
83 
84 #[cfg(any(
85     target_os = "android",
86     all(
87         target_os = "linux",
88         not(any(
89             target_env = "musl",
90             target_arch = "powerpc",
91             target_arch = "powerpc64"
92         ))
93     )
94 ))]
get_termios(fd: RawFd) -> Result<Termios>95 pub(crate) fn get_termios(fd: RawFd) -> Result<Termios> {
96     crate::posix::ioctl::tcgets2(fd)
97 }
98 
99 #[cfg(any(target_os = "ios", target_os = "macos",))]
set_termios(fd: RawFd, termios: &libc::termios, baud_rate: u32) -> Result<()>100 pub(crate) fn set_termios(fd: RawFd, termios: &libc::termios, baud_rate: u32) -> Result<()> {
101     let res = unsafe { libc::tcsetattr(fd, libc::TCSANOW, termios) };
102     nix::errno::Errno::result(res)?;
103 
104     // Note: attempting to set the baud rate on a pseudo terminal via this ioctl call will fail
105     // with the `ENOTTY` error.
106     if baud_rate > 0 {
107         crate::posix::ioctl::iossiospeed(fd, &(baud_rate as libc::speed_t))?;
108     }
109 
110     Ok(())
111 }
112 
113 #[cfg(any(
114     target_os = "dragonfly",
115     target_os = "freebsd",
116     target_os = "netbsd",
117     target_os = "openbsd",
118     all(
119         target_os = "linux",
120         any(
121             target_env = "musl",
122             target_arch = "powerpc",
123             target_arch = "powerpc64"
124         )
125     )
126 ))]
set_termios(fd: RawFd, termios: &libc::termios) -> Result<()>127 pub(crate) fn set_termios(fd: RawFd, termios: &libc::termios) -> Result<()> {
128     let res = unsafe { libc::tcsetattr(fd, libc::TCSANOW, termios) };
129     nix::errno::Errno::result(res)?;
130     Ok(())
131 }
132 
133 #[cfg(any(
134     target_os = "android",
135     all(
136         target_os = "linux",
137         not(any(
138             target_env = "musl",
139             target_arch = "powerpc",
140             target_arch = "powerpc64"
141         ))
142     )
143 ))]
set_termios(fd: RawFd, termios: &Termios) -> Result<()>144 pub(crate) fn set_termios(fd: RawFd, termios: &Termios) -> Result<()> {
145     crate::posix::ioctl::tcsets2(fd, termios)
146 }
147 
set_parity(termios: &mut Termios, parity: Parity)148 pub(crate) fn set_parity(termios: &mut Termios, parity: Parity) {
149     match parity {
150         Parity::None => {
151             termios.c_cflag &= !(libc::PARENB | libc::PARODD);
152             termios.c_iflag &= !libc::INPCK;
153             termios.c_iflag |= libc::IGNPAR;
154         }
155         Parity::Odd => {
156             termios.c_cflag |= libc::PARENB | libc::PARODD;
157             termios.c_iflag |= libc::INPCK;
158             termios.c_iflag &= !libc::IGNPAR;
159         }
160         Parity::Even => {
161             termios.c_cflag &= !libc::PARODD;
162             termios.c_cflag |= libc::PARENB;
163             termios.c_iflag |= libc::INPCK;
164             termios.c_iflag &= !libc::IGNPAR;
165         }
166     };
167 }
168 
set_flow_control(termios: &mut Termios, flow_control: FlowControl)169 pub(crate) fn set_flow_control(termios: &mut Termios, flow_control: FlowControl) {
170     match flow_control {
171         FlowControl::None => {
172             termios.c_iflag &= !(libc::IXON | libc::IXOFF);
173             termios.c_cflag &= !libc::CRTSCTS;
174         }
175         FlowControl::Software => {
176             termios.c_iflag |= libc::IXON | libc::IXOFF;
177             termios.c_cflag &= !libc::CRTSCTS;
178         }
179         FlowControl::Hardware => {
180             termios.c_iflag &= !(libc::IXON | libc::IXOFF);
181             termios.c_cflag |= libc::CRTSCTS;
182         }
183     };
184 }
185 
set_data_bits(termios: &mut Termios, data_bits: DataBits)186 pub(crate) fn set_data_bits(termios: &mut Termios, data_bits: DataBits) {
187     let size = match data_bits {
188         DataBits::Five => libc::CS5,
189         DataBits::Six => libc::CS6,
190         DataBits::Seven => libc::CS7,
191         DataBits::Eight => libc::CS8,
192     };
193 
194     termios.c_cflag &= !libc::CSIZE;
195     termios.c_cflag |= size;
196 }
197 
set_stop_bits(termios: &mut Termios, stop_bits: StopBits)198 pub(crate) fn set_stop_bits(termios: &mut Termios, stop_bits: StopBits) {
199     match stop_bits {
200         StopBits::One => termios.c_cflag &= !libc::CSTOPB,
201         StopBits::Two => termios.c_cflag |= libc::CSTOPB,
202     };
203 }
204 
205 #[cfg(any(
206     target_os = "android",
207     all(
208         target_os = "linux",
209         not(any(
210             target_env = "musl",
211             target_arch = "powerpc",
212             target_arch = "powerpc64"
213         ))
214     )
215 ))]
set_baud_rate(termios: &mut Termios, baud_rate: u32) -> Result<()>216 pub(crate) fn set_baud_rate(termios: &mut Termios, baud_rate: u32) -> Result<()> {
217     termios.c_cflag &= !nix::libc::CBAUD;
218     termios.c_cflag |= nix::libc::BOTHER;
219     termios.c_ispeed = baud_rate;
220     termios.c_ospeed = baud_rate;
221     Ok(())
222 }
223 
224 // BSDs use the baud rate as the constant value so there's no translation necessary
225 #[cfg(any(
226     target_os = "dragonfly",
227     target_os = "freebsd",
228     target_os = "netbsd",
229     target_os = "openbsd"
230 ))]
set_baud_rate(termios: &mut Termios, baud_rate: u32) -> Result<()>231 pub(crate) fn set_baud_rate(termios: &mut Termios, baud_rate: u32) -> Result<()> {
232     let res = unsafe { libc::cfsetspeed(termios, baud_rate.into()) };
233     nix::errno::Errno::result(res)?;
234     Ok(())
235 }
236 
237 #[cfg(all(
238     target_os = "linux",
239     any(
240         target_env = "musl",
241         target_arch = "powerpc",
242         target_arch = "powerpc64"
243     )
244 ))]
set_baud_rate(termios: &mut Termios, baud_rate: u32) -> Result<()>245 pub(crate) fn set_baud_rate(termios: &mut Termios, baud_rate: u32) -> Result<()> {
246     use crate::{Error, ErrorKind};
247 
248     use self::libc::{
249         B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000, B460800,
250         B500000, B576000, B921600,
251     };
252     use self::libc::{
253         B110, B115200, B1200, B134, B150, B1800, B19200, B200, B230400, B2400, B300, B38400, B4800,
254         B50, B57600, B600, B75, B9600,
255     };
256 
257     let baud_rate = match baud_rate {
258         50 => B50,
259         75 => B75,
260         110 => B110,
261         134 => B134,
262         150 => B150,
263         200 => B200,
264         300 => B300,
265         600 => B600,
266         1200 => B1200,
267         1800 => B1800,
268         2400 => B2400,
269         4800 => B4800,
270         9600 => B9600,
271         19_200 => B19200,
272         38_400 => B38400,
273         57_600 => B57600,
274         115_200 => B115200,
275         230_400 => B230400,
276         460_800 => B460800,
277         500_000 => B500000,
278         576_000 => B576000,
279         921_600 => B921600,
280         1_000_000 => B1000000,
281         1_152_000 => B1152000,
282         1_500_000 => B1500000,
283         2_000_000 => B2000000,
284         2_500_000 => B2500000,
285         3_000_000 => B3000000,
286         3_500_000 => B3500000,
287         4_000_000 => B4000000,
288         _ => return Err(Error::new(ErrorKind::InvalidInput, "Unsupported baud rate")),
289     };
290     let res = unsafe { libc::cfsetspeed(termios, baud_rate) };
291     nix::errno::Errno::result(res)?;
292     Ok(())
293 }
294