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 = 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] |
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 | |