dht_mmap_rust/mmap_gpio.rs
1use memmap::MmapMut;
2use std::fs::{File, OpenOptions};
3use std::os::unix::prelude::OpenOptionsExt;
4
5/*
6
7This file contains a helper struct to access gpio pins directly via memory mapped IO
8
9*/
10
11/// Base memory location in /dev/gpiomem - only used for calculating GPIO_BASE
12const BASE: u64 = 0x20000000;
13
14/// Base memory location of the Gpio memmap part if /dev/gpiomem
15const GPIO_BASE: u64 = BASE + 0x200000;
16
17/// Size of the GPIO memmapped region
18const GPIO_LENGTH: usize = 4096;
19
20/// Direct Gpio access using mmap. Unmaps the mmapped region on drop.
21/// The functions for gpio access are "unsafe", because the pin number is used as a memory offset.
22pub(crate) struct GpioMmapAccess {
23 _file: File,
24 _map_struct: MmapMut,
25 map: *mut u32,
26}
27
28/// Any type of error that happens when attempting to first access GPIO via mmap
29#[derive(Debug)]
30pub enum GpioOpenError {
31 /// In order to function, the file `/dev/gpiomem` needs to be opened. This error means that that did
32 /// not work. The most likely reason is that the program is not run as root / has access to the file
33 /// or that the program has been run on a device that is not a raspberry pi.
34 OpenGpioFileFailed(std::io::Error),
35 /// An Error happened in trying to map the already opened File to memory. See contained io::Error
36 /// for more information.
37 CreateMmapError(std::io::Error),
38}
39
40impl GpioMmapAccess {
41 /// Create a new `GpioMmapAccess`. Opens the `/dev/gpiomem` linux file and mmaps the region responsible for gpio pins.
42 pub fn new() -> Result<Self, GpioOpenError> {
43 let gpiomem = OpenOptions::new()
44 .read(true)
45 .write(true)
46 .custom_flags(libc::O_SYNC)
47 .open("/dev/gpiomem")
48 .map_err(|err| GpioOpenError::OpenGpioFileFailed(err))?;
49
50 let mut mmap_mut = unsafe {
51 memmap::MmapOptions::new()
52 .len(GPIO_LENGTH)
53 .offset(GPIO_BASE)
54 .map_mut(&gpiomem)
55 .map_err(|err| GpioOpenError::CreateMmapError(err))?
56 };
57
58 let raw_pointer = mmap_mut.as_mut_ptr() as *mut u32;
59
60 return Ok(Self {
61 _file: gpiomem,
62 _map_struct: mmap_mut,
63 map: raw_pointer,
64 });
65 }
66}
67
68impl GpioMmapAccess {
69 /// Set the given pin to INPUT mode
70 /// Safety: the pin number has to be valid. Invalid pin numbers may result in undefined behavior.
71 pub unsafe fn pi_mmio_set_input(&mut self, pin: usize) {
72 let address = self.map.add(pin / 10);
73 let old_val = address.read_volatile();
74 let new_val = old_val & !(7 << ((pin % 10) * 3));
75
76 address.write_volatile(new_val);
77 }
78
79 /// Set the given pin to OUTPUT mode
80 /// Safety: the pin number has to be valid. Invalid pin numbers may result in undefined behavior.
81 pub unsafe fn pi_mmio_set_output(&mut self, pin: usize) {
82 self.pi_mmio_set_input(pin);
83
84 let address = self.map.add(pin / 10);
85 let old_val = address.read_volatile();
86 let new_val = old_val | (1 << ((pin % 10) * 3));
87 address.write_volatile(new_val);
88 }
89
90 /// Set the given pin to HIGH to output a voltage. Assumes that the pin is in OUTPUT mode.
91 /// Safety: the pin number has to be valid. Invalid pin numbers may result in undefined behavior.
92 pub unsafe fn pi_mmio_set_high(&mut self, pin: usize) {
93 self.map.add(7).write_volatile(1 << pin);
94 }
95
96 /// Set the given pin to LOW to output a voltage. Assumes that the pin is in OUTPUT mode.
97 /// Safety: the pin number has to be valid. Invalid pin numbers may result in undefined behavior.
98 pub unsafe fn pi_mmio_set_low(&mut self, pin: usize) {
99 self.map.add(10).write_volatile(1 << pin);
100 }
101
102 /// Read the value of the given pin
103 /// Safety: the pin number has to be valid. Invalid pin numbers may result in undefined behavior.
104 pub unsafe fn pi_mmio_input(&mut self, pin: usize) -> bool {
105 (self.map.add(13).read_volatile() & (1 << pin)) != 0
106 }
107}