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}