1 //! `ioctl` opcode behavior for Linux platforms.
2 
3 use super::{Direction, RawOpcode};
4 use consts::*;
5 
6 /// Compose an opcode from its component parts.
compose_opcode( dir: Direction, group: RawOpcode, num: RawOpcode, size: RawOpcode, ) -> RawOpcode7 pub(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 
32 const NUM_BITS: RawOpcode = 8;
33 const GROUP_BITS: RawOpcode = 8;
34 
35 const NUM_SHIFT: RawOpcode = 0;
36 const GROUP_SHIFT: RawOpcode = NUM_SHIFT + NUM_BITS;
37 const SIZE_SHIFT: RawOpcode = GROUP_SHIFT + GROUP_BITS;
38 const DIR_SHIFT: RawOpcode = SIZE_SHIFT + SIZE_BITS;
39 
40 const NUM_MASK: RawOpcode = (1 << NUM_BITS) - 1;
41 const GROUP_MASK: RawOpcode = (1 << GROUP_BITS) - 1;
42 const SIZE_MASK: RawOpcode = (1 << SIZE_BITS) - 1;
43 const 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 ))]
56 mod 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 ))]
76 mod 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
88     // so can't use that as a known-good value for this test.
89     target_arch = "sparc",
90     target_arch = "sparc64"
91 )))]
92 #[test]
check_known_opcodes()93 fn 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 }
119