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. |
7 | 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: 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 | |
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 | ))] |
55 | mod 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 | ))] |
75 | mod 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 ] |
92 | fn 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 | |