1 #![allow(dead_code)]
2 
3 // Simple tests to ensure macro generated fns compile
4 ioctl_none_bad!(do_bad, 0x1234);
5 ioctl_read_bad!(do_bad_read, 0x1234, u16);
6 ioctl_write_int_bad!(do_bad_write_int, 0x1234);
7 ioctl_write_ptr_bad!(do_bad_write_ptr, 0x1234, u8);
8 ioctl_readwrite_bad!(do_bad_readwrite, 0x1234, u32);
9 ioctl_none!(do_none, 0, 0);
10 ioctl_read!(read_test, 0, 0, u32);
11 ioctl_write_int!(write_ptr_int, 0, 0);
12 ioctl_write_ptr!(write_ptr_u8, 0, 0, u8);
13 ioctl_write_ptr!(write_ptr_u32, 0, 0, u32);
14 ioctl_write_ptr!(write_ptr_u64, 0, 0, u64);
15 ioctl_readwrite!(readwrite_test, 0, 0, u64);
16 ioctl_read_buf!(readbuf_test, 0, 0, u32);
17 const SPI_IOC_MAGIC: u8 = b'k';
18 const SPI_IOC_MESSAGE: u8 = 0;
19 ioctl_write_buf!(writebuf_test_consts, SPI_IOC_MAGIC, SPI_IOC_MESSAGE, u8);
20 ioctl_write_buf!(writebuf_test_u8, 0, 0, u8);
21 ioctl_write_buf!(writebuf_test_u32, 0, 0, u32);
22 ioctl_write_buf!(writebuf_test_u64, 0, 0, u64);
23 ioctl_readwrite_buf!(readwritebuf_test, 0, 0, u32);
24 
25 // See C code for source of values for op calculations (does NOT work for mips/powerpc):
26 // https://gist.github.com/posborne/83ea6880770a1aef332e
27 //
28 // TODO:  Need a way to compute these constants at test time.  Using precomputed
29 // values is fragile and needs to be maintained.
30 
31 #[cfg(linux_android)]
32 mod linux {
33     // The cast is not unnecessary on all platforms.
34     #[allow(clippy::unnecessary_cast)]
35     #[test]
test_op_none()36     fn test_op_none() {
37         if cfg!(any(
38             target_arch = "mips",
39             target_arch = "mips32r6",
40             target_arch = "mips64",
41             target_arch = "mips64r6",
42             target_arch = "powerpc",
43             target_arch = "powerpc64"
44         )) {
45             assert_eq!(request_code_none!(b'q', 10) as u32, 0x2000_710A);
46             assert_eq!(request_code_none!(b'a', 255) as u32, 0x2000_61FF);
47         } else {
48             assert_eq!(request_code_none!(b'q', 10) as u32, 0x0000_710A);
49             assert_eq!(request_code_none!(b'a', 255) as u32, 0x0000_61FF);
50         }
51     }
52 
53     // The cast is not unnecessary on all platforms.
54     #[allow(clippy::unnecessary_cast)]
55     #[test]
test_op_write()56     fn test_op_write() {
57         if cfg!(any(
58             target_arch = "mips",
59             target_arch = "mips32r6",
60             target_arch = "mips64",
61             target_arch = "mips64r6",
62             target_arch = "powerpc",
63             target_arch = "powerpc64"
64         )) {
65             assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x8001_7A0A);
66             assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x8200_7A0A);
67         } else {
68             assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x4001_7A0A);
69             assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x4200_7A0A);
70         }
71     }
72 
73     #[cfg(target_pointer_width = "64")]
74     #[test]
test_op_write_64()75     fn test_op_write_64() {
76         if cfg!(any(
77             target_arch = "mips64",
78             target_arch = "mips64r6",
79             target_arch = "powerpc64"
80         )) {
81             assert_eq!(
82                 request_code_write!(b'z', 10, 1u64 << 32) as u32,
83                 0x8000_7A0A
84             );
85         } else {
86             assert_eq!(
87                 request_code_write!(b'z', 10, 1u64 << 32) as u32,
88                 0x4000_7A0A
89             );
90         }
91     }
92 
93     // The cast is not unnecessary on all platforms.
94     #[allow(clippy::unnecessary_cast)]
95     #[test]
test_op_read()96     fn test_op_read() {
97         if cfg!(any(
98             target_arch = "mips",
99             target_arch = "mips32r6",
100             target_arch = "mips64",
101             target_arch = "mips64r6",
102             target_arch = "powerpc",
103             target_arch = "powerpc64"
104         )) {
105             assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x4001_7A0A);
106             assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x4200_7A0A);
107         } else {
108             assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x8001_7A0A);
109             assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x8200_7A0A);
110         }
111     }
112 
113     #[cfg(target_pointer_width = "64")]
114     #[test]
test_op_read_64()115     fn test_op_read_64() {
116         if cfg!(any(
117             target_arch = "mips64",
118             target_arch = "mips64r6",
119             target_arch = "powerpc64"
120         )) {
121             assert_eq!(
122                 request_code_read!(b'z', 10, 1u64 << 32) as u32,
123                 0x4000_7A0A
124             );
125         } else {
126             assert_eq!(
127                 request_code_read!(b'z', 10, 1u64 << 32) as u32,
128                 0x8000_7A0A
129             );
130         }
131     }
132 
133     // The cast is not unnecessary on all platforms.
134     #[allow(clippy::unnecessary_cast)]
135     #[test]
test_op_read_write()136     fn test_op_read_write() {
137         assert_eq!(request_code_readwrite!(b'z', 10, 1) as u32, 0xC001_7A0A);
138         assert_eq!(request_code_readwrite!(b'z', 10, 512) as u32, 0xC200_7A0A);
139     }
140 
141     #[cfg(target_pointer_width = "64")]
142     #[test]
test_op_read_write_64()143     fn test_op_read_write_64() {
144         assert_eq!(
145             request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
146             0xC000_7A0A
147         );
148     }
149 }
150 
151 #[cfg(bsd)]
152 mod bsd {
153     #[test]
test_op_none()154     fn test_op_none() {
155         assert_eq!(request_code_none!(b'q', 10), 0x2000_710A);
156         assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF);
157     }
158 
159     #[cfg(freebsdlike)]
160     #[test]
test_op_write_int()161     fn test_op_write_int() {
162         assert_eq!(request_code_write_int!(b'v', 4), 0x2004_7604);
163         assert_eq!(request_code_write_int!(b'p', 2), 0x2004_7002);
164     }
165 
166     #[test]
test_op_write()167     fn test_op_write() {
168         assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A);
169         assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A);
170     }
171 
172     #[cfg(target_pointer_width = "64")]
173     #[test]
test_op_write_64()174     fn test_op_write_64() {
175         assert_eq!(request_code_write!(b'z', 10, 1u64 << 32), 0x8000_7A0A);
176     }
177 
178     #[test]
test_op_read()179     fn test_op_read() {
180         assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A);
181         assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A);
182     }
183 
184     #[cfg(target_pointer_width = "64")]
185     #[test]
test_op_read_64()186     fn test_op_read_64() {
187         assert_eq!(request_code_read!(b'z', 10, 1u64 << 32), 0x4000_7A0A);
188     }
189 
190     #[test]
test_op_read_write()191     fn test_op_read_write() {
192         assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A);
193         assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A);
194     }
195 
196     #[cfg(target_pointer_width = "64")]
197     #[test]
test_op_read_write_64()198     fn test_op_read_write_64() {
199         assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32), 0xC000_7A0A);
200     }
201 }
202 
203 #[cfg(linux_android)]
204 mod linux_ioctls {
205     use std::mem;
206     use std::os::unix::io::AsRawFd;
207 
208     use libc::{termios, TCGETS, TCSBRK, TCSETS, TIOCNXCL};
209     use tempfile::tempfile;
210 
211     use nix::errno::Errno;
212 
213     ioctl_none_bad!(tiocnxcl, TIOCNXCL);
214     #[test]
test_ioctl_none_bad()215     fn test_ioctl_none_bad() {
216         let file = tempfile().unwrap();
217         let res = unsafe { tiocnxcl(file.as_raw_fd()) };
218         assert_eq!(res, Err(Errno::ENOTTY));
219     }
220 
221     ioctl_read_bad!(tcgets, TCGETS, termios);
222     #[test]
test_ioctl_read_bad()223     fn test_ioctl_read_bad() {
224         let file = tempfile().unwrap();
225         let mut termios = unsafe { mem::zeroed() };
226         let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) };
227         assert_eq!(res, Err(Errno::ENOTTY));
228     }
229 
230     ioctl_write_int_bad!(tcsbrk, TCSBRK);
231     #[test]
test_ioctl_write_int_bad()232     fn test_ioctl_write_int_bad() {
233         let file = tempfile().unwrap();
234         let res = unsafe { tcsbrk(file.as_raw_fd(), 0) };
235         assert_eq!(res, Err(Errno::ENOTTY));
236     }
237 
238     ioctl_write_ptr_bad!(tcsets, TCSETS, termios);
239     #[test]
test_ioctl_write_ptr_bad()240     fn test_ioctl_write_ptr_bad() {
241         let file = tempfile().unwrap();
242         let termios: termios = unsafe { mem::zeroed() };
243         let res = unsafe { tcsets(file.as_raw_fd(), &termios) };
244         assert_eq!(res, Err(Errno::ENOTTY));
245     }
246 
247     // FIXME: Find a suitable example for `ioctl_readwrite_bad`
248 
249     // From linux/videodev2.h
250     ioctl_none!(log_status, b'V', 70);
251     #[test]
test_ioctl_none()252     fn test_ioctl_none() {
253         let file = tempfile().unwrap();
254         let res = unsafe { log_status(file.as_raw_fd()) };
255         assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
256     }
257 
258     #[repr(C)]
259     pub struct v4l2_audio {
260         index: u32,
261         name: [u8; 32],
262         capability: u32,
263         mode: u32,
264         reserved: [u32; 2],
265     }
266 
267     // From linux/videodev2.h
268     ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
269     #[test]
test_ioctl_write_ptr()270     fn test_ioctl_write_ptr() {
271         let file = tempfile().unwrap();
272         let data: v4l2_audio = unsafe { mem::zeroed() };
273         let res = unsafe { s_audio(file.as_raw_fd(), &data) };
274         assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
275     }
276 
277     // From linux/net/bluetooth/hci_sock.h
278     const HCI_IOC_MAGIC: u8 = b'H';
279     const HCI_IOC_HCIDEVUP: u8 = 201;
280     ioctl_write_int!(hcidevup, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
281     #[test]
test_ioctl_write_int()282     fn test_ioctl_write_int() {
283         let file = tempfile().unwrap();
284         let res = unsafe { hcidevup(file.as_raw_fd(), 0) };
285         assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
286     }
287 
288     // From linux/videodev2.h
289     ioctl_read!(g_audio, b'V', 33, v4l2_audio);
290     #[test]
test_ioctl_read()291     fn test_ioctl_read() {
292         let file = tempfile().unwrap();
293         let mut data: v4l2_audio = unsafe { mem::zeroed() };
294         let res = unsafe { g_audio(file.as_raw_fd(), &mut data) };
295         assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
296     }
297 
298     // From linux/videodev2.h
299     ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
300     #[test]
test_ioctl_readwrite()301     fn test_ioctl_readwrite() {
302         let file = tempfile().unwrap();
303         let mut data: v4l2_audio = unsafe { mem::zeroed() };
304         let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) };
305         assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
306     }
307 
308     // FIXME: Find a suitable example for `ioctl_read_buf`.
309 
310     #[repr(C)]
311     pub struct spi_ioc_transfer {
312         tx_buf: u64,
313         rx_buf: u64,
314         len: u32,
315         speed_hz: u32,
316         delay_usecs: u16,
317         bits_per_word: u8,
318         cs_change: u8,
319         tx_nbits: u8,
320         rx_nbits: u8,
321         pad: u16,
322     }
323 
324     // From linux/spi/spidev.h
325     ioctl_write_buf!(
326         spi_ioc_message,
327         super::SPI_IOC_MAGIC,
328         super::SPI_IOC_MESSAGE,
329         spi_ioc_transfer
330     );
331     #[test]
test_ioctl_write_buf()332     fn test_ioctl_write_buf() {
333         let file = tempfile().unwrap();
334         let data: [spi_ioc_transfer; 4] = unsafe { mem::zeroed() };
335         let res = unsafe { spi_ioc_message(file.as_raw_fd(), &data[..]) };
336         assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
337     }
338 
339     // FIXME: Find a suitable example for `ioctl_readwrite_buf`.
340 }
341 
342 #[cfg(target_os = "freebsd")]
343 mod freebsd_ioctls {
344     use std::mem;
345     use std::os::unix::io::AsRawFd;
346 
347     use libc::termios;
348     use tempfile::tempfile;
349 
350     use nix::errno::Errno;
351 
352     // From sys/sys/ttycom.h
353     const TTY_IOC_MAGIC: u8 = b't';
354     const TTY_IOC_TYPE_NXCL: u8 = 14;
355     const TTY_IOC_TYPE_GETA: u8 = 19;
356     const TTY_IOC_TYPE_SETA: u8 = 20;
357 
358     ioctl_none!(tiocnxcl, TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL);
359     #[test]
test_ioctl_none()360     fn test_ioctl_none() {
361         let file = tempfile().unwrap();
362         let res = unsafe { tiocnxcl(file.as_raw_fd()) };
363         assert_eq!(res, Err(Errno::ENOTTY));
364     }
365 
366     ioctl_read!(tiocgeta, TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA, termios);
367     #[test]
test_ioctl_read()368     fn test_ioctl_read() {
369         let file = tempfile().unwrap();
370         let mut termios = unsafe { mem::zeroed() };
371         let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) };
372         assert_eq!(res, Err(Errno::ENOTTY));
373     }
374 
375     ioctl_write_ptr!(tiocseta, TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA, termios);
376     #[test]
test_ioctl_write_ptr()377     fn test_ioctl_write_ptr() {
378         let file = tempfile().unwrap();
379         let termios: termios = unsafe { mem::zeroed() };
380         let res = unsafe { tiocseta(file.as_raw_fd(), &termios) };
381         assert_eq!(res, Err(Errno::ENOTTY));
382     }
383 }
384