base/sys/linux/
ioctl.rs

1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Macros and wrapper functions for dealing with ioctls.
6
7// Allow missing safety comments because this file provides just thin helper functions for
8// `libc::ioctl`. Their safety follows `libc::ioctl`'s safety.
9#![allow(clippy::missing_safety_doc)]
10
11use std::os::raw::c_int;
12use std::os::raw::c_uint;
13use std::os::raw::c_ulong;
14use std::os::raw::c_void;
15
16use crate::descriptor::AsRawDescriptor;
17
18/// Raw macro to declare the expression that calculates an ioctl number
19#[macro_export]
20macro_rules! ioctl_expr {
21    ($dir:expr, $ty:expr, $nr:expr, $size:expr) => {
22        ((($dir as $crate::linux::IoctlNr) << $crate::linux::ioctl::_IOC_DIRSHIFT)
23            | (($ty as $crate::linux::IoctlNr) << $crate::linux::ioctl::_IOC_TYPESHIFT)
24            | (($nr as $crate::linux::IoctlNr) << $crate::linux::ioctl::_IOC_NRSHIFT)
25            | (($size as $crate::linux::IoctlNr) << $crate::linux::ioctl::_IOC_SIZESHIFT))
26    };
27}
28
29/// Raw macro to declare a constant or a function that returns an ioctl number.
30#[macro_export]
31macro_rules! ioctl_ioc_nr {
32    ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr) => {
33        #[allow(non_snake_case)]
34        /// Constant ioctl request number.
35        pub const $name: $crate::linux::IoctlNr = $crate::ioctl_expr!($dir, $ty, $nr, $size);
36    };
37    ($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr, $($v:ident),+) => {
38        #[allow(non_snake_case)]
39        /// Generates ioctl request number.
40        pub const fn $name($($v: ::std::os::raw::c_uint),+) -> $crate::linux::IoctlNr {
41            $crate::ioctl_expr!($dir, $ty, $nr, $size)
42        }
43    };
44}
45
46/// Declare an ioctl that transfers no data.
47#[macro_export]
48macro_rules! ioctl_io_nr {
49    ($name:ident, $ty:expr, $nr:expr) => {
50        $crate::ioctl_ioc_nr!($name, $crate::linux::ioctl::_IOC_NONE, $ty, $nr, 0);
51    };
52    ($name:ident, $ty:expr, $nr:expr, $($v:ident),+) => {
53        $crate::ioctl_ioc_nr!($name, $crate::linux::ioctl::_IOC_NONE, $ty, $nr, 0, $($v),+);
54    };
55}
56
57/// Declare an ioctl that reads data.
58#[macro_export]
59macro_rules! ioctl_ior_nr {
60    ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
61        $crate::ioctl_ioc_nr!(
62            $name,
63            $crate::linux::ioctl::_IOC_READ,
64            $ty,
65            $nr,
66            ::std::mem::size_of::<$size>() as u32
67        );
68    };
69    ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
70        $crate::ioctl_ioc_nr!(
71            $name,
72            $crate::linux::ioctl::_IOC_READ,
73            $ty,
74            $nr,
75            ::std::mem::size_of::<$size>() as u32,
76            $($v),+
77        );
78    };
79}
80
81/// Declare an ioctl that writes data.
82#[macro_export]
83macro_rules! ioctl_iow_nr {
84    ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
85        $crate::ioctl_ioc_nr!(
86            $name,
87            $crate::linux::ioctl::_IOC_WRITE,
88            $ty,
89            $nr,
90            ::std::mem::size_of::<$size>() as u32
91        );
92    };
93    ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
94        $crate::ioctl_ioc_nr!(
95            $name,
96            $crate::linux::ioctl::_IOC_WRITE,
97            $ty,
98            $nr,
99            ::std::mem::size_of::<$size>() as u32,
100            $($v),+
101        );
102    };
103}
104
105/// Declare an ioctl that reads and writes data.
106#[macro_export]
107macro_rules! ioctl_iowr_nr {
108    ($name:ident, $ty:expr, $nr:expr, $size:ty) => {
109        $crate::ioctl_ioc_nr!(
110            $name,
111            $crate::linux::ioctl::_IOC_READ | $crate::linux::ioctl::_IOC_WRITE,
112            $ty,
113            $nr,
114            ::std::mem::size_of::<$size>() as u32
115        );
116    };
117    ($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
118        $crate::ioctl_ioc_nr!(
119            $name,
120            $crate::linux::ioctl::_IOC_READ | $crate::linux::ioctl::_IOC_WRITE,
121            $ty,
122            $nr,
123            ::std::mem::size_of::<$size>() as u32,
124            $($v),+
125        );
126    };
127}
128
129pub const _IOC_NRBITS: c_uint = 8;
130pub const _IOC_TYPEBITS: c_uint = 8;
131pub const _IOC_SIZEBITS: c_uint = 14;
132pub const _IOC_DIRBITS: c_uint = 2;
133pub const _IOC_NRMASK: c_uint = 255;
134pub const _IOC_TYPEMASK: c_uint = 255;
135pub const _IOC_SIZEMASK: c_uint = 16383;
136pub const _IOC_DIRMASK: c_uint = 3;
137pub const _IOC_NRSHIFT: c_uint = 0;
138pub const _IOC_TYPESHIFT: c_uint = 8;
139pub const _IOC_SIZESHIFT: c_uint = 16;
140pub const _IOC_DIRSHIFT: c_uint = 30;
141pub const _IOC_NONE: c_uint = 0;
142pub const _IOC_WRITE: c_uint = 1;
143pub const _IOC_READ: c_uint = 2;
144pub const IOC_IN: c_uint = 1_073_741_824;
145pub const IOC_OUT: c_uint = 2_147_483_648;
146pub const IOC_INOUT: c_uint = 3_221_225_472;
147pub const IOCSIZE_MASK: c_uint = 1_073_676_288;
148pub const IOCSIZE_SHIFT: c_uint = 16;
149
150#[cfg(any(target_os = "android", target_env = "musl"))]
151pub type IoctlNr = c_int;
152#[cfg(not(any(target_os = "android", target_env = "musl")))]
153pub type IoctlNr = c_ulong;
154
155/// Run an ioctl with no arguments.
156/// # Safety
157/// The caller is responsible for determining the safety of the particular ioctl.
158pub unsafe fn ioctl<F: AsRawDescriptor>(descriptor: &F, nr: IoctlNr) -> c_int {
159    libc::ioctl(descriptor.as_raw_descriptor(), nr, 0)
160}
161
162/// Run an ioctl with a single value argument.
163/// # Safety
164/// The caller is responsible for determining the safety of the particular ioctl.
165pub unsafe fn ioctl_with_val(descriptor: &dyn AsRawDescriptor, nr: IoctlNr, arg: c_ulong) -> c_int {
166    libc::ioctl(descriptor.as_raw_descriptor(), nr, arg)
167}
168
169/// Run an ioctl with an immutable reference.
170/// # Safety
171///
172/// The caller is responsible for determining the safety of the particular ioctl.
173pub unsafe fn ioctl_with_ref<T>(descriptor: &dyn AsRawDescriptor, nr: IoctlNr, arg: &T) -> c_int {
174    libc::ioctl(
175        descriptor.as_raw_descriptor(),
176        nr,
177        arg as *const T as *const c_void,
178    )
179}
180
181/// Run an ioctl with a mutable reference.
182/// # Safety
183///
184/// The caller is responsible for determining the safety of the particular ioctl.
185pub unsafe fn ioctl_with_mut_ref<T>(
186    descriptor: &dyn AsRawDescriptor,
187    nr: IoctlNr,
188    arg: &mut T,
189) -> c_int {
190    libc::ioctl(
191        descriptor.as_raw_descriptor(),
192        nr,
193        arg as *mut T as *mut c_void,
194    )
195}
196
197/// Run an ioctl with a raw pointer.
198/// # Safety
199/// The caller is responsible for determining the safety of the particular ioctl.
200pub unsafe fn ioctl_with_ptr<T>(
201    descriptor: &dyn AsRawDescriptor,
202    nr: IoctlNr,
203    arg: *const T,
204) -> c_int {
205    libc::ioctl(descriptor.as_raw_descriptor(), nr, arg as *const c_void)
206}
207
208/// Run an ioctl with a mutable raw pointer.
209/// # Safety
210/// The caller is responsible for determining the safety of the particular ioctl.
211pub unsafe fn ioctl_with_mut_ptr<T>(
212    descriptor: &dyn AsRawDescriptor,
213    nr: IoctlNr,
214    arg: *mut T,
215) -> c_int {
216    libc::ioctl(descriptor.as_raw_descriptor(), nr, arg as *mut c_void)
217}
218#[cfg(test)]
219mod tests {
220    const TUNTAP: ::std::os::raw::c_uint = 0x54;
221    const VHOST: ::std::os::raw::c_uint = 0xaf;
222    const EVDEV: ::std::os::raw::c_uint = 0x45;
223
224    ioctl_io_nr!(VHOST_SET_OWNER, VHOST, 0x01);
225    ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint);
226    ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int);
227    ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, ::std::os::raw::c_int);
228
229    ioctl_ior_nr!(EVIOCGBIT, EVDEV, 0x20 + evt, [u8; 128], evt);
230    ioctl_io_nr!(FAKE_IOCTL_2_ARG, EVDEV, 0x01 + x + y, x, y);
231
232    #[test]
233    fn ioctl_macros() {
234        assert_eq!(0x0000af01, VHOST_SET_OWNER);
235        assert_eq!(0x800454cf, TUNGETFEATURES);
236        assert_eq!(0x400454d9, TUNSETQUEUE);
237        assert_eq!(0xc004af12, VHOST_GET_VRING_BASE);
238
239        assert_eq!(0x80804522, EVIOCGBIT(2));
240        assert_eq!(0x00004509, FAKE_IOCTL_2_ARG(3, 5));
241    }
242}