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