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