#![allow(unsafe_code)]
use crate::fd::{AsFd, BorrowedFd};
use crate::ffi as c;
use crate::io::Result;
#[cfg(any(linux_kernel, bsd))]
use core::mem;
pub use patterns::*;
mod patterns;
#[cfg(linux_kernel)]
mod linux;
#[cfg(bsd)]
mod bsd;
#[cfg(linux_kernel)]
use linux as platform;
#[cfg(bsd)]
use bsd as platform;
#[inline]
pub unsafe fn ioctl<F: AsFd, I: Ioctl>(fd: F, mut ioctl: I) -> Result<I::Output> {
let fd = fd.as_fd();
let request = ioctl.opcode();
let arg = ioctl.as_ptr();
let output = if I::IS_MUTATING {
_ioctl(fd, request, arg)?
} else {
_ioctl_readonly(fd, request, arg)?
};
I::output_from_ptr(output, arg)
}
unsafe fn _ioctl(fd: BorrowedFd<'_>, request: Opcode, arg: *mut c::c_void) -> Result<IoctlOutput> {
crate::backend::io::syscalls::ioctl(fd, request, arg)
}
unsafe fn _ioctl_readonly(
fd: BorrowedFd<'_>,
request: Opcode,
arg: *mut c::c_void,
) -> Result<IoctlOutput> {
crate::backend::io::syscalls::ioctl_readonly(fd, request, arg)
}
pub unsafe trait Ioctl {
type Output;
const IS_MUTATING: bool;
fn opcode(&self) -> Opcode;
fn as_ptr(&mut self) -> *mut c::c_void;
unsafe fn output_from_ptr(
out: IoctlOutput,
extract_output: *mut c::c_void,
) -> Result<Self::Output>;
}
#[cfg(any(linux_kernel, bsd))]
pub mod opcode {
use super::*;
#[doc(alias = "_IOC")]
#[inline]
pub const fn from_components(
direction: Direction,
group: u8,
number: u8,
data_size: usize,
) -> Opcode {
assert!(data_size <= Opcode::MAX as usize, "data size is too large");
platform::compose_opcode(
direction,
group as Opcode,
number as Opcode,
data_size as Opcode,
)
}
#[doc(alias = "_IO")]
#[inline]
pub const fn none(group: u8, number: u8) -> Opcode {
from_components(Direction::None, group, number, 0)
}
#[doc(alias = "_IOR")]
#[inline]
pub const fn read<T>(group: u8, number: u8) -> Opcode {
from_components(Direction::Read, group, number, mem::size_of::<T>())
}
#[doc(alias = "_IOW")]
#[inline]
pub const fn write<T>(group: u8, number: u8) -> Opcode {
from_components(Direction::Write, group, number, mem::size_of::<T>())
}
#[doc(alias = "_IOWR")]
#[inline]
pub const fn read_write<T>(group: u8, number: u8) -> Opcode {
from_components(Direction::ReadWrite, group, number, mem::size_of::<T>())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Direction {
None,
Read,
Write,
ReadWrite,
}
pub type IoctlOutput = c::c_int;
pub type Opcode = _Opcode;
#[cfg(linux_raw)]
type _Opcode = c::c_uint;
#[cfg(all(
not(linux_raw),
target_os = "linux",
any(target_env = "gnu", target_env = "uclibc")
))]
type _Opcode = c::c_ulong;
#[cfg(all(
not(linux_raw),
target_os = "linux",
not(target_env = "gnu"),
not(target_env = "uclibc")
))]
type _Opcode = c::c_int;
#[cfg(all(not(linux_raw), target_os = "android"))]
type _Opcode = c::c_int;
#[cfg(any(
bsd,
target_os = "redox",
target_os = "haiku",
target_os = "horizon",
target_os = "hurd",
target_os = "vita"
))]
type _Opcode = c::c_ulong;
#[cfg(any(
solarish,
target_os = "aix",
target_os = "cygwin",
target_os = "fuchsia",
target_os = "emscripten",
target_os = "nto",
target_os = "wasi",
))]
type _Opcode = c::c_int;
#[cfg(target_os = "espidf")]
type _Opcode = c::c_uint;
#[cfg(windows)]
type _Opcode = i32;
#[cfg(linux_raw_dep)]
#[cfg(not(any(target_arch = "sparc", target_arch = "sparc64")))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_opcode_funcs() {
assert_eq!(
linux_raw_sys::ioctl::TUNGETDEVNETNS as Opcode,
opcode::none(b'T', 227)
);
assert_eq!(
linux_raw_sys::ioctl::FS_IOC_GETVERSION as Opcode,
opcode::read::<c::c_long>(b'v', 1)
);
assert_eq!(
linux_raw_sys::ioctl::TUNSETNOCSUM as Opcode,
opcode::write::<c::c_int>(b'T', 200)
);
assert_eq!(
linux_raw_sys::ioctl::FIFREEZE as Opcode,
opcode::read_write::<c::c_int>(b'X', 119)
);
}
}