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