linux_ioctl/lib.rs
1//! `ioctl`s for Linux APIs.
2//!
3//! This library provides a convenient way to bind to Linux `ioctl`s.
4//!
5//! It is intended to help with writing wrappers around driver functionality, and tries to mirror
6//! the syntax you'll find in C headers closely.
7//!
8//! # Example
9//!
10//! Let's wrap V4L2's `QUERYCAP` ioctl.
11//!
12//! From `linux/videodev2.h`:
13//!
14//! ```c
15//! struct v4l2_capability {
16//! __u8 driver[16];
17//! __u8 card[32];
18//! __u8 bus_info[32];
19//! __u32 version;
20//! __u32 capabilities;
21//! __u32 device_caps;
22//! __u32 reserved[3];
23//! };
24//! // ...
25//! #define VIDIOC_QUERYCAP _IOR('V', 0, struct v4l2_capability)
26//! ```
27//!
28//! ```no_run
29//! use std::mem::MaybeUninit;
30//! use linux_ioctl::*;
31//!
32//! #[repr(C)]
33//! struct Capability {
34//! driver: [u8; 16],
35//! card: [u8; 32],
36//! bus_info: [u8; 32],
37//! version: u32,
38//! capabilities: u32,
39//! device_caps: u32,
40//! reserved: [u32; 3],
41//! }
42//!
43//! const VIDIOC_QUERYCAP: Ioctl<*mut Capability> = _IOR(b'V', 0);
44//!
45//! // Use as follows:
46//!
47//! # let fd = 123;
48//! let capability = unsafe {
49//! let mut capability = MaybeUninit::uninit();
50//! VIDIOC_QUERYCAP.ioctl(&fd, capability.as_mut_ptr())?;
51//! capability.assume_init()
52//! };
53//! # std::io::Result::Ok(())
54//! ```
55//!
56//! # Portability
57//!
58//! Despite being about Linux APIs, and following the Linux convention for declaring *ioctl* codes,
59//! this library should also work on other operating systems that implement a Linux-comparible
60//! `ioctl`-based API.
61//!
62//! For example, FreeBSD implements a variety of compatible interfaces like *evdev* and *V4L2*.
63//!
64//! # Safety
65//!
66//! To safely perform an *ioctl*, the actual behavior of the kernel-side has to match the behavior
67//! expected by userspace (which is encoded in the [`Ioctl`] type).
68//!
69//! To accomplish this, it is necessary that the [`Ioctl`] was constructed correctly by the caller:
70//! the direction, type, number, and argument type size are used to build the ioctl request code,
71//! and the Rust type used as the ioctl argument has to match what the kernel expects.
72//! If the argument is a pointer the kernel will read from or write to, the data behind the pointer
73//! also has to be valid, of course (`ioctl`s are arbitrary functions, so the same care is needed
74//! as when binding to an arbitrary C function).
75//!
76//! However, this is not, strictly speaking, *sufficient* to ensure safety:
77//! several drivers and subsystems share the same *ioctl* "type" value, which may lead to an ioctl
78//! request code that is interpreted differently, depending on which driver receives the request.
79//! Since the *ioctl* request code encodes the size of the argument type, this operation is unlikely
80//! to cause a fault when accessing memory, since both argument types have the same size, so the
81//! `ioctl` syscall may complete successfully instead of returning `EFAULT`.
82//!
83//! The result of this situation is that a type intended for data from one driver now has data from
84//! an entirely unrelated driver in it, which will likely cause UB, either because a *validity
85//! invariant* was violated by the data written to the structure, or because userspace will trust
86//! the kernel to only write valid data (including pointers) to the structure.
87//!
88//! While it may technically be possible to tell which driver owns a given device file descriptor
89//! by crawling `/sys` or querying `udev`, in practice this situation is deemed "sufficiently
90//! unlikely to cause problems" and programs don't bother with this.
91//!
92//! One way to rule out this issue is to prevent arbitrary file descriptors from making their way
93//! to the ioctl, and to ensure that only files that match the driver's naming convention are used
94//! for these ioctls.
95//! For example, an *evdev* wrapper could refuse to operate on files outside of `/dev/input`, and a
96//! KVM API could always open `/dev/kvm` without offering a safe API to act on a different device
97//! file.
98//!
99//! For more information, you can look at the list of ioctl groups here:
100//! <https://www.kernel.org/doc/html/latest/userspace-api/ioctl/ioctl-number.html>
101//!
102//! ***TL;DR**: don't worry about it kitten :)*
103
104#[doc = include_str!("../README.md")]
105mod readme {}
106
107mod consts;
108
109use std::{ffi::c_int, fmt, io, marker::PhantomData, ops::BitOr, os::fd::AsRawFd};
110
111use consts::_IOC_SIZEMASK;
112
113/// An *ioctl*.
114///
115/// [`Ioctl`] can represent *ioctl*s that take either no arguments or a single argument.
116/// If `T` is [`NoArgs`], the *ioctl* takes no arguments.
117/// For other values of `T`, the *ioctl* takes `T` as its only argument.
118/// Often, the argument `T` is a pointer or reference to a struct that contains the actual
119/// arguments.
120///
121/// While [`Ioctl`] cannot handle *ioctl*s that require passing more than one argument to the
122/// `ioctl(2)` function, Linux doesn't have any *ioctl*s that take more than one argument, and is
123/// unlikely to gain any in the future.
124///
125/// The [`Ioctl`] type is constructed with the free functions [`_IO`], [`_IOR`], [`_IOW`],
126/// [`_IOWR`], and [`_IOC`].
127/// For legacy *ioctl*s, it can also be created via [`Ioctl::from_raw`].
128pub struct Ioctl<T: ?Sized = NoArgs> {
129 request: u32,
130 _p: PhantomData<T>,
131}
132
133impl<T: ?Sized> Copy for Ioctl<T> {}
134impl<T: ?Sized> Clone for Ioctl<T> {
135 fn clone(&self) -> Self {
136 *self
137 }
138}
139
140impl<T: ?Sized> Ioctl<T> {
141 /// Creates an [`Ioctl`] object from a raw request code and an arbitrary argument type.
142 ///
143 /// This can be used for legacy *ioctl*s that were defined before the `_IOx` macros were
144 /// introduced.
145 ///
146 /// # Examples
147 ///
148 /// From `asm-generic/ioctls.h`:
149 ///
150 /// ```c
151 /// #define FIONREAD 0x541B
152 /// ```
153 ///
154 /// From `man 2const FIONREAD`:
155 ///
156 /// ```text
157 /// DESCRIPTION
158 /// FIONREAD
159 /// Get the number of bytes in the input buffer.
160 /// ...
161 /// SYNOPSIS
162 /// ...
163 /// int ioctl(int fd, FIONREAD, int *argp);
164 /// ...
165 /// ```
166 ///
167 /// ```
168 /// use std::io;
169 /// use std::fs::File;
170 /// use std::ffi::c_int;
171 /// use linux_ioctl::*;
172 ///
173 /// const FIONREAD: Ioctl<*mut c_int> = Ioctl::from_raw(0x541B);
174 ///
175 /// let file = File::open("/dev/ptmx")
176 /// .map_err(|e| io::Error::new(e.kind(), format!("failed to open `/dev/ptmx`: {e}")))?;
177 ///
178 /// let mut bytes = c_int::MAX;
179 /// unsafe { FIONREAD.ioctl(&file, &mut bytes)? };
180 /// assert_ne!(bytes, c_int::MAX);
181 ///
182 /// println!("{} bytes in input buffer", bytes);
183 /// # std::io::Result::Ok(())
184 /// ```
185 pub const fn from_raw(request: u32) -> Self {
186 Self {
187 request,
188 _p: PhantomData,
189 }
190 }
191
192 /// Changes the *ioctl* argument type to `T2`.
193 ///
194 /// This can be used for *ioctl*s that incorrectly declare their type, or for *ioctl*s that take
195 /// a by-value argument, rather than [`_IOW`]-type *ioctl*s that take their argument indirectly
196 /// through a pointer.
197 ///
198 /// Returns an [`Ioctl`] that passes an argument of type `T2` to the kernel, while using the
199 /// *ioctl* request code from `self`.
200 ///
201 /// # Examples
202 ///
203 /// The `KVM_CREATE_VM` *ioctl* is declared with [`_IO`], but expects an `int` argument to be
204 /// passed to `ioctl(2)`, specifying the VM type (`KVM_VM_*`).
205 ///
206 /// From `linux/kvm.h`:
207 ///
208 /// ```c
209 /// #define KVMIO 0xAE
210 /// ...
211 /// #define KVM_CREATE_VM _IO(KVMIO, 0x01) /* returns a VM fd */
212 /// ```
213 ///
214 /// ```no_run
215 /// use std::fs::File;
216 /// use std::ffi::c_int;
217 /// use linux_ioctl::*;
218 ///
219 /// const KVMIO: u8 = 0xAE;
220 /// const KVM_CREATE_VM: Ioctl<c_int> = _IO(KVMIO, 0x01).with_arg::<c_int>();
221 ///
222 /// // The `KVM_CREATE_VM` ioctl takes the VM type as an argument. 0 is a reasonable default on
223 /// // most architectures.
224 /// let vm_type: c_int = 0;
225 ///
226 /// let file = File::open("/dev/kvm")?;
227 ///
228 /// let vm_fd = unsafe { KVM_CREATE_VM.ioctl(&file, vm_type)? };
229 /// println!("created new VM with file descriptor {vm_fd}");
230 ///
231 /// unsafe { libc::close(vm_fd) };
232 /// # std::io::Result::Ok(())
233 /// ```
234 pub const fn with_arg<T2>(self) -> Ioctl<T2> {
235 Ioctl {
236 request: self.request,
237 _p: PhantomData,
238 }
239 }
240
241 /// Returns the *ioctl* request code.
242 ///
243 /// This is passed to `ioctl(2)` as its second argument.
244 pub fn request(self) -> u32 {
245 self.request
246 }
247}
248
249impl<T> Ioctl<*const T> {
250 /// Changes the [`Ioctl`] argument type to be passed directly instead of behind a pointer.
251 ///
252 /// Does not change the request code.
253 ///
254 /// Many linux headers define `ioctl`s like `_IOW('U', 100, int)`, but then expect the `int`
255 /// argument to be passed as a direct argument to `ioctl(2)` instead of passing a pointer.
256 /// This method can be used to bind to these `ioctl`s.
257 ///
258 /// # Example
259 ///
260 /// `uinput` defines several `ioctl`s where this method is useful:
261 ///
262 /// ```c
263 /// #define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
264 /// ```
265 ///
266 /// ```
267 /// use std::ffi::c_int;
268 /// use linux_ioctl::{Ioctl, _IOW};
269 ///
270 /// const UI_SET_EVBIT: Ioctl<c_int> = _IOW(b'U', 100).with_direct_arg();
271 /// ```
272 #[inline]
273 pub const fn with_direct_arg(self) -> Ioctl<T> {
274 self.with_arg()
275 }
276}
277
278impl Ioctl<NoArgs> {
279 /// Performs an *ioctl* that doesn't take an argument.
280 ///
281 /// On success, returns the value returned by the `ioctl` syscall. On error (when `ioctl`
282 /// returns -1), returns the error from *errno*.
283 ///
284 /// Note that the actual `ioctl(2)` call performed will pass 0 as a dummy argument to the
285 /// *ioctl*. This is because some Linux *ioctl*s are declared without an argument, but will fail
286 /// unless they receive 0 as their argument (eg. `KVM_GET_API_VERSION`). There should be no harm
287 /// in passing this argument unconditionally, as the kernel will typically just ignore excess
288 /// arguments.
289 ///
290 /// # Safety
291 ///
292 /// This method performs an arbitrary *ioctl* on an arbitrary file descriptor.
293 /// The caller has to ensure that any safety requirements of the *ioctl* are met, and that `fd`
294 /// belongs to the driver it expects.
295 pub unsafe fn ioctl(self, fd: &impl AsRawFd) -> io::Result<c_int> {
296 let res = unsafe { libc::ioctl(fd.as_raw_fd(), self.request as _, 0) };
297 if res == -1 {
298 Err(io::Error::last_os_error())
299 } else {
300 Ok(res)
301 }
302 }
303}
304
305impl<T> Ioctl<T> {
306 /// Performs an *ioctl* that takes an argument of type `T`.
307 ///
308 /// Returns the value returned by the `ioctl(2)` invocation, or an I/O error if the call failed.
309 ///
310 /// For many *ioctl*s, `T` will be a pointer to the actual argument.
311 /// The caller must ensure that it points to valid data that conforms to the requirements of the
312 /// *ioctl*.
313 ///
314 /// # Safety
315 ///
316 /// This method performs an arbitrary *ioctl* on an arbitrary file descriptor.
317 /// The caller has to ensure that any safety requirements of the *ioctl* are met, and that `fd`
318 /// belongs to the driver it expects.
319 pub unsafe fn ioctl(self, fd: &impl AsRawFd, arg: T) -> io::Result<c_int> {
320 let res = unsafe { libc::ioctl(fd.as_raw_fd(), self.request as _, arg) };
321 if res == -1 {
322 Err(io::Error::last_os_error())
323 } else {
324 Ok(res)
325 }
326 }
327}
328
329/// Indicates that an [`Ioctl`] does not take any arguments.
330///
331/// This is used as the type parameter of [`Ioctl`] by the [`_IO`] and [`_IOC`] functions.
332/// [`Ioctl<NoArgs>`] comes with its own, separate `IOCTL.ioctl(fd)` method that only takes the file
333/// descriptor as an argument.
334///
335/// Since [`NoArgs`] is the default value for [`Ioctl`]'s type parameter, it can typically be
336/// omitted.
337///
338/// # Example
339///
340/// The *uinput* ioctls `UI_DEV_CREATE` and `UI_DEV_DESTROY` do not take any arguments, while
341/// `UI_DEV_SETUP` *does* take an argument.
342///
343/// From `linux/uinput.h`:
344///
345/// ```c
346/// /* ioctl */
347/// #define UINPUT_IOCTL_BASE 'U'
348/// #define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
349/// #define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
350/// ...
351/// #define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
352/// ```
353///
354/// ```rust
355/// use std::{mem, fs::File, ffi::c_char};
356/// use libc::uinput_setup;
357/// use linux_ioctl::*;
358///
359/// const UINPUT_IOCTL_BASE: u8 = b'U';
360/// const UI_DEV_CREATE: Ioctl<NoArgs> = _IO(UINPUT_IOCTL_BASE, 1);
361/// const UI_DEV_DESTROY: Ioctl<NoArgs> = _IO(UINPUT_IOCTL_BASE, 2);
362/// const UI_DEV_SETUP: Ioctl<*const uinput_setup> = _IOW(UINPUT_IOCTL_BASE, 3);
363///
364/// let uinput = File::options().write(true).open("/dev/uinput")?;
365///
366/// let mut setup: libc::uinput_setup = unsafe { mem::zeroed() };
367/// setup.name[0] = b'A' as c_char; // (must not be blank)
368/// unsafe {
369/// UI_DEV_SETUP.ioctl(&uinput, &setup)?;
370/// UI_DEV_CREATE.ioctl(&uinput)?;
371/// // ...use the device...
372/// UI_DEV_DESTROY.ioctl(&uinput)?;
373/// }
374/// # std::io::Result::Ok(())
375/// ```
376pub struct NoArgs {
377 // Unsized type so that the `impl<T> Ioctl<T>` does not conflict.
378 _f: [u8],
379}
380
381/// Direction of an [`Ioctl`].
382///
383/// Used by [`_IOC`]. Constructed by using the constants [`_IOC_NONE`], [`_IOC_READ`], and
384/// [`_IOC_WRITE`].
385#[derive(Clone, Copy, PartialEq, Eq)]
386pub struct Dir(u32);
387
388impl BitOr for Dir {
389 type Output = Dir;
390
391 #[inline]
392 fn bitor(self, rhs: Self) -> Self::Output {
393 // `_IOC_NONE` is 0 on x86, but non-zero on other architectures. It is invalid and
394 // non-portable to combine it with other usages, so we prevent it here.
395 // This check will easily optimize out in almost all cases, since the direction is a
396 // compile-time constant.
397 if (self == _IOC_NONE && rhs != _IOC_NONE) || (self != _IOC_NONE && rhs == _IOC_NONE) {
398 panic!("`_IOC_NONE` cannot be combined with other values");
399 }
400
401 Self(self.0 | rhs.0)
402 }
403}
404
405impl fmt::Debug for Dir {
406 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
407 if *self == _IOC_READ | _IOC_WRITE {
408 f.write_str("_IOC_READ | _IOC_WRITE")
409 } else if *self == _IOC_READ {
410 f.write_str("_IOC_READ")
411 } else if *self == _IOC_WRITE {
412 f.write_str("_IOC_WRITE")
413 } else if *self == _IOC_NONE {
414 f.write_str("_IOC_NONE")
415 } else {
416 write!(f, "{:#x}", self.0)
417 }
418 }
419}
420
421/// Indicates that an *ioctl* neither reads nor writes data through its argument.
422pub const _IOC_NONE: Dir = Dir(consts::_IOC_NONE);
423
424/// Indicates that an *ioctl* reads data through its pointer argument.
425pub const _IOC_READ: Dir = Dir(consts::_IOC_READ);
426
427/// Indicates that an *ioctl* writes data through its pointer argument.
428pub const _IOC_WRITE: Dir = Dir(consts::_IOC_WRITE);
429
430/// Indicates that an *ioctl* both reads and writes data through its pointer argument.
431///
432/// Equivalent to `_IOC_READ | _IOC_WRITE`, which doesn't work in `const` contexts. Does not have a
433/// C equivalent.
434pub const _IOC_READ_WRITE: Dir = Dir(consts::_IOC_READ | consts::_IOC_WRITE);
435
436// NB: these are bare `u32`s because `const` `BitOr` impls aren't possible on stable
437// (they're only used with `_IOC`, which is a somewhat niche API)
438
439/// Creates an [`Ioctl`] that doesn't read or write any userspace data.
440///
441/// This type of ioctl can return an `int` to userspace via the return value of the `ioctl` syscall.
442/// By default, the returned [`Ioctl`] takes no argument.
443/// [`Ioctl::with_arg`] can be used to pass a direct argument to the *ioctl*.
444///
445/// # Example
446///
447/// `KVM_GET_API_VERSION` is an *ioctl* that does not take any arguments. The API version is
448/// returned as the return value of the `ioctl(2)` function.
449///
450/// From `linux/kvm.h`:
451///
452/// ```c
453/// #define KVMIO 0xAE
454/// ...
455/// #define KVM_GET_API_VERSION _IO(KVMIO, 0x00)
456/// ```
457///
458/// ```rust
459/// use std::fs::File;
460/// use linux_ioctl::*;
461///
462/// const KVMIO: u8 = 0xAE;
463/// const KVM_GET_API_VERSION: Ioctl<NoArgs> = _IO(KVMIO, 0x00);
464///
465/// let file = File::open("/dev/kvm")?;
466///
467/// let version = unsafe { KVM_GET_API_VERSION.ioctl(&file)? };
468/// println!("KVM API version: {version}");
469/// # std::io::Result::Ok(())
470/// ```
471#[allow(non_snake_case)]
472pub const fn _IO(ty: u8, nr: u8) -> Ioctl<NoArgs> {
473 _IOC(_IOC_NONE, ty, nr, 0)
474}
475
476/// Creates an [`Ioctl`] that reads data of type `T` from the kernel.
477///
478/// A pointer to the data will be passed to `ioctl(2)`, and the kernel will fill the destination
479/// with data.
480///
481/// # Errors
482///
483/// This method will cause a compile-time assertion failure if the size of `T` exceeds the *ioctl*
484/// argument size limit.
485/// This typically means that the wrong type `T` was specified.
486///
487/// # Examples
488///
489/// From `linux/random.h`:
490///
491/// ```c
492/// /* ioctl()'s for the random number generator */
493///
494/// /* Get the entropy count. */
495/// #define RNDGETENTCNT _IOR( 'R', 0x00, int )
496/// ```
497///
498/// ```
499/// use std::fs::File;
500/// use std::ffi::c_int;
501/// use linux_ioctl::*;
502///
503/// const RNDGETENTCNT: Ioctl<*mut c_int> = _IOR(b'R', 0x00);
504///
505/// let file = File::open("/dev/urandom")?;
506///
507/// let mut entropy = 0;
508/// unsafe { RNDGETENTCNT.ioctl(&file, &mut entropy)? };
509///
510/// println!("{entropy} bits of entropy in /dev/urandom");
511/// # std::io::Result::Ok(())
512/// ```
513#[allow(non_snake_case)]
514pub const fn _IOR<T>(ty: u8, nr: u8) -> Ioctl<*mut T> {
515 const {
516 assert!(size_of::<T>() <= (_IOC_SIZEMASK as usize));
517 }
518 _IOC(_IOC_READ, ty, nr, size_of::<T>())
519}
520
521/// Creates an [`Ioctl`] that writes data of type `T` to the kernel.
522///
523/// By default, a pointer to the data will be passed to `ioctl(2)`, and the kernel will read the
524/// argument from that location.
525/// This is generally correct if the argument is a `struct`, but if the argument is a primitive type
526/// like `int`, or is already a pointer like `char*`, many drivers expect the argument to be passed
527/// to `ioctl(2)` *without* indirection.
528/// To bind to those `ioctl`s, you can call [`Ioctl::with_direct_arg`] on the [`Ioctl`] returned by
529/// [`_IOW`].
530///
531/// # Errors
532///
533/// This method will cause a compile-time assertion failure if the size of `T` exceeds the *ioctl*
534/// argument size limit.
535/// This typically means that the wrong type `T` was specified.
536///
537/// # Example
538///
539/// Let's create a virtual input device with *uinput*.
540///
541/// From `linux/uinput.h`:
542///
543/// ```c
544/// /* ioctl */
545/// #define UINPUT_IOCTL_BASE 'U'
546/// #define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
547/// #define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
548/// ...
549/// #define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
550/// ...
551/// #define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
552/// #define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
553/// ```
554///
555/// From `linux/input.h`:
556///
557/// ```c
558/// #define EV_KEY 0x01
559/// ...
560/// #define KEY_A 30
561/// ```
562///
563/// ```rust
564/// use std::{mem, fs::File, ffi::{c_char, c_int}};
565/// use libc::uinput_setup;
566/// use linux_ioctl::*;
567///
568/// const UINPUT_IOCTL_BASE: u8 = b'U';
569/// const UI_DEV_CREATE: Ioctl<NoArgs> = _IO(UINPUT_IOCTL_BASE, 1);
570/// const UI_DEV_DESTROY: Ioctl<NoArgs> = _IO(UINPUT_IOCTL_BASE, 2);
571/// const UI_DEV_SETUP: Ioctl<*const uinput_setup> = _IOW(UINPUT_IOCTL_BASE, 3);
572/// // These two expect their argument to be passed directly instead of behind a pointer:
573/// const UI_SET_EVBIT: Ioctl<c_int> = _IOW(UINPUT_IOCTL_BASE, 100).with_direct_arg();
574/// const UI_SET_KEYBIT: Ioctl<c_int> = _IOW(UINPUT_IOCTL_BASE, 101).with_direct_arg();
575///
576/// const EV_KEY: c_int = 0x01;
577/// const KEY_A: c_int = 30;
578///
579/// let uinput = File::options().write(true).open("/dev/uinput")?;
580///
581/// // Enable the "A" key:
582/// unsafe {
583/// UI_SET_EVBIT.ioctl(&uinput, EV_KEY)?;
584/// UI_SET_KEYBIT.ioctl(&uinput, KEY_A)?;
585/// }
586///
587/// let mut setup: uinput_setup = unsafe { mem::zeroed() };
588/// setup.name[0] = b'A' as c_char; // (must not be blank)
589/// unsafe {
590/// UI_DEV_SETUP.ioctl(&uinput, &setup)?;
591/// UI_DEV_CREATE.ioctl(&uinput)?;
592/// // ...use the device...
593/// UI_DEV_DESTROY.ioctl(&uinput)?;
594/// }
595/// # std::io::Result::Ok(())
596/// ```
597#[allow(non_snake_case)]
598pub const fn _IOW<T>(ty: u8, nr: u8) -> Ioctl<*const T> {
599 const {
600 assert!(size_of::<T>() <= (_IOC_SIZEMASK as usize));
601 }
602 _IOC(_IOC_WRITE, ty, nr, size_of::<T>())
603}
604
605/// Creates an [`Ioctl`] that writes and reads data of type `T`.
606///
607/// A pointer to the data will be passed to `ioctl(2)`, and the kernel will read and write to the
608/// data `T`.
609///
610/// # Errors
611///
612/// This method will cause a compile-time assertion failure if the size of `T` exceeds the *ioctl*
613/// argument size limit.
614/// This typically means that the wrong type `T` was specified.
615#[allow(non_snake_case)]
616pub const fn _IOWR<T>(ty: u8, nr: u8) -> Ioctl<*mut T> {
617 const {
618 assert!(size_of::<T>() <= (_IOC_SIZEMASK as usize));
619 }
620 _IOC(_IOC_READ_WRITE, ty, nr, size_of::<T>())
621}
622
623/// Manually constructs an [`Ioctl`] from its components.
624///
625/// Also see [`Ioctl::from_raw`] for a way to interface with "legacy" ioctls that don't yet follow
626/// this scheme.
627///
628/// Prefer to use [`_IO`], [`_IOR`], [`_IOW`], or [`_IOWR`] where possible.
629///
630/// # Arguments
631///
632/// - **`dir`**: Direction of the ioctl. One of [`_IOC_NONE`], [`_IOC_READ`], [`_IOC_WRITE`], or
633/// `_IOC_READ | _IOC_WRITE` (aka [`_IOC_READ_WRITE`]).
634/// - **`ty`**: the `ioctl` group or type to identify the driver or subsystem. You can find a list
635/// [here].
636/// - **`nr`**: the `ioctl` number within its group.
637/// - **`size`**: the size of the `ioctl`'s indirect argument. `ioctl`s that take an argument
638/// directly (without passing a pointer to it) typically set this to 0.
639///
640/// [here]: https://www.kernel.org/doc/html/latest/userspace-api/ioctl/ioctl-number.html
641///
642/// # Panics
643///
644/// This function may panic when `size` exceeds the (platform-specific) maximum parameter size.
645///
646/// # Example
647///
648/// `UI_GET_SYSNAME` is a polymorphic *ioctl* that can be invoked with a variety of buffer lengths.
649/// This function can be used to bind to it.
650///
651/// From `linux/uinput.h`:
652///
653/// ```c
654/// /* ioctl */
655/// #define UINPUT_IOCTL_BASE 'U'
656/// ...
657/// #define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 44, len)
658/// ```
659///
660/// ```no_run
661/// use std::ffi::c_char;
662/// use linux_ioctl::*;
663///
664/// const UINPUT_IOCTL_BASE: u8 = b'U';
665/// const fn UI_GET_SYSNAME(len: usize) -> Ioctl<*mut c_char> {
666/// _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 44, len)
667/// }
668///
669/// // Use it like this:
670/// unsafe {
671/// # let fd = &123;
672/// let mut buffer = [0 as c_char; 16];
673/// UI_GET_SYSNAME(16).ioctl(fd, buffer.as_mut_ptr())?;
674/// }
675/// # std::io::Result::Ok(())
676/// ```
677#[allow(non_snake_case)]
678#[inline]
679pub const fn _IOC<T: ?Sized>(dir: Dir, ty: u8, nr: u8, size: usize) -> Ioctl<T> {
680 use consts::*;
681
682 assert!(size <= (_IOC_SIZEMASK as usize));
683
684 let request = (dir.0 << _IOC_DIRSHIFT)
685 | ((ty as u32) << _IOC_TYPESHIFT)
686 | ((nr as u32) << _IOC_NRSHIFT)
687 | ((size as u32) << _IOC_SIZESHIFT);
688 Ioctl::from_raw(request)
689}
690
691#[cfg(test)]
692mod tests {
693 use super::*;
694
695 #[test]
696 fn dir_or() {
697 assert_ne!(_IOC_NONE, _IOC_READ);
698 assert_ne!(_IOC_NONE, _IOC_WRITE);
699
700 assert_eq!(_IOC_READ | _IOC_WRITE, _IOC_READ_WRITE);
701 assert_eq!(_IOC_READ | _IOC_READ, _IOC_READ);
702 assert_eq!(_IOC_WRITE | _IOC_WRITE, _IOC_WRITE);
703 assert_eq!(_IOC_NONE | _IOC_NONE, _IOC_NONE);
704 }
705
706 #[test]
707 #[should_panic(expected = "`_IOC_NONE` cannot be combined with other values")]
708 fn dir_none_or_read() {
709 let _ = _IOC_NONE | _IOC_READ;
710 }
711
712 #[test]
713 #[should_panic(expected = "`_IOC_NONE` cannot be combined with other values")]
714 fn dir_none_or_write() {
715 let _ = _IOC_NONE | _IOC_WRITE;
716 }
717
718 #[test]
719 #[should_panic(expected = "`_IOC_NONE` cannot be combined with other values")]
720 fn dir_read_or_none() {
721 let _ = _IOC_READ | _IOC_NONE;
722 }
723
724 #[test]
725 #[should_panic(expected = "`_IOC_NONE` cannot be combined with other values")]
726 fn dir_write_or_none() {
727 let _ = _IOC_WRITE | _IOC_NONE;
728 }
729}