diff --git a/src/sys/ioctl/platform/bsd.rs b/src/sys/ioctl/bsd.rs similarity index 90% rename from src/sys/ioctl/platform/bsd.rs rename to src/sys/ioctl/bsd.rs index a36808d785..d099ccbd2c 100644 --- a/src/sys/ioctl/platform/bsd.rs +++ b/src/sys/ioctl/bsd.rs @@ -28,24 +28,24 @@ macro_rules! ioc { #[macro_export] #[doc(hidden)] -macro_rules! io { +macro_rules! request_code_none { ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0)) } #[macro_export] #[doc(hidden)] -macro_rules! ior { +macro_rules! request_code_read { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len)) } #[macro_export] #[doc(hidden)] -macro_rules! iow { +macro_rules! request_code_write { ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len)) } #[macro_export] #[doc(hidden)] -macro_rules! iorw { +macro_rules! request_code_readwrite { ($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/linux.rs similarity index 62% rename from src/sys/ioctl/platform/linux.rs rename to src/sys/ioctl/linux.rs index 117085d20b..e0d02ad419 100644 --- a/src/sys/ioctl/platform/linux.rs +++ b/src/sys/ioctl/linux.rs @@ -25,17 +25,6 @@ mod consts { pub const DIRBITS: u8 = 3; } -#[cfg(not(any(target_arch = "powerpc", - target_arch = "mips", - target_arch = "mips64", - target_arch = "x86", - target_arch = "arm", - target_arch = "x86_64", - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "aarch64")))] -use this_arch_not_supported; - // "Generic" ioctl protocol #[cfg(any(target_arch = "x86", target_arch = "arm", @@ -86,30 +75,63 @@ macro_rules! ioc { (($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. +/// Generate an ioctl request code for a command that passes no data. +/// +/// This is equivalent to the `_IO()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_none!()` directly. +/// +/// # Example +/// +/// ``` +/// # #[macro_use] extern crate nix; +/// const KVMIO: u8 = 0xAE; +/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03)); +/// # fn main() {} +/// ``` #[macro_export] -#[doc(hidden)] -macro_rules! io { +macro_rules! request_code_none { ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)) } -/// Encode an ioctl command that reads. +/// Generate an ioctl request code for a command that reads. +/// +/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_read!()` directly. +/// +/// The read/write direction is relative to userland, so this +/// command would be userland is reading and the kernel is +/// writing. #[macro_export] -#[doc(hidden)] -macro_rules! ior { +macro_rules! request_code_read { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)) } -/// Encode an ioctl command that writes. +/// Generate an ioctl request code for a command that writes. +/// +/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_write!()` directly. +/// +/// The read/write direction is relative to userland, so this +/// command would be userland is writing and the kernel is +/// reading. #[macro_export] -#[doc(hidden)] -macro_rules! iow { +macro_rules! request_code_write { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } -/// Encode an ioctl command that both reads and writes. +/// Generate an ioctl request code for a command that reads and writes. +/// +/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API. +/// +/// You should only use this macro directly if the `ioctl` you're working +/// with is "bad" and you cannot use `ioctl_readwrite!()` directly. #[macro_export] -#[doc(hidden)] -macro_rules! iorw { +macro_rules! request_code_readwrite { ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz)) } diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index 44822e4fde..d7f9c58d6b 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -41,8 +41,8 @@ //! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are //! commonly referred to as "bad" in `ioctl` documentation. //! -//! Defining ioctls -//! =============== +//! Defining `ioctl`s +//! ================= //! //! 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 @@ -57,7 +57,7 @@ //! # #[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); +//! ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8); //! # fn main() {} //! ``` //! @@ -72,7 +72,7 @@ //! # 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); +//! let res = libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::()), data); //! Errno::result(res) //! } //! # fn main() {} @@ -91,7 +91,7 @@ //! # #[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); +//! ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); //! # fn main() {} //! ``` //! @@ -109,11 +109,11 @@ //! //! 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 -//! ------------------------------ +//! Using "bad" `ioctl`s +//! -------------------- //! //! 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 *` +//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the `*` //! 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. @@ -128,7 +128,7 @@ //! # #[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); +//! ioctl_read_bad!(tcgets, TCGETS, termios); //! # fn main() {} //! ``` //! @@ -138,11 +138,30 @@ //! 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. +//! The `ioctl_*_bad` variants should also be used when `ioctl` numbers are generated using the +//! wrong macro. For example, the `KVM_CREATE_VM` ioctl is defined as follows in C: //! -//! Working with arrays -//! -------------------- +//! ```C +//! #define KVM_CREATE_VM _IO(KVMIO, 0x03); +//! ioctl(fd, KVM_CREATE_VM, some_flags); +//! ``` +//! +//! It uses the `_IO` macro, which should mean it passes no data, but this ioctl actually expects +//! an `int` to be passed containing various flags. The only way to know this would be to refer to +//! the documentation for it or other code examples rather looking only at the header file +//! declaration. To declare this `ioctl` in Rust is therefore: +//! +//! ```rust +//! # #[macro_use] extern crate nix; +//! const KVMIO: u8 = 0xAE; +//! ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03)); +//! # fn main() {} +//! ``` +//! +//! The other `ioctl_*_bad` functions all work in a similar manner. +//! +//! 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 @@ -160,7 +179,7 @@ //! #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 +//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl_*!` macros, so all that's //! needed to define this `ioctl` is: //! //! ``` @@ -168,7 +187,7 @@ //! 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); +//! ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer); //! # fn main() {} //! ``` //! @@ -185,22 +204,22 @@ //! # 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::()), +//! request_code_write!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::()), //! data); //! Errno::result(res) //! } //! # fn main() {} //! ``` //! -//! Finding ioctl documentation -//! --------------------------- +//! 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)`). //! -//! Documenting the generated functions +//! Documenting the Generated Functions //! =================================== //! //! In many cases, users will wish for the functions generated by the `ioctl` @@ -214,35 +233,42 @@ //! ``` //! # #[macro_use] extern crate nix; //! # use nix::libc::c_int; -//! ioctl! { +//! ioctl_read! { //! /// Make the given terminal the controlling terminal of the calling process. The calling //! /// process must be a session leader and not have a controlling terminal already. If the //! /// terminal is already the controlling terminal of a different session group then the //! /// ioctl will fail with **EPERM**, unless the caller is root (more precisely: has the //! /// **CAP_SYS_ADMIN** capability) and arg equals 1, in which case the terminal is stolen //! /// and all processes that had it as controlling terminal lose it. -//! read tiocsctty with b't', 19; c_int +//! tiocsctty, b't', 19, c_int //! } //! //! # fn main() {} //! ``` //! -#[cfg(any(target_os = "linux", target_os = "android"))] -#[path = "platform/linux.rs"] +#[cfg(any(target_os = "android", target_os = "linux"))] #[macro_use] -mod platform; +mod linux; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::linux::*; -#[cfg(any(target_os = "macos", +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd", - target_os = "freebsd", - target_os = "dragonfly"))] -#[path = "platform/bsd.rs"] + target_os = "openbsd"))] #[macro_use] -mod platform; +mod bsd; -pub use self::platform::*; +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +pub use self::bsd::*; /// Convert raw ioctl return value to a Nix result #[macro_export] @@ -255,109 +281,196 @@ macro_rules! convert_ioctl_res { ); } -/// Generates ioctl functions. See [`::sys::ioctl`](sys/ioctl/index.html). +/// Creates an `ioctl` wrapper function for ioctls that pass no data +/// +/// # Example +/// +/// The `videodev2` driver on Linux defines the `log_status` `ioctl` as: +/// +/// ```C +/// #define VIDIOC_LOG_STATUS _IO('V', 70) +/// ``` +/// +/// This can be implemented in Rust like: +/// +/// ```no_run +/// # #[macro_use] extern crate nix; +/// # use std::fs::File; +/// # use std::os::unix::io::AsRawFd; +/// ioctl_none!(log_status, b'V', 70); +/// fn main() { +/// let file = tempfile().unwrap(); +/// let res = unsafe { log_status(file.as_raw_fd()) }.unwrap(); +/// } +/// ``` #[macro_export] -macro_rules! ioctl { - ($(#[$attr:meta])* bad none $name:ident with $nr:expr) => ( +macro_rules! ioctl_none { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => ( + $(#[$attr])* + pub unsafe fn $name(fd: $crate::libc::c_int) + -> $crate::Result<$crate::libc::c_int> { + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type)) + } + ) +} + +/// Creates an `ioctl` wrapper function for "bad" ioctls that pass no data. +/// +/// # Example +/// +/// ```no_run +/// # #[macro_use] extern crate nix; +/// # extern crate libc; +/// # use libc::TIOCNXCL; +/// # use std::fs::File; +/// # use std::os::unix::io::AsRawFd; +/// ioctl_none_bad!(tiocnxcl, TIOCNXCL); +/// fn main() { +/// let file = File::open("/dev/ttyUSB0").unwrap(); +/// unsafe { tiocnxcl(file.as_raw_fd()) }.unwrap(); +/// } +/// ``` +/// +/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). +#[macro_export] +macro_rules! ioctl_none_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr) => ( $(#[$attr])* 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)) } - ); - ($(#[$attr:meta])* bad read $name:ident with $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_read { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( $(#[$attr])* 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)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* bad write_ptr $name:ident with $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_read_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => ( $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, - data: *const $ty) + 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)) } - ); - ($(#[$attr:meta])* bad write_int $name:ident with $nr:expr) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_write_ptr { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, - data: $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)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* bad readwrite $name:ident with $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_write_ptr_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => ( $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut $ty) + 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)) } - ); - ($(#[$attr:meta])* none $name:ident with $ioty:expr, $nr:expr) => ( - $(#[$attr])* - 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)) - } - ); - ($(#[$attr:meta])* read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_write_int { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => ( $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, - data: *mut $ty) + data: $crate::libc::c_int) -> $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)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* write_ptr $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_write_int_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr) => ( $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, - data: *const $ty) + 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::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* write_int $name:ident with $ioty:expr, $nr:expr) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_readwrite { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( $(#[$attr])* pub unsafe fn $name(fd: $crate::libc::c_int, - data: $crate::libc::c_int) + data: *mut $ty) -> $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)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_readwrite_bad { + ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => ( $(#[$attr])* 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)) + convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* read_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_read_buf { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( $(#[$attr])* 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)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* write_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_write_buf { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( $(#[$attr])* 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)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } - ); - ($(#[$attr:meta])* readwrite_buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => ( + ) +} + +#[macro_export] +macro_rules! ioctl_readwrite_buf { + ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => ( $(#[$attr])* 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)) + convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data)) } - ); + ) } diff --git a/src/sys/mod.rs b/src/sys/mod.rs index a94b8a062f..cd312f4c60 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -23,6 +23,14 @@ pub mod eventfd; #[cfg(target_os = "linux")] pub mod memfd; +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] #[macro_use] pub mod ioctl; diff --git a/test/sys/test_ioctl.rs b/test/sys/test_ioctl.rs index 9a7a4819d7..d38ba99f90 100644 --- a/test/sys/test_ioctl.rs +++ b/test/sys/test_ioctl.rs @@ -1,80 +1,26 @@ #![allow(dead_code)] // Simple tests to ensure macro generated fns compile -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_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_none_bad!(do_bad, 0x1234); +ioctl_read_bad!(do_bad_read, 0x1234, u16); +ioctl_write_int_bad!(do_bad_write_int, 0x1234); +ioctl_write_ptr_bad!(do_bad_write_ptr, 0x1234, u8); +ioctl_readwrite_bad!(do_bad_readwrite, 0x1234, u32); +ioctl_none!(do_none, 0, 0); +ioctl_read!(read_test, 0, 0, u32); +ioctl_write_int!(write_ptr_int, 0, 0); +ioctl_write_ptr!(write_ptr_u8, 0, 0, u8); +ioctl_write_ptr!(write_ptr_u32, 0, 0, u32); +ioctl_write_ptr!(write_ptr_u64, 0, 0, u64); +ioctl_readwrite!(readwrite_test, 0, 0, u64); +ioctl_read_buf!(readbuf_test, 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); - -// Make sure documentation works -ioctl! { - /// This documents the ioctl function - bad none do_bad_docs with 0x1234 -} -ioctl! { - /// This documents the ioctl function - bad read do_bad_read_docs with 0x1234; u16 -} -ioctl! { - /// This documents the ioctl function - bad write_int do_bad_write_int_docs with 0x1234 -} -ioctl! { - /// This documents the ioctl function - bad write_ptr do_bad_write_ptr_docs with 0x1234; u8 -} -ioctl! { - /// This documents the ioctl function - bad readwrite do_bad_readwrite_docs with 0x1234; u32 -} -ioctl! { - /// This documents the ioctl function - none do_none_docs with 0, 0 -} -ioctl! { - /// This documents the ioctl function - read do_read_docs with 0, 0; u32 -} -ioctl! { - /// This documents the ioctl function - write_int do_write_int_docs with 0, 0 -} -ioctl! { - /// This documents the ioctl function - write_ptr do_write_ptr_docs with 0, 0; u32 -} -ioctl! { - /// This documents the ioctl function - readwrite do_readwrite_docs with 0, 0; u32 -} -ioctl! { - /// This documents the ioctl function - read_buf do_read_buf_docs with 0, 0; u32 -} -ioctl! { - /// This documents the ioctl function - write_buf do_write_buf_docs with 0, 0; u32 -} -ioctl! { - /// This documents the ioctl function - readwrite_buf do_readwrite_buf_docs with 0, 0; u32 -} +ioctl_write_buf!(writebuf_test_consts, SPI_IOC_MAGIC, SPI_IOC_MESSAGE, u8); +ioctl_write_buf!(writebuf_test_u8, 0, 0, u8); +ioctl_write_buf!(writebuf_test_u32, 0, 0, u32); +ioctl_write_buf!(writebuf_test_u64, 0, 0, u64); +ioctl_readwrite_buf!(readwritebuf_test, 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 @@ -87,22 +33,22 @@ mod linux { #[test] fn test_op_none() { if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ - assert_eq!(io!(b'q', 10), 0x2000_710A); - assert_eq!(io!(b'a', 255), 0x2000_61FF); + assert_eq!(request_code_none!(b'q', 10), 0x2000_710A); + assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF); } else { - assert_eq!(io!(b'q', 10), 0x0000_710A); - assert_eq!(io!(b'a', 255), 0x0000_61FF); + assert_eq!(request_code_none!(b'q', 10), 0x0000_710A); + assert_eq!(request_code_none!(b'a', 255), 0x0000_61FF); } } #[test] fn test_op_write() { if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ - assert_eq!(iow!(b'z', 10, 1), 0x8001_7A0A); - assert_eq!(iow!(b'z', 10, 512), 0x8200_7A0A); + assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A); } else { - assert_eq!(iow!(b'z', 10, 1), 0x4001_7A0A); - assert_eq!(iow!(b'z', 10, 512), 0x4200_7A0A); + assert_eq!(request_code_write!(b'z', 10, 1), 0x4001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512), 0x4200_7A0A); } } @@ -110,9 +56,9 @@ mod linux { #[test] fn test_op_write_64() { if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ - assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A); + assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A); } else { - assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); + assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); } } @@ -120,11 +66,11 @@ mod linux { #[test] fn test_op_read() { if cfg!(any(target_arch = "mips", target_arch = "mips64", target_arch="powerpc", target_arch="powerpc64")){ - assert_eq!(ior!(b'z', 10, 1), 0x4001_7A0A); - assert_eq!(ior!(b'z', 10, 512), 0x4200_7A0A); + assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A); } else { - assert_eq!(ior!(b'z', 10, 1), 0x8001_7A0A); - assert_eq!(ior!(b'z', 10, 512), 0x8200_7A0A); + assert_eq!(request_code_read!(b'z', 10, 1), 0x8001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512), 0x8200_7A0A); } } @@ -132,72 +78,72 @@ mod linux { #[test] fn test_op_read_64() { if cfg!(any(target_arch = "mips64", target_arch="powerpc64")){ - assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); + assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); } else { - assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A); + assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A); } } #[test] fn test_op_read_write() { - assert_eq!(iorw!(b'z', 10, 1), 0xC001_7A0A); - assert_eq!(iorw!(b'z', 10, 512), 0xC200_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A); } #[cfg(target_pointer_width = "64")] #[test] fn test_op_read_write_64() { - assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC000_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, (1 as u64) << 32), 0xC000_7A0A); } } -#[cfg(any(target_os = "macos", +#[cfg(any(target_os = "dragonflybsd", + target_os = "freebsd", target_os = "ios", + target_os = "macos", target_os = "netbsd", - target_os = "openbsd", - target_os = "freebsd", - target_os = "dragonfly"))] + target_os = "openbsd"))] mod bsd { #[test] fn test_op_none() { - assert_eq!(io!(b'q', 10), 0x2000_710A); - assert_eq!(io!(b'a', 255), 0x2000_61FF); + assert_eq!(request_code_none!(b'q', 10), 0x2000_710A); + assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF); } #[test] fn test_op_write() { - assert_eq!(iow!(b'z', 10, 1), 0x8001_7A0A); - assert_eq!(iow!(b'z', 10, 512), 0x8200_7A0A); + assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A); + assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A); } #[cfg(target_pointer_width = "64")] #[test] fn test_op_write_64() { - assert_eq!(iow!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A); + assert_eq!(request_code_write!(b'z', 10, (1 as u64) << 32), 0x8000_7A0A); } #[test] fn test_op_read() { - assert_eq!(ior!(b'z', 10, 1), 0x4001_7A0A); - assert_eq!(ior!(b'z', 10, 512), 0x4200_7A0A); + assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A); + assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A); } #[cfg(target_pointer_width = "64")] #[test] fn test_op_read_64() { - assert_eq!(ior!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); + assert_eq!(request_code_read!(b'z', 10, (1 as u64) << 32), 0x4000_7A0A); } #[test] fn test_op_read_write() { - assert_eq!(iorw!(b'z', 10, 1), 0xC001_7A0A); - assert_eq!(iorw!(b'z', 10, 512), 0xC200_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A); } #[cfg(target_pointer_width = "64")] #[test] fn test_op_read_write_64() { - assert_eq!(iorw!(b'z', 10, (1 as u64) << 32), 0xC000_7A0A); + assert_eq!(request_code_readwrite!(b'z', 10, (1 as u64) << 32), 0xC000_7A0A); } } @@ -212,44 +158,44 @@ mod linux_ioctls { use nix::Error::Sys; use nix::errno::Errno::{ENOTTY, ENOSYS}; - ioctl!(bad none tiocnxcl with TIOCNXCL); + ioctl_none_bad!(tiocnxcl, TIOCNXCL); #[test] - fn test_ioctl_bad_none() { + fn test_ioctl_none_bad() { 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); + ioctl_read_bad!(tcgets, TCGETS, termios); #[test] - fn test_ioctl_bad_read() { + fn test_ioctl_read_bad() { 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); + ioctl_write_int_bad!(tcsbrk, TCSBRK); #[test] - fn test_ioctl_bad_write_int() { + fn test_ioctl_write_int_bad() { 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); + ioctl_write_ptr_bad!(tcsets, TCSETS, termios); #[test] - fn test_ioctl_bad_write_ptr() { + fn test_ioctl_write_ptr_bad() { 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". + // FIXME: Find a suitable example for `ioctl_readwrite_bad` // From linux/videodev2.h - ioctl!(none log_status with b'V', 70); + ioctl_none!(log_status, b'V', 70); #[test] fn test_ioctl_none() { let file = tempfile().unwrap(); @@ -267,7 +213,7 @@ mod linux_ioctls { } // From linux/videodev2.h - ioctl!(write_ptr s_audio with b'V', 34; v4l2_audio); + ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio); #[test] fn test_ioctl_write_ptr() { let file = tempfile().unwrap(); @@ -279,7 +225,7 @@ mod linux_ioctls { // 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); + ioctl_write_int!(hcidevup, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP); #[test] fn test_ioctl_write_int() { let file = tempfile().unwrap(); @@ -288,7 +234,7 @@ mod linux_ioctls { } // From linux/videodev2.h - ioctl!(read g_audio with b'V', 33; v4l2_audio); + ioctl_read!(g_audio, b'V', 33, v4l2_audio); #[test] fn test_ioctl_read() { let file = tempfile().unwrap(); @@ -298,7 +244,7 @@ mod linux_ioctls { } // From linux/videodev2.h - ioctl!(readwrite enum_audio with b'V', 65; v4l2_audio); + ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio); #[test] fn test_ioctl_readwrite() { let file = tempfile().unwrap(); @@ -307,7 +253,7 @@ mod linux_ioctls { assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); } - // FIXME: Find a suitable example for read_buf. + // FIXME: Find a suitable example for `ioctl_read_buf`. #[repr(C)] pub struct spi_ioc_transfer { @@ -324,7 +270,7 @@ mod linux_ioctls { } // From linux/spi/spidev.h - ioctl!(write_buf spi_ioc_message with super::SPI_IOC_MAGIC, super::SPI_IOC_MESSAGE; spi_ioc_transfer); + ioctl_write_buf!(spi_ioc_message, super::SPI_IOC_MAGIC, super::SPI_IOC_MESSAGE, spi_ioc_transfer); #[test] fn test_ioctl_write_buf() { let file = tempfile().unwrap(); @@ -333,7 +279,7 @@ mod linux_ioctls { assert!(res == Err(Sys(ENOTTY)) || res == Err(Sys(ENOSYS))); } - // FIXME: Find a suitable example for readwrite_buf. + // FIXME: Find a suitable example for `ioctl_readwrite_buf`. } #[cfg(target_os = "freebsd")]