diff --git a/CHANGELOG.md b/CHANGELOG.md index 350fc117c2..817a89f45e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,11 +24,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). and nix::Error::UnsupportedOperation}` ([#614](https://github.com/nix-rust/nix/pull/614)) - Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527)) +- Added "bad none", "bad write_ptr", "bad write_int", and "bad readwrite" variants to the `ioctl!` + macro. ([#670](https://github.com/nix-rust/nix/pull/670)) ### Changed -- Changed `ioctl!(write ...)` to take argument by value instead as pointer. - If you need a pointer as argument, use `ioctl!(write buf ...)`. - ([#626](https://github.com/nix-rust/nix/pull/626)) +- Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants + to more clearly separate those use cases. ([#670](https://github.com/nix-rust/nix/pull/670)) - Marked `sys::mman::{ mmap, munmap, madvise, munlock, msync }` as unsafe. ([#559](https://github.com/nix-rust/nix/pull/559)) - Minimum supported Rust version is now 1.13. @@ -48,6 +49,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Revised the termios API including additional tests and documentation and exposed it on iOS. ([#527](https://github.com/nix-rust/nix/pull/527)) - `eventfd`, `signalfd`, and `pwritev`/`preadv` functionality is now included by default for all supported platforms. ([#681](https://github.com/nix-rust/nix/pull/561)) +- The `ioctl!` macro's plain variants has been replaced with "bad read" to be consistent with + other variants. The generated functions also have more strict types for their arguments. The + "*_buf" variants also now calculate total array size and take slice references for improved type + safety. The documentation has also been dramatically improved. + ([#670](https://github.com/nix-rust/nix/pull/670)) ### Removed - Removed `io::Error` from `nix::Error` and the conversion from `nix::Error` to `Errno` @@ -55,6 +61,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). - All feature flags have been removed in favor of conditional compilation on supported platforms. `execvpe` is no longer supported, but this was already broken and will be added back in the next release. ([#681](https://github.com/nix-rust/nix/pull/561)) +- Removed `ioc_*` functions and many helper constants and macros within the `ioctl` module. These + should always have been private and only the `ioctl!` should be used in public code. + ([#670](https://github.com/nix-rust/nix/pull/670)) ### Fixed - Fixed multiple issues compiling under different archetectures and OSes. @@ -73,6 +82,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#623](https://github.com/nix-rust/nix/pull/623)) - Multiple constants related to the termios API have now been properly defined for all supported platforms. ([#527](https://github.com/nix-rust/nix/pull/527)) +- `ioctl!` macro now supports working with non-int datatypes and properly supports all platforms. + ([#670](https://github.com/nix-rust/nix/pull/670)) ## [0.8.1] 2017-04-16 diff --git a/src/lib.rs b/src/lib.rs index f0cdb581ee..b53510ba6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,6 @@ pub mod libc { pub use self::libc::*; } -pub use libc::{c_int, c_void}; pub use errno::Errno; pub mod errno; @@ -96,7 +95,7 @@ pub enum Error { /// The operation involved a conversion to Rust's native String type, which failed because the /// string did not contain all valid UTF-8. InvalidUtf8, - /// The operation is not supported by Nix, in this instance either use the libc bindings or + /// The operation is not supported by Nix, in this instance either use the libc bindings or /// consult the module documentation to see if there is a more appropriate interface available. UnsupportedOperation, } diff --git a/src/sys/aio.rs b/src/sys/aio.rs index 64aee7ca36..abb742f3bd 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -94,7 +94,7 @@ impl<'a> AioCb<'a> { /// be prioritized at the process's priority level minus `prio` /// * `sigev_notify` Determines how you will be notified of event /// completion. - pub fn from_fd(fd: RawFd, prio: ::c_int, + pub fn from_fd(fd: RawFd, prio: libc::c_int, sigev_notify: SigevNotify) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = 0; @@ -118,13 +118,13 @@ impl<'a> AioCb<'a> { /// * `opcode` This field is only used for `lio_listio`. It determines /// which operation to use for this individual aiocb pub fn from_mut_slice(fd: RawFd, offs: off_t, buf: &'a mut [u8], - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; a.aio_nbytes = buf.len() as size_t; a.aio_buf = buf.as_ptr() as *mut c_void; - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb { aiocb: a, mutable: true, in_progress: false, keeper: Keeper::phantom(PhantomData)}; @@ -146,13 +146,13 @@ impl<'a> AioCb<'a> { /// * `opcode` This field is only used for `lio_listio`. It determines /// which operation to use for this individual aiocb pub fn from_boxed_slice(fd: RawFd, offs: off_t, buf: Rc>, - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb<'a> { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; a.aio_nbytes = buf.len() as size_t; a.aio_buf = buf.as_ptr() as *mut c_void; - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb{ aiocb: a, mutable: true, in_progress: false, keeper: Keeper::boxed(buf)}; @@ -173,7 +173,7 @@ impl<'a> AioCb<'a> { // AioCb, and they must all be the same type. We're basically stuck with // using an unsafe function, since aio (as designed in C) is an unsafe API. pub fn from_slice(fd: RawFd, offs: off_t, buf: &'a [u8], - prio: ::c_int, sigev_notify: SigevNotify, + prio: libc::c_int, sigev_notify: SigevNotify, opcode: LioOpcode) -> AioCb { let mut a = AioCb::common_init(fd, prio, sigev_notify); a.aio_offset = offs; @@ -183,14 +183,14 @@ impl<'a> AioCb<'a> { // it. a.aio_buf = buf.as_ptr() as *mut c_void; assert!(opcode != LioOpcode::LIO_READ, "Can't read into an immutable buffer"); - a.aio_lio_opcode = opcode as ::c_int; + a.aio_lio_opcode = opcode as libc::c_int; let aiocb = AioCb { aiocb: a, mutable: false, in_progress: false, keeper: Keeper::none}; aiocb } - fn common_init(fd: RawFd, prio: ::c_int, + fn common_init(fd: RawFd, prio: libc::c_int, sigev_notify: SigevNotify) -> libc::aiocb { // Use mem::zeroed instead of explicitly zeroing each field, because the // number and name of reserved fields is OS-dependent. On some OSes, @@ -235,7 +235,7 @@ impl<'a> AioCb<'a> { pub fn fsync(&mut self, mode: AioFsyncMode) -> Result<()> { let p: *mut libc::aiocb = &mut self.aiocb; self.in_progress = true; - Errno::result(unsafe { libc::aio_fsync(mode as ::c_int, p) }).map(drop) + Errno::result(unsafe { libc::aio_fsync(mode as libc::c_int, p) }).map(drop) } /// Asynchronously reads from a file descriptor into a buffer diff --git a/src/sys/event.rs b/src/sys/event.rs index 494c12dd47..95b7619eca 100644 --- a/src/sys/event.rs +++ b/src/sys/event.rs @@ -21,7 +21,7 @@ pub struct KEvent { #[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios"))] -type type_of_udata = *mut ::c_void; +type type_of_udata = *mut libc::c_void; #[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios"))] @@ -127,7 +127,7 @@ libc_bitflags!( #[cfg(any(target_os = "macos", target_os = "ios"))] NOTE_EXITSTATUS, NOTE_EXTEND, - #[cfg(any(target_os = "macos", + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 48b3827bb8..4db3fe760b 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -1,102 +1,202 @@ -//! Provide helpers for making ioctl system calls -//! -//! Currently supports Linux on all architectures. Other platforms welcome! +//! Provide helpers for making ioctl system calls. //! //! This library is pretty low-level and messy. `ioctl` is not fun. //! //! What is an `ioctl`? //! =================== //! -//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want -//! to add a new syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, -//! and the commands that can be send with it. `ioctl` stands for "IO control", -//! and the commands are always sent to a file descriptor. +//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new +//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be +//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file +//! descriptor. //! //! It is common to see `ioctl`s used for the following purposes: //! -//! * Provide read/write access to out-of-band data related -//! to a device such as configuration (for instance, setting -//! serial port options) -//! * Provide a mechanism for performing full-duplex data -//! transfers (for instance, xfer on SPI devices). -//! * Provide access to control functions on a device (for example, -//! on Linux you can send commands like pause, resume, and eject -//! to the CDROM device. -//! * Do whatever else the device driver creator thought made most sense. +//! * Provide read/write access to out-of-band data related to a device such as configuration +//! (for instance, setting serial port options) +//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI +//! devices). +//! * Provide access to control functions on a device (for example, on Linux you can send +//! commands like pause, resume, and eject to the CDROM device. +//! * Do whatever else the device driver creator thought made most sense. +//! +//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard. +//! They operate on file descriptors and have an identifier that specifies what the ioctl is. +//! Additionally they may read or write data and therefore need to pass along a data pointer. +//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also +//! be difficult. +//! +//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some +//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into +//! subcomponents (For linux this is documented in +//! [`Documentation/ioctl/ioctl-number.txt`](http://elixir.free-electrons.com/linux/latest/source/Documentation/ioctl/ioctl-number.txt)): +//! +//! * Number: The actual ioctl ID +//! * Type: A grouping of ioctls for a common purpose or driver +//! * Size: The size in bytes of the data that will be transferred +//! * Direction: Whether there is any data and if it's read, write, or both //! -//! `ioctl`s are synchronous system calls and are similar to read and -//! write calls in that regard. +//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead +//! preferring to use the 4 components above to generate the final ioctl identifier. Because of +//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are +//! commonly referred to as "bad" in `ioctl` documentation. //! -//! What does this module support? -//! =============================== +//! Defining ioctls +//! =============== //! -//! This library provides the `ioctl!` macro, for binding `ioctl`s. -//! Here's a few examples of how that can work for SPI under Linux -//! from [rust-spidev](https://github.com/posborne/rust-spidev). +//! This library provides the `ioctl!` macro, for binding `ioctl`s. This macro generates public +//! unsafe functions that can then be used for calling the ioctl. This macro has a few different +//! ways it can be used depending on the specific ioctl you're working with. +//! +//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This +//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in +//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR` +//! macro, we know it's a `read` ioctl and can use the `ioctl!` macro as follows: //! //! ``` -//! #[allow(non_camel_case_types)] -//! pub struct spi_ioc_transfer { -//! pub tx_buf: u64, -//! pub rx_buf: u64, -//! pub len: u32, -//! -//! // optional overrides -//! pub speed_hz: u32, -//! pub delay_usecs: u16, -//! pub bits_per_word: u8, -//! pub cs_change: u8, -//! pub pad: u32, -//! } +//! # #[macro_use] extern crate nix; +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MODE: u8 = 1; +//! ioctl!(read spi_read_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE; u8); +//! # fn main() {} +//! ``` //! -//! #[cfg(linux)] -//! mod ioctl { -//! use super::*; -//! -//! const SPI_IOC_MAGIC: u8 = 'k' as u8; -//! const SPI_IOC_NR_TRANSFER: u8 = 0; -//! const SPI_IOC_NR_MODE: u8 = 1; -//! const SPI_IOC_NR_LSB_FIRST: u8 = 2; -//! const SPI_IOC_NR_BITS_PER_WORD: u8 = 3; -//! const SPI_IOC_NR_MAX_SPEED_HZ: u8 = 4; -//! const SPI_IOC_NR_MODE32: u8 = 5; -//! -//! ioctl!(read get_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8); -//! ioctl!(read get_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u32); -//! ioctl!(write set_mode_u8 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE; u8); -//! ioctl!(write set_mode_u32 with SPI_IOC_MAGIC, SPI_IOC_NR_MODE32; u32); -//! ioctl!(read get_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8); -//! ioctl!(write set_lsb_first with SPI_IOC_MAGIC, SPI_IOC_NR_LSB_FIRST; u8); -//! ioctl!(read get_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8); -//! ioctl!(write set_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_NR_BITS_PER_WORD; u8); -//! ioctl!(read get_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32); -//! ioctl!(write set_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_NR_MAX_SPEED_HZ; u32); -//! ioctl!(write spidev_transfer with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer); -//! ioctl!(write buf spidev_transfer_buf with SPI_IOC_MAGIC, SPI_IOC_NR_TRANSFER; spi_ioc_transfer); +//! This generates the function: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{Errno, libc, Result}; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! # const SPI_IOC_TYPE_MODE: u8 = 1; +//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result { +//! let res = libc::ioctl(fd, ior!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::()), data); +//! Errno::result(res) //! } +//! # fn main() {} +//! ``` +//! +//! The return value for `ioctl` functions generated by the `ioctl!` macro are `nix::Error`s. +//! These are generated by assuming the return value of the ioctl is `-1` on error and everything +//! else is a valid return value. If this is not the case, `Result::map` can be used to map some +//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function. //! -//! // doctest workaround -//! fn main() {} +//! Writing `ioctl`s generally use pointers as their data source and these should use the +//! `write_ptr` variant. But in some cases an `int` is passed directly. For these `ioctl`s use the +//! `write_int` variant of the `ioctl!` macro. This variant does not take a type as the last argument: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! const HCI_IOC_MAGIC: u8 = b'k'; +//! const HCI_IOC_HCIDEVUP: u8 = 1; +//! ioctl!(write_int hci_dev_up with HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); +//! # fn main() {} //! ``` //! -//! Spidev uses the `_IOC` macros that are encouraged (as far as -//! `ioctl` can be encouraged at all) for newer drivers. Many -//! drivers, however, just use magic numbers with no attached -//! semantics. For those, the `ioctl!(bad ...)` variant should be -//! used (the "bad" terminology is from the Linux kernel). +//! Some `ioctl`s don't transfer any data, and those should use the `none` variant. This variant +//! doesn't take a type and so it is declared similar to the `write_int` variant shown above. //! -//! How do I get the magic numbers? -//! =============================== +//! The mode for a given `ioctl` should be clear from the documentation if it has good +//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl` +//! number where `_IO`, `_IOR`, `_IOW`, and `_IORW` map to "none", "read", "write_*", and "readwrite" +//! respectively. To determine the specific `write_` variant to use you'll need to find +//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used, +//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the +//! [`ioctl_list` man page](http://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a +//! large number of `ioctl`s and describes their argument data type. //! -//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot -//! of lines defining macros which use `_IOR`, `_IOW`, `_IOC`, and `_IORW`. These macros -//! correspond to the `ior!`, `iow!`, `ioc!`, and `iorw!` macros defined in this crate. -//! Additionally, there is the `ioctl!` macro for creating a wrapper around `ioctl` that is -//! somewhat more type-safe. +//! More examples on using `ioctl!` can be found in the [rust-spidev crate](https://github.com/rust-embedded/rust-spidev). +//! +//! Using hard-coded ioctl numbers +//! ------------------------------ +//! +//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of +//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `bad *` +//! variants of the `ioctl!` macro. This naming comes from the Linux kernel which refers to these +//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates +//! the ioctl number and instead use the defined value directly. +//! +//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor. +//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::TCGETS as TCGETS; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! # use nix::libc::termios as termios; +//! # #[cfg(any(target_os = "android", target_os = "linux"))] +//! ioctl!(bad read tcgets with TCGETS; termios); +//! # fn main() {} +//! ``` +//! +//! The generated function has the same form as that generated by `read`: +//! +//! ```text +//! pub unsafe fn tcgets(fd: c_int, data: *mut termios) -> Result; +//! ``` +//! +//! There is also a `bad none`, `bad write_int`/`bad write_ptr`, and `bad readwrite` variant that work +//! similar to the standard `none`, `write_int`/`write_ptr`, and `readwrite` variants. +//! +//! Working with arrays +//! -------------------- +//! +//! Some `ioctl`s work with entire arrays of elements. These are supported by the `*_buf` variants in +//! the `ioctl!` macro which can be used by specifying `read_buf`, `write_buf`, and +//! `readwrite_buf`. Note that there are no "bad" versions for working with buffers. The generated +//! functions include a `len` argument to specify the number of elements (where the type of each +//! element is specified in the macro). +//! +//! Again looking to the SPI `ioctl`s on Linux for an example, there is a `SPI_IOC_MESSAGE` `ioctl` +//! that queues up multiple SPI messages by writing an entire array of `spi_ioc_transfer` structs. +//! `linux/spi/spidev.h` defines a macro to calculate the `ioctl` number like: //! -//! Most `ioctl`s have no or little documentation. You'll need to scrounge through -//! the source to figure out what they do and how they should be used. +//! ```C +//! #define SPI_IOC_MAGIC 'k' +//! #define SPI_MSGSIZE(N) ... +//! #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)]) +//! ``` +//! +//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl!` macro, so all that's +//! needed to define this `ioctl` is: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h +//! const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; spi_ioc_transfer); +//! # fn main() {} +//! ``` +//! +//! This generates a function like: +//! +//! ``` +//! # #[macro_use] extern crate nix; +//! # use std::mem; +//! # use nix::{Errno, libc, Result}; +//! # use nix::libc::c_int as c_int; +//! # const SPI_IOC_MAGIC: u8 = b'k'; +//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0; +//! # pub struct spi_ioc_transfer(u64); +//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result { +//! let res = libc::ioctl(fd, +//! iow!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::()), +//! data); +//! Errno::result(res) +//! } +//! # fn main() {} +//! ``` +//! +//! Finding ioctl documentation +//! --------------------------- //! +//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot +//! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IORW`. Some `ioctl`s are +//! documented directly in the headers defining their constants, but others have more extensive +//! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`). #[cfg(any(target_os = "linux", target_os = "android"))] #[path = "platform/linux.rs"] #[macro_use] @@ -114,18 +214,9 @@ mod platform; pub use self::platform::*; -// liblibc has the wrong decl for linux :| hack until #26809 lands. -extern "C" { - #[doc(hidden)] - pub fn ioctl(fd: libc::c_int, req: libc::c_ulong, ...) -> libc::c_int; -} - -/// A hack to get the macros to work nicely. -#[doc(hidden)] -pub use ::libc as libc; - /// Convert raw ioctl return value to a Nix result #[macro_export] +#[doc(hidden)] macro_rules! convert_ioctl_res { ($w:expr) => ( { @@ -134,63 +225,96 @@ macro_rules! convert_ioctl_res { ); } +/// Generates ioctl functions. See [::sys::ioctl](sys/ioctl/index.html). #[macro_export] macro_rules! ioctl { - ($name:ident with $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, - data: *mut u8) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data)) + (bad none $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type)) + } + ); + (bad read $name:ident with $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (bad write_ptr $name:ident with $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *const $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (bad write_int $name:ident with $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (bad readwrite $name:ident with $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } ); (none $name:ident with $ioty:expr, $nr:expr) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong)) + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type)) } ); (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, - val: *mut $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + } + ); + (write_ptr $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *const $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, - val: $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + (write_int $name:ident with $ioty:expr, $nr:expr) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, - val: *mut $ty) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val)) + pub unsafe fn $name(fd: $crate::libc::c_int, + data: *mut $ty) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, - val: *mut $ty, - len: usize) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + (read_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: &mut [$ty]) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, ior!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, - val: *const $ty, - len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iow!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + (write_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: &[$ty]) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iow!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); - (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( - pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int, - val: *mut $ty, - len: usize) - -> $crate::Result<$crate::sys::ioctl::libc::c_int> { - convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val)) + (readwrite_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + pub unsafe fn $name(fd: $crate::libc::c_int, + data: &mut [$ty]) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, iorw!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } ); } diff --git a/src/sys/ioctl/platform/bsd.rs b/src/sys/ioctl/platform/bsd.rs index 57b4d637ca..ea39da3de6 100644 --- a/src/sys/ioctl/platform/bsd.rs +++ b/src/sys/ioctl/platform/bsd.rs @@ -1,36 +1,51 @@ +/// The datatype used for the ioctl number +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_ulong; + mod consts { - pub const VOID: u32 = 0x20000000; - pub const OUT: u32 = 0x40000000; - pub const IN: u32 = 0x80000000; - pub const INOUT: u32 = (IN|OUT); - pub const IOCPARM_MASK: u32 = 0x1fff; + use ::sys::ioctl::platform::ioctl_num_type; + #[doc(hidden)] + pub const VOID: ioctl_num_type = 0x20000000; + #[doc(hidden)] + pub const OUT: ioctl_num_type = 0x40000000; + #[doc(hidden)] + pub const IN: ioctl_num_type = 0x80000000; + #[doc(hidden)] + pub const INOUT: ioctl_num_type = (IN|OUT); + #[doc(hidden)] + pub const IOCPARM_MASK: ioctl_num_type = 0x1fff; } pub use self::consts::*; #[macro_export] +#[doc(hidden)] macro_rules! ioc { ($inout:expr, $group:expr, $num:expr, $len:expr) => ( - $inout | (($len as u32 & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as u32) << 8) | ($num as u32) + $inout | (($len as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as $crate::sys::ioctl::ioctl_num_type) << 8) | ($num as $crate::sys::ioctl::ioctl_num_type) ) } #[macro_export] +#[doc(hidden)] macro_rules! io { ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0)) } #[macro_export] +#[doc(hidden)] macro_rules! ior { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len)) } #[macro_export] +#[doc(hidden)] macro_rules! iow { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len)) } #[macro_export] +#[doc(hidden)] macro_rules! iorw { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)) } diff --git a/src/sys/ioctl/platform/linux.rs b/src/sys/ioctl/platform/linux.rs index cdf5b20ab8..117085d20b 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/platform/linux.rs @@ -1,12 +1,27 @@ -pub const NRBITS: u32 = 8; -pub const TYPEBITS: u32 = 8; +/// The datatype used for the ioctl number +#[cfg(any(target_os = "android", target_env = "musl"))] +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_int; +#[cfg(not(any(target_os = "android", target_env = "musl")))] +#[doc(hidden)] +pub type ioctl_num_type = ::libc::c_ulong; + +#[doc(hidden)] +pub const NRBITS: ioctl_num_type = 8; +#[doc(hidden)] +pub const TYPEBITS: ioctl_num_type = 8; #[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64"))] mod consts { + #[doc(hidden)] pub const NONE: u8 = 1; + #[doc(hidden)] pub const READ: u8 = 2; + #[doc(hidden)] pub const WRITE: u8 = 4; + #[doc(hidden)] pub const SIZEBITS: u8 = 13; + #[doc(hidden)] pub const DIRBITS: u8 = 3; } @@ -28,84 +43,73 @@ use this_arch_not_supported; target_arch = "x86_64", target_arch = "aarch64"))] mod consts { + #[doc(hidden)] pub const NONE: u8 = 0; + #[doc(hidden)] pub const READ: u8 = 2; + #[doc(hidden)] pub const WRITE: u8 = 1; + #[doc(hidden)] pub const SIZEBITS: u8 = 14; + #[doc(hidden)] pub const DIRBITS: u8 = 2; } pub use self::consts::*; -pub const NRSHIFT: u32 = 0; -pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32; -pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32; -pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32; - -pub const NRMASK: u32 = (1 << NRBITS) - 1; -pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1; -pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1; -pub const DIRMASK: u32 = (1 << DIRBITS) - 1; +#[doc(hidden)] +pub const NRSHIFT: ioctl_num_type = 0; +#[doc(hidden)] +pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type; +#[doc(hidden)] +pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type; +#[doc(hidden)] +pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type; + +#[doc(hidden)] +pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1; +#[doc(hidden)] +pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1; +#[doc(hidden)] +pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1; +#[doc(hidden)] +pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1; /// Encode an ioctl command. #[macro_export] +#[doc(hidden)] macro_rules! ioc { ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => ( - (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) | - (($ty as u32) << $crate::sys::ioctl::TYPESHIFT) | - (($nr as u32) << $crate::sys::ioctl::NRSHIFT) | - (($sz as u32) << $crate::sys::ioctl::SIZESHIFT)) + (($dir as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::DIRMASK) << $crate::sys::ioctl::DIRSHIFT) | + (($ty as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::TYPEMASK) << $crate::sys::ioctl::TYPESHIFT) | + (($nr as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::NRMASK) << $crate::sys::ioctl::NRSHIFT) | + (($sz as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::SIZEMASK) << $crate::sys::ioctl::SIZESHIFT)) } /// Encode an ioctl command that has no associated data. #[macro_export] +#[doc(hidden)] macro_rules! io { ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)) } /// Encode an ioctl command that reads. #[macro_export] +#[doc(hidden)] macro_rules! ior { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)) } /// Encode an ioctl command that writes. #[macro_export] +#[doc(hidden)] macro_rules! iow { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } /// Encode an ioctl command that both reads and writes. #[macro_export] +#[doc(hidden)] macro_rules! iorw { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } - -/// Extracts the "direction" (read/write/none) from an encoded ioctl command. -#[inline(always)] -pub fn ioc_dir(nr: u32) -> u8 { - ((nr >> DIRSHIFT) & DIRMASK) as u8 -} - -/// Extracts the type from an encoded ioctl command. -#[inline(always)] -pub fn ioc_type(nr: u32) -> u32 { - (nr >> TYPESHIFT) & TYPEMASK -} - -/// Extracts the ioctl number from an encoded ioctl command. -#[inline(always)] -pub fn ioc_nr(nr: u32) -> u32 { - (nr >> NRSHIFT) & NRMASK -} - -/// Extracts the size from an encoded ioctl command. -#[inline(always)] -pub fn ioc_size(nr: u32) -> u32 { - ((nr >> SIZESHIFT) as u32) & SIZEMASK -} - -pub const IN: u32 = (WRITE as u32) << DIRSHIFT; -pub const OUT: u32 = (READ as u32) << DIRSHIFT; -pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT; -pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT; diff --git a/src/sys/signal.rs b/src/sys/signal.rs index 309919b7eb..f885af9238 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -523,20 +523,20 @@ impl SigEvent { SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined }; sev.sigev_signo = match sigev_notify { - SigevNotify::SigevSignal{ signal, .. } => signal as ::c_int, + SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int, #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] SigevNotify::SigevKevent{ kq, ..} => kq, #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ signal, .. } => signal as ::c_int, + SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int, _ => 0 }; sev.sigev_value.sival_ptr = match sigev_notify { SigevNotify::SigevNone => ptr::null_mut::(), - SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut ::c_void, + SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void, #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] - SigevNotify::SigevKevent{ udata, .. } => udata as *mut ::c_void, + SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void, #[cfg(any(target_os = "linux", target_os = "freebsd"))] - SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut ::c_void, + SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void, }; SigEvent::set_tid(&mut sev, &sigev_notify); SigEvent{sigevent: sev} diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 55b61fd7b3..38ecdc99af 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -1,14 +1,26 @@ #![allow(dead_code)] // Simple tests to ensure macro generated fns compile -ioctl!(do_bad with 0x1234); +ioctl!(bad none do_bad with 0x1234); +ioctl!(bad read do_bad_read with 0x1234; u16); +ioctl!(bad write_int do_bad_write_int with 0x1234); +ioctl!(bad write_ptr do_bad_write_ptr with 0x1234; u8); +ioctl!(bad readwrite do_bad_readwrite with 0x1234; u32); ioctl!(none do_none with 0, 0); ioctl!(read read_test with 0, 0; u32); -ioctl!(write write_test with 0, 0; u64); +ioctl!(write_int write_ptr_int with 0, 0); +ioctl!(write_ptr write_ptr_u8 with 0, 0; u8); +ioctl!(write_ptr write_ptr_u32 with 0, 0; u32); +ioctl!(write_ptr write_ptr_u64 with 0, 0; u64); ioctl!(readwrite readwrite_test with 0, 0; u64); -ioctl!(read buf readbuf_test with 0, 0; u32); -ioctl!(write buf writebuf_test with 0, 0; u32); -ioctl!(readwrite buf readwritebuf_test with 0, 0; u32); +ioctl!(read_buf readbuf_test with 0, 0; u32); +const SPI_IOC_MAGIC: u8 = b'k'; +const SPI_IOC_MESSAGE: u8 = 0; +ioctl!(write_buf writebuf_test_consts with SPI_IOC_MAGIC, SPI_IOC_MESSAGE; u8); +ioctl!(write_buf writebuf_test_u8 with 0, 0; u8); +ioctl!(write_buf writebuf_test_u32 with 0, 0; u32); +ioctl!(write_buf writebuf_test_u64 with 0, 0; u64); +ioctl!(readwrite_buf readwritebuf_test with 0, 0; u32); // See C code for source of values for op calculations (does NOT work for mips/powerpc): // https://gist.github.com/posborne/83ea6880770a1aef332e @@ -82,7 +94,7 @@ mod linux { #[test] fn test_op_read_write_64() { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); - } + } } #[cfg(any(target_os = "macos", @@ -132,5 +144,184 @@ mod bsd { #[test] fn test_op_read_write_64() { assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC0007A0A); - } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::{TCGETS, TCSBRK, TCSETS, TIOCNXCL, termios}; + + use nix::Error::Sys; + use nix::errno::{ENOTTY, ENOSYS}; + + ioctl!(bad none tiocnxcl with TIOCNXCL); + #[test] + fn test_ioctl_bad_none() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad read tcgets with TCGETS; termios); + #[test] + fn test_ioctl_bad_read() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad write_int tcsbrk with TCSBRK); + #[test] + fn test_ioctl_bad_write_int() { + let file = tempfile().unwrap(); + let res = unsafe { tcsbrk(file.as_raw_fd(), 0) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(bad write_ptr tcsets with TCSETS; termios); + #[test] + fn test_ioctl_bad_write_ptr() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::uninitialized() }; + let res = unsafe { tcsets(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + // FIXME: Find a suitable example for "bad readwrite". + + // From linux/videodev2.h + ioctl!(none log_status with b'V', 70); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { log_status(file.as_raw_fd()) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + #[repr(C)] + pub struct v4l2_audio { + index: u32, + name: [u8; 32], + capability: u32, + mode: u32, + reserved: [u32; 2], + } + + // From linux/videodev2.h + ioctl!(write_ptr s_audio with b'V', 34; v4l2_audio); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { g_audio(file.as_raw_fd(), &data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/net/bluetooth/hci_sock.h + const HCI_IOC_MAGIC: u8 = b'H'; + const HCI_IOC_HCIDEVUP: u8 = 201; + ioctl!(write_int hcidevup with HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); + #[test] + fn test_ioctl_write_int() { + let file = tempfile().unwrap(); + let res = unsafe { hcidevup(file.as_raw_fd(), 0) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/videodev2.h + ioctl!(write_ptr g_audio with b'V', 33; v4l2_audio); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { g_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // From linux/videodev2.h + ioctl!(readwrite enum_audio with b'V', 65; v4l2_audio); + #[test] + fn test_ioctl_readwrite() { + let file = tempfile().unwrap(); + let mut data: v4l2_audio = unsafe { mem::uninitialized() }; + let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // FIXME: Find a suitable example for read_buf. + + #[repr(C)] + pub struct spi_ioc_transfer { + tx_buf: u64, + rx_buf: u64, + len: u32, + speed_hz: u32, + delay_usecs: u16, + bits_per_word: u8, + cs_change: u8, + tx_nbits: u8, + rx_nbits: u8, + pad: u16, + } + + // From linux/spi/spidev.h + ioctl!(write_buf spi_ioc_message with super::SPI_IOC_MAGIC, super::SPI_IOC_MESSAGE; spi_ioc_transfer); + #[test] + fn test_ioctl_write_buf() { + let file = tempfile().unwrap(); + let mut data: [spi_ioc_transfer; 4] = unsafe { mem::uninitialized() }; + let res = unsafe { spi_ioc_message(file.as_raw_fd(), &mut data[..]) }; + assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); + } + + // FIXME: Find a suitable example for readwrite_buf. +} + +#[cfg(target_os = "freebsd")] +mod freebsd_ioctls { + use std::mem; + use std::os::unix::io::AsRawFd; + + use tempfile::tempfile; + use libc::termios; + + use nix::Error::Sys; + use nix::errno::ENOTTY; + + // From sys/sys/ttycom.h + const TTY_IOC_MAGIC: u8 = b't'; + const TTY_IOC_TYPE_NXCL: u8 = 14; + const TTY_IOC_TYPE_GETA: u8 = 19; + const TTY_IOC_TYPE_SETA: u8 = 20; + + ioctl!(none tiocnxcl with TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL); + #[test] + fn test_ioctl_none() { + let file = tempfile().unwrap(); + let res = unsafe { tiocnxcl(file.as_raw_fd()) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(read tiocgeta with TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA; termios); + #[test] + fn test_ioctl_read() { + let file = tempfile().unwrap(); + let mut termios = unsafe { mem::uninitialized() }; + let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } + + ioctl!(write_ptr tiocseta with TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA; termios); + #[test] + fn test_ioctl_write_ptr() { + let file = tempfile().unwrap(); + let termios: termios = unsafe { mem::uninitialized() }; + let res = unsafe { tiocseta(file.as_raw_fd(), &termios) }; + assert_eq!(res, Err(Sys(ENOTTY))); + } }