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: u32 = 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))]
55mod consts {
56 use super::RawOpcode;
57
58 pub(super) const NONE: RawOpcode = 0;
59 pub(super) const READ: RawOpcode = 2;
60 pub(super) const WRITE: RawOpcode = 1;
61 pub(super) const SIZE_BITS: RawOpcode = 14;
62 pub(super) const DIR_BITS: RawOpcode = 2;
63}
64
65#[cfg(any(
66 target_arch = "mips",
67 target_arch = "mips32r6",
68 target_arch = "mips64",
69 target_arch = "mips64r6",
70 target_arch = "powerpc",
71 target_arch = "powerpc64",
72 target_arch = "sparc",
73 target_arch = "sparc64"
74))]
75mod consts {
76 use super::RawOpcode;
77
78 pub(super) const NONE: RawOpcode = 1;
79 pub(super) const READ: RawOpcode = 2;
80 pub(super) const WRITE: RawOpcode = 4;
81 pub(super) const SIZE_BITS: RawOpcode = 13;
82 pub(super) const DIR_BITS: RawOpcode = 3;
83}
84
85#[cfg(not(any(
86 // These have no ioctl opcodes defined in linux_raw_sys
87 // so can't use that as a known-good value for this test.
88 target_arch = "sparc",
89 target_arch = "sparc64"
90)))]
91#[test]
92fn check_known_opcodes() {
93 use crate::backend::c::{c_long, c_uint};
94 use core::mem::size_of;
95
96 // _IOR('U', 15, unsigned int)
97 assert_eq!(
98 compose_opcode(
99 Direction::Read,
100 b'U' as RawOpcode,
101 15,
102 size_of::<c_uint>() as RawOpcode
103 ),
104 linux_raw_sys::ioctl::USBDEVFS_CLAIMINTERFACE as RawOpcode
105 );
106
107 // _IOW('v', 2, long)
108 assert_eq!(
109 compose_opcode(
110 Direction::Write,
111 b'v' as RawOpcode,
112 2,
113 size_of::<c_long>() as RawOpcode
114 ),
115 linux_raw_sys::ioctl::FS_IOC_SETVERSION as RawOpcode
116 );
117}
118