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