1use nix::sys::mman;
2
3const CONTROL_BLOCK_ADDRESS : i64 = 0x3f200000;
4const CONTROL_BLOCK_SIZE : usize = 0x00000100;
5
6pub enum Reg {
7 GPFSEL0 = 0x00,
8 GPFSEL1 = 0x04,
9 GPFSEL2 = 0x08,
10 GPFSEL3 = 0x0C,
11 GPFSEL4 = 0x10,
12 GPFSEL5 = 0x14,
13
14 GPSET0 = 0x1C,
15 GPSET1 = 0x20,
16
17 GPCLR0 = 0x28,
18 GPCLR1 = 0x2C,
19
20 GPLEV0 = 0x34,
21 GPLEV1 = 0x38,
22
23 GPPEDS0 = 0x40,
24 GPPEDS1 = 0x44,
25
26 GPREN0 = 0x4C,
27 GPREN1 = 0x50,
28
29 GPFEN0 = 0x58,
30 GPFEN1 = 0x5C,
31
32 GPHEN0 = 0x64,
33 GPHEN1 = 0x68,
34
35 GPLEN0 = 0x70,
36 GPLEN1 = 0x74,
37
38 GPAREN0 = 0x7C,
39 GPAREN1 = 0x80,
40
41 GPAFEN0 = 0x88,
42 GPAFEN1 = 0x8C,
43
44 GPPUD = 0x94,
45 GPPUDCLK0 = 0x98,
46 GPPUDCLK1 = 0x9C,
47}
48
49pub struct Rpio {
50 control_block: *mut std::ffi::c_void,
51}
52
53impl Rpio {
54 pub fn new() -> nix::Result<Rpio> {
55 use nix::{fcntl::OFlag, sys::stat::Mode};
56
57 let fd = nix::fcntl::open("/dev/mem", OFlag::O_CLOEXEC | OFlag::O_RDONLY, Mode::empty())?;
58 let control_block = unsafe { mman::mmap(std::ptr::null_mut(), CONTROL_BLOCK_SIZE, mman::ProtFlags::PROT_READ, mman::MapFlags::MAP_SHARED, fd, CONTROL_BLOCK_ADDRESS)? };
59 drop(nix::unistd::close(fd));
60
61 Ok(Self {
62 control_block
63 })
64 }
65
66 pub fn read_all(&self) -> RpioState {
67 let address = self.control_block as *const [u32; 0x100];
68 RpioState::from_data(unsafe { std::ptr::read_volatile(address) })
69 }
70
71 pub fn read_register(&self, reg: Reg) -> u32 {
72 unsafe { std::ptr::read_volatile(self.register_address(reg)) }
73 }
74
75 pub fn write_register(&mut self, reg: Reg, value: u32) {
76 unsafe { std::ptr::write_volatile(self.register_address_mut(reg), value) }
77 }
78
79 fn register_address(&self, reg: Reg) -> *const u32 {
80 self.control_block.wrapping_add(reg as usize) as *const u32
81 }
82
83 fn register_address_mut(&self, reg: Reg) -> *mut u32 {
84 self.control_block.wrapping_add(reg as usize) as *mut u32
85 }
86}
87
88impl Drop for Rpio {
89 fn drop(&mut self) {
90 unsafe {
91 drop(mman::munmap(self.control_block, CONTROL_BLOCK_SIZE))
92 }
93 }
94}
95
96pub struct RpioState {
97 data: [u32; 0x100],
98}
99
100impl RpioState {
101 pub fn from_data(data: [u32; 0x100]) -> Self {
102 Self { data }
103 }
104
105 pub fn data(&self) -> &[u32; 0x100] {
106 &self.data
107 }
108
109 pub fn into_data(self) -> [u32; 0x100] {
110 self.data
111 }
112
113 pub fn pin_mode(&self, index: u32) -> PinMode {
114 PinMode::try_from_bits(self.read_pin_bits(index, Reg::GPFSEL0, 10, 3) as u8).unwrap()
115 }
116
117 pub fn pin_level(&self, index: u32) -> bool {
118 self.read_pin_bits(index, Reg::GPLEV0, 32, 1) != 0
119 }
120
121 pub fn pin_event(&self, index: u32) -> bool {
122 self.read_pin_bits(index, Reg::GPPEDS0, 32, 1) != 0
123 }
124
125 pub fn pin_detect_rise(&self, index: u32) -> bool {
126 self.read_pin_bits(index, Reg::GPREN0, 32, 1) != 0
127 }
128
129 pub fn pin_detect_fall(&self, index: u32) -> bool {
130 self.read_pin_bits(index, Reg::GPFEN0, 32, 1) != 0
131 }
132
133 pub fn pin_detect_high(&self, index: u32) -> bool {
134 self.read_pin_bits(index, Reg::GPHEN0, 32, 1) != 0
135 }
136
137 pub fn pin_detect_low(&self, index: u32) -> bool {
138 self.read_pin_bits(index, Reg::GPLEN0, 32, 1) != 0
139 }
140
141 pub fn pin_detect_async_rise(&self, index: u32) -> bool {
142 self.read_pin_bits(index, Reg::GPAREN0, 32, 1) != 0
143 }
144
145 pub fn pin_detect_async_fall(&self, index: u32) -> bool {
146 self.read_pin_bits(index, Reg::GPAFEN0, 32, 1) != 0
147 }
148
149 pub fn pin(&self, index: u32) -> PinInfo {
150 PinInfo {
151 mode: self.pin_mode(index),
152 level: self.pin_level(index),
153 event: self.pin_event(index),
154 detect_rise: self.pin_detect_rise(index),
155 detect_fall: self.pin_detect_fall(index),
156 detect_high: self.pin_detect_high(index),
157 detect_low: self.pin_detect_low(index),
158 detect_async_rise: self.pin_detect_async_rise(index),
159 detect_async_fall: self.pin_detect_async_fall(index),
160 }
161 }
162
163 pub fn pins(&self) -> Vec<PinInfo> {
164 (0..53).map(|i| self.pin(i)).collect()
165 }
166
167 fn read_pin_bits(&self, index: u32, base: Reg, pins_per_register: u8, bits_per_pin: u8) -> u32 {
168 assert!(index <= 53, "gpio pin index out of range, expected a value in the range [0-53], got {}", index);
169
170 let pins_per_register = pins_per_register as u32;
171 let bits_per_pin = bits_per_pin as u32;
172
173 let base = base as u32 / 4;
176 let register_index = base + index / pins_per_register;
177 let index = index % pins_per_register;
178
179 let value = self.data[register_index as usize] >> (bits_per_pin * index);
180 let mask = !(std::u32::MAX << bits_per_pin);
181 value & mask
182 }
183}
184
185#[derive(Copy, Clone, Debug, Eq, PartialEq)]
186pub enum PinMode {
187 Input,
188 Output,
189 Alt0,
190 Alt1,
191 Alt2,
192 Alt3,
193 Alt4,
194 Alt5,
195}
196
197impl PinMode {
198 pub fn try_from_bits(bits: u8) -> Result<Self, ()> {
199 match bits {
200 0b000 => Ok(PinMode::Input),
201 0b001 => Ok(PinMode::Output),
202 0b100 => Ok(PinMode::Alt0),
203 0b101 => Ok(PinMode::Alt1),
204 0b110 => Ok(PinMode::Alt2),
205 0b111 => Ok(PinMode::Alt3),
206 0b011 => Ok(PinMode::Alt4),
207 0b010 => Ok(PinMode::Alt5),
208 _ => Err(())
209 }
210 }
211}
212
213#[derive(Clone, Debug, Eq, PartialEq)]
214pub struct PinInfo {
215 pub mode: PinMode,
216 pub level: bool,
217 pub event: bool,
218 pub detect_rise: bool,
219 pub detect_fall: bool,
220 pub detect_high: bool,
221 pub detect_low: bool,
222 pub detect_async_rise: bool,
223 pub detect_async_fall: bool,
224}