rustix/ioctl/
linux.rs

1//! `ioctl` opcode behavior for Linux platforms.
2
3use super::{Direction, RawOpcode};
4use consts::*;
5
6/// Compose an opcode from its component parts.
7pub(super) const fn compose_opcode(
8    dir: Direction,
9    group: RawOpcode,
10    num: RawOpcode,
11    size: RawOpcode,
12) -> RawOpcode {
13    macro_rules! mask_and_shift {
14        ($val:expr, $shift:expr, $mask:expr) => {{
15            ($val & $mask) << $shift
16        }};
17    }
18
19    let dir = match dir {
20        Direction::None => NONE,
21        Direction::Read => READ,
22        Direction::Write => WRITE,
23        Direction::ReadWrite => READ | WRITE,
24    };
25
26    mask_and_shift!(group, GROUP_SHIFT, GROUP_MASK)
27        | mask_and_shift!(num, NUM_SHIFT, NUM_MASK)
28        | mask_and_shift!(size, SIZE_SHIFT, SIZE_MASK)
29        | mask_and_shift!(dir, DIR_SHIFT, DIR_MASK)
30}
31
32const NUM_BITS: RawOpcode = 8;
33const GROUP_BITS: RawOpcode = 8;
34
35const NUM_SHIFT: RawOpcode = 0;
36const GROUP_SHIFT: RawOpcode = NUM_SHIFT + NUM_BITS;
37const SIZE_SHIFT: RawOpcode = GROUP_SHIFT + GROUP_BITS;
38const DIR_SHIFT: RawOpcode = SIZE_SHIFT + SIZE_BITS;
39
40const NUM_MASK: RawOpcode = (1 << NUM_BITS) - 1;
41const GROUP_MASK: RawOpcode = (1 << GROUP_BITS) - 1;
42const SIZE_MASK: RawOpcode = (1 << SIZE_BITS) - 1;
43const DIR_MASK: RawOpcode = (1 << DIR_BITS) - 1;
44
45#[cfg(any(
46    target_arch = "x86",
47    target_arch = "arm",
48    target_arch = "s390x",
49    target_arch = "x86_64",
50    target_arch = "aarch64",
51    target_arch = "riscv32",
52    target_arch = "riscv64",
53    target_arch = "loongarch64",
54    target_arch = "csky"
55))]
56mod consts {
57    use super::RawOpcode;
58
59    pub(super) const NONE: RawOpcode = 0;
60    pub(super) const READ: RawOpcode = 2;
61    pub(super) const WRITE: RawOpcode = 1;
62    pub(super) const SIZE_BITS: RawOpcode = 14;
63    pub(super) const DIR_BITS: RawOpcode = 2;
64}
65
66#[cfg(any(
67    target_arch = "mips",
68    target_arch = "mips32r6",
69    target_arch = "mips64",
70    target_arch = "mips64r6",
71    target_arch = "powerpc",
72    target_arch = "powerpc64",
73    target_arch = "sparc",
74    target_arch = "sparc64"
75))]
76mod consts {
77    use super::RawOpcode;
78
79    pub(super) const NONE: RawOpcode = 1;
80    pub(super) const READ: RawOpcode = 2;
81    pub(super) const WRITE: RawOpcode = 4;
82    pub(super) const SIZE_BITS: RawOpcode = 13;
83    pub(super) const DIR_BITS: RawOpcode = 3;
84}
85
86#[cfg(not(any(
87    // These have no ioctl opcodes defined in linux_raw_sys so we can't use
88    // that as a known-good value for this test.
89    target_arch = "sparc",
90    target_arch = "sparc64"
91)))]
92#[test]
93fn check_known_opcodes() {
94    use crate::backend::c::{c_long, c_uint};
95    use core::mem::size_of;
96
97    // _IOR('U', 15, unsigned int)
98    assert_eq!(
99        compose_opcode(
100            Direction::Read,
101            b'U' as RawOpcode,
102            15,
103            size_of::<c_uint>() as RawOpcode
104        ),
105        linux_raw_sys::ioctl::USBDEVFS_CLAIMINTERFACE as RawOpcode
106    );
107
108    // _IOW('v', 2, long)
109    assert_eq!(
110        compose_opcode(
111            Direction::Write,
112            b'v' as RawOpcode,
113            2,
114            size_of::<c_long>() as RawOpcode
115        ),
116        linux_raw_sys::ioctl::FS_IOC_SETVERSION as RawOpcode
117    );
118}