1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
//! Provide helpers for making ioctl system calls //! //! # Overview of IOCTLs //! //! The `ioctl` system call is a widely support system //! call on *nix systems providing access to functions //! and data that do not fit nicely into the standard //! read and write operations on a file itself. It is //! common to see ioctls 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. //! //! Ioctls are synchronous system calls and are similar to read and //! write calls in that regard. //! //! The prototype for the ioctl system call in libc is as follows: //! //! ```c //! int ioctl(int fd, unsigned long request, ...); //! ``` //! //! Typically, an ioctl takes 3 parameters as arguments: //! //! 1. An open file descriptor, `fd`. //! 2. An device-dependennt request code or operation. This request //! code is referred to as `op` in this module. //! 3. Either a pointer to a location in memory or an integer. This //! number of pointer may either be used by the kernel or written //! to by the kernel depending on how the operation is documented //! to work. //! //! The `op` request code is essentially an arbitrary integer having //! a device-driver specific meaning. Over time, it proved difficult //! for various driver implementors to use this field sanely, so a //! convention with macros was introduced to the Linux Kernel that //! is used by most newer drivers. See //! https://github.com/torvalds/linux/blob/master/Documentation/ioctl/ioctl-number.txt //! for additional details. The macros exposed by the kernel for //! consumers are implemented in this module and may be used to //! instead of calls like `_IOC`, `_IO`, `_IOR`, and `_IOW`. //! //! # Interface Overview //! //! This ioctl module seeks to tame the ioctl beast by providing //! a set of safer (although not safe) functions //! implementing the most common ioctl access patterns. //! //! The most common access patterns for ioctls are as follows: //! //! 1. `read`: A pointer is provided to the kernel which is populated //! with a value containing the "result" of the operation. The //! result may be an integer or structure. The kernel may also //! read values from the provided pointer (usually a structure). //! 2. `write`: A pointer is provided to the kernel containing values //! that the kernel will read in order to perform the operation. //! 3. `execute`: The operation is passed to the kernel but no //! additional pointer is passed. The operation is enough //! and it either succeeds or results in an error. //! //! Where appropriate, versions of these interface function are provided //! taking either refernces or pointers. The pointer versions are //! necessary for cases (notably slices) where a reference cannot //! be generically cast to a pointer. use libc::{c_int, c_ulong}; use libc::funcs::bsd44::ioctl as libc_ioctl; use std::mem; use fcntl::Fd; use {Error, Result, errno}; pub type ioctl_op_t = c_ulong; // the libc definiton of the 'op' type is platform dependent #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "dragonfly"))] type os_ioctl_op_t = c_ulong; #[cfg(any(target_os = "linux", target_os = "android"))] type os_ioctl_op_t = c_int; // low-level ioctl functions and definitions matching the // macros provided in ioctl.h from the kernel const IOC_NRBITS: u32 = 8; const IOC_TYPEBITS: u32 = 8; const IOC_SIZEBITS: u32 = 14; // const IOC_DIRBITS: u32 = 2; const IOC_NRSHIFT: u32 = 0; const IOC_TYPESHIFT: u32 = IOC_NRSHIFT + IOC_NRBITS; const IOC_SIZESHIFT: u32 = IOC_TYPESHIFT + IOC_TYPEBITS; const IOC_DIRSHIFT: u32 = IOC_SIZESHIFT + IOC_SIZEBITS; /// Flags indicating the direction of the ioctl operation /// for ioctls using modern operation conventions bitflags! { flags IoctlDirFlags: u8 { /// Indicates that the ioctl data pointer is not used const IOC_NONE = 0x00, /// Indicates that the ioctl data pointer contains data that /// will be consumed by the operating system const IOC_WRITE = 0x01, /// Indicates tha the ioctl data pointer contains data that /// will be populated by the operating system to be consumed /// by userspace const IOC_READ = 0x02, } } /// Build an ioctl op with the provide parameters. This is a helper /// function for IOCTLs in the Linux kernel using the newer conventions /// for IOCTLs operations. Many ioctls do not use this newer convention /// and the constants for those should just be used as-is. /// /// This provides the same functionality as the Linux `_IOC` macro. pub fn op(dir: IoctlDirFlags, ioctl_type: u8, nr: u8, size: usize) -> ioctl_op_t { // actual number will always fit in 32 bits, but ioctl() expects // an unsigned long for the op let size_to_use: u32 = if size < (1 << IOC_SIZEBITS) { size as u32 } else { 0 }; (((dir.bits as u32) << IOC_DIRSHIFT) | ((ioctl_type as u32) << IOC_TYPESHIFT) | ((nr as u32) << IOC_NRSHIFT) | ((size_to_use) << IOC_SIZESHIFT)) as ioctl_op_t } /// Build an op indicating that the data pointer is not used. /// That is, the command itself is sufficient. /// /// This provides the same functionality the Linux `_IO` macro. pub fn op_none(ioctl_type: u8, nr: u8) -> ioctl_op_t { op(IOC_NONE, ioctl_type, nr, 0) } /// Build an op indicating that the data pointer will be populated /// with data from the kernel /// /// This provides the same functionality as the Linux `_IOR` macro. pub fn op_read(ioctl_type: u8, nr: u8, size: usize) -> ioctl_op_t { op(IOC_READ, ioctl_type, nr, size) } /// Build an op indicating that the data pointer contains data /// to be consumed by the kernel (and not written to). /// /// This provides the same functionality as the Linux `_IOW` macro. pub fn op_write(ioctl_type: u8, nr: u8, size: usize) -> ioctl_op_t { op(IOC_WRITE, ioctl_type, nr, size) } /// Build an op indicating that the data pointer both contains /// data to be consumed by the kernel and contains fields that /// will be populated by the kernel. /// /// This provides the same functionality as the Linux `_IOWR` macro. pub fn op_read_write(ioctl_type: u8, nr: u8, size: usize) -> ioctl_op_t { op(IOC_WRITE | IOC_READ, ioctl_type, nr, size) } fn convert_ioctl_res(res: c_int) -> Result<c_int> { if res < 0 { return Err(Error::Sys(errno::Errno::last())) } Ok(res) // res may length or similar useful to caller } /// Ioctl call that is expected to return a result /// but which does not take any additional arguments on the input side /// /// This function will allocate allocate space for and returned an owned /// reference to the result. pub unsafe fn read<T>(fd: Fd, op: ioctl_op_t) -> Result<T> { // allocate memory for the result (should get a value from kernel) let mut dst: T = mem::zeroed(); let dst_ptr: *mut T = &mut dst; try!(read_into_ptr(fd, op, dst_ptr)); Ok(dst) } /// Ioctl where the result from the kernel will be written to the /// provided reference /// /// The refernced data may also contain information that will be consumed /// by the kernel. pub unsafe fn read_into<T>(fd: Fd, op: ioctl_op_t, data: &mut T) -> Result<c_int> { read_into_ptr(fd, op, data as *mut T) } /// Ioctl where the result from the kernel will be written to the /// provided pointer /// /// The refernced data may also contain information that will be consumed /// by the kernel. pub unsafe fn read_into_ptr<T>(fd: Fd, op: ioctl_op_t, data_ptr: *mut T) -> Result<c_int> { convert_ioctl_res(libc_ioctl(fd, op as os_ioctl_op_t, data_ptr)) } /// Ioctl call that sends a value to the kernel but /// does not return anything (pure side effect). pub unsafe fn write<T>(fd: Fd, op: ioctl_op_t, data: &T) -> Result<c_int> { write_ptr(fd, op, data as *const T) } /// Ioctl call that sends a value to the kernel but /// does not return anything (pure side effect). pub unsafe fn write_ptr<T>(fd: Fd, op: ioctl_op_t, data: *const T) -> Result<c_int> { convert_ioctl_res(libc_ioctl(fd, op as os_ioctl_op_t, data as *const T)) } /// Ioctl call for which no data pointer is provided to the kernel. /// That is, the kernel has sufficient information about what to /// do based on the op alone. pub fn execute(fd: Fd, op: ioctl_op_t) -> Result<c_int> { convert_ioctl_res(unsafe { libc_ioctl(fd, op as os_ioctl_op_t) }) }