Skip to content

Commit

Permalink
Use a different gpiomem cdev depending on the Pi model
Browse files Browse the repository at this point in the history
  • Loading branch information
golemparts committed Oct 12, 2023
1 parent 45817d1 commit 95ca188
Show file tree
Hide file tree
Showing 8 changed files with 490 additions and 76 deletions.
2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,3 @@ simple-signal = "1.1.1"
default = []
hal = ["nb", "embedded-hal", "embedded-hal-nb", "embedded-hal-0", "spin_sleep", "void"]
hal-unproven = ["nb", "embedded-hal-0/unproven", "hal"]
# gpiochip v2 API support is a work in progress, and should only be used for testing purposes.
gpiochip_v2 = []
8 changes: 4 additions & 4 deletions src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ use std::sync::{Arc, Mutex, Once, Weak};
use std::time::Duration;

mod epoll;
mod gpiomem;
#[cfg(feature = "hal")]
mod hal;
#[cfg(feature = "hal-unproven")]
mod hal_unproven;
mod interrupt;
mod ioctl;
mod mem;
mod pin;
mod soft_pwm;

Expand Down Expand Up @@ -331,7 +331,7 @@ impl fmt::Display for Trigger {
// Store Gpio's state separately, so we can conveniently share it through
// a cloned Arc.
pub(crate) struct GpioState {
gpio_mem: mem::GpioMem,
gpio_mem: Box<dyn gpiomem::GpioRegisters>,
cdev: std::fs::File,
sync_interrupts: Mutex<interrupt::EventLoop>,
pins_taken: [AtomicBool; u8::MAX as usize],
Expand Down Expand Up @@ -385,7 +385,7 @@ impl Gpio {
inner: state.clone(),
})
} else {
let gpio_mem = mem::GpioMem::open()?;
let gpio_mem = gpiomem::bcm::GpioMem::open()?;
let cdev = ioctl::find_gpiochip()?;
let sync_interrupts = Mutex::new(interrupt::EventLoop::new(
cdev.as_raw_fd(),
Expand All @@ -397,7 +397,7 @@ impl Gpio {
.gpio_lines();

let gpio_state = Arc::new(GpioState {
gpio_mem,
gpio_mem: Box::new(gpio_mem),
cdev,
sync_interrupts,
pins_taken,
Expand Down
13 changes: 13 additions & 0 deletions src/gpio/gpiomem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use crate::gpio::{Level, Mode, PullUpDown};

pub mod bcm;
pub mod rp1;

pub(crate) trait GpioRegisters: std::fmt::Debug + Sync + Send {
fn set_high(&self, pin: u8);
fn set_low(&self, pin: u8);
fn level(&self, pin: u8) -> Level;
fn mode(&self, pin: u8) -> Mode;
fn set_mode(&self, pin: u8, mode: Mode);
fn set_pullupdown(&self, pin: u8, pud: PullUpDown);
}
33 changes: 18 additions & 15 deletions src/gpio/mem.rs → src/gpio/gpiomem/bcm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::time::Duration;

use libc::{self, c_void, off_t, size_t, MAP_FAILED, MAP_SHARED, O_SYNC, PROT_READ, PROT_WRITE};

use crate::gpio::gpiomem::GpioRegisters;
use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::{DeviceInfo, SoC};

Expand All @@ -25,7 +26,7 @@ const GPCLR0: usize = 0x28 / std::mem::size_of::<u32>();
const GPLEV0: usize = 0x34 / std::mem::size_of::<u32>();
const GPPUD: usize = 0x94 / std::mem::size_of::<u32>();
const GPPUDCLK0: usize = 0x98 / std::mem::size_of::<u32>();
// Only available on BCM2711 (RPi4) and BCM2712 (RPi5).
// Only available on BCM2711 (RPi4)
const GPPUD_CNTRL_REG0: usize = 0xe4 / std::mem::size_of::<u32>();

pub struct GpioMem {
Expand Down Expand Up @@ -150,41 +151,51 @@ impl GpioMem {
ptr::write_volatile(self.mem_ptr.add(offset), value);
}
}
}

impl Drop for GpioMem {
fn drop(&mut self) {
unsafe {
libc::munmap(self.mem_ptr as *mut c_void, GPIO_MEM_SIZE as size_t);
}
}
}

impl GpioRegisters for GpioMem {
#[inline(always)]
pub(crate) fn set_high(&self, pin: u8) {
fn set_high(&self, pin: u8) {
let offset = GPSET0 + pin as usize / 32;
let shift = pin % 32;

self.write(offset, 1 << shift);
}

#[inline(always)]
pub(crate) fn set_low(&self, pin: u8) {
fn set_low(&self, pin: u8) {
let offset = GPCLR0 + pin as usize / 32;
let shift = pin % 32;

self.write(offset, 1 << shift);
}

#[inline(always)]
pub(crate) fn level(&self, pin: u8) -> Level {
fn level(&self, pin: u8) -> Level {
let offset = GPLEV0 + pin as usize / 32;
let shift = pin % 32;
let reg_value = self.read(offset);

unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b1) }
}

pub(crate) fn mode(&self, pin: u8) -> Mode {
fn mode(&self, pin: u8) -> Mode {
let offset = GPFSEL0 + pin as usize / 10;
let shift = (pin % 10) * 3;
let reg_value = self.read(offset);

unsafe { std::mem::transmute((reg_value >> shift) as u8 & 0b111) }
}

pub(crate) fn set_mode(&self, pin: u8, mode: Mode) {
fn set_mode(&self, pin: u8, mode: Mode) {
let offset = GPFSEL0 + pin as usize / 10;
let shift = (pin % 10) * 3;

Expand All @@ -206,7 +217,7 @@ impl GpioMem {
self.locks[offset].store(false, Ordering::SeqCst);
}

pub(crate) fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
// Offset for register.
let offset: usize;
// Bit shift for pin position within register value.
Expand Down Expand Up @@ -286,14 +297,6 @@ impl GpioMem {
}
}

impl Drop for GpioMem {
fn drop(&mut self) {
unsafe {
libc::munmap(self.mem_ptr as *mut c_void, GPIO_MEM_SIZE as size_t);
}
}
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}

Expand Down
137 changes: 137 additions & 0 deletions src/gpio/gpiomem/rp1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::time::Duration;

use libc::{self, c_void, off_t, size_t, MAP_FAILED, MAP_SHARED, O_SYNC, PROT_READ, PROT_WRITE};

use crate::gpio::{Error, Level, Mode, PullUpDown, Result};
use crate::system::{DeviceInfo, SoC};

const PATH_DEV_GPIOMEM: &str = "/dev/rp1-gpiomem";

const RW_OFFSET: usize = 0x0000;
const XOR_OFFSET: usize = 0x1000;
const SET_OFFSET: usize = 0x2000;
const CLR_OFFSET: usize = 0x3000;

const GPIO_STATUS: usize = 0x0000;
const GPIO_CTRL: usize = 0x0004;

// rp1-gpiomem contains IO_BANK0-2, SYS_RIO0-2, PADS_BANK0-2, PADS_ETH
const MEM_SIZE: usize = 0x30000;

const IO_BANK0_OFFSET: usize = 0x00;
const PADS_BANK0_OFFST: usize = 0x20000;

pub struct GpioMem {
mem_ptr: *mut u32,
soc: SoC,
}

impl fmt::Debug for GpioMem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GpioMem")
.field("mem_ptr", &self.mem_ptr)
.field("soc", &self.soc)
.finish()
}
}

impl GpioMem {
pub fn open() -> Result<GpioMem> {
let mem_ptr = Self::map_devgpiomem()?;

// Identify which SoC we're using.
let soc = DeviceInfo::new().map_err(|_| Error::UnknownModel)?.soc();

Ok(GpioMem { mem_ptr, soc })
}

fn map_devgpiomem() -> Result<*mut u32> {
// Open /dev/gpiomem with read/write/sync flags. This might fail if
// /dev/gpiomem doesn't exist (< Raspbian Jessie), or /dev/gpiomem
// doesn't have the appropriate permissions, or the current user is
// not a member of the gpio group.
let gpiomem_file = OpenOptions::new()
.read(true)
.write(true)
.custom_flags(O_SYNC)
.open(PATH_DEV_GPIOMEM)?;

// Memory-map /dev/rp1-gpiomem at offset 0
let gpiomem_ptr = unsafe {
libc::mmap(
ptr::null_mut(),
MEM_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
gpiomem_file.as_raw_fd(),
0,
)
};

if gpiomem_ptr == MAP_FAILED {
return Err(Error::Io(io::Error::last_os_error()));
}

Ok(gpiomem_ptr as *mut u32)
}

#[inline(always)]
fn read(&self, offset: usize) -> u32 {
unsafe { ptr::read_volatile(self.mem_ptr.add(offset)) }
}

#[inline(always)]
fn write(&self, offset: usize, value: u32) {
unsafe {
ptr::write_volatile(self.mem_ptr.add(offset), value);
}
}

#[inline(always)]
pub(crate) fn set_high(&self, pin: u8) {
unimplemented!()
}

#[inline(always)]
pub(crate) fn set_low(&self, pin: u8) {
unimplemented!()
}

#[inline(always)]
pub(crate) fn level(&self, pin: u8) -> Level {
unimplemented!()
}

pub(crate) fn mode(&self, pin: u8) -> Mode {
unimplemented!()
}

pub(crate) fn set_mode(&self, pin: u8, mode: Mode) {
unimplemented!()
}

pub(crate) fn set_pullupdown(&self, pin: u8, pud: PullUpDown) {
unimplemented!()
}
}

impl Drop for GpioMem {
fn drop(&mut self) {
unsafe {
libc::munmap(self.mem_ptr as *mut c_void, MEM_SIZE as size_t);
}
}
}

// Required because of the raw pointer to our memory-mapped file
unsafe impl Send for GpioMem {}

unsafe impl Sync for GpioMem {}
5 changes: 1 addition & 4 deletions src/gpio/ioctl.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
mod v1;
mod v2;

// gpiochip v2 API support is a work in progress, and should only be used for testing purposes.
#[cfg(not(feature = "gpiochip_v2"))]
pub use v1::*;
#[cfg(feature = "gpiochip_v2")]
pub use v2::*;
// pub use v2::*;
Loading

0 comments on commit 95ca188

Please sign in to comment.