rpi_gpio/
lib.rs

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		// Register has a relative byte address,
174		// but registers are 32 bit.
175		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}