Skip to content

[Linux]: use shared pin FD cache #1027

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions utility/SPIDEV/gpio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@
#include <sys/ioctl.h> // ioctl()
#include <errno.h> // errno, strerror()
#include <string.h> // std::string, strcpy()
#include <map>
#include "gpio.h"

// instantiate some global structs to setup cache
// doing this globally ensures the data struct is zero-ed out
typedef int gpio_fd; // for readability
std::map<rf24_gpio_pin_t, gpio_fd> cachedPins;
struct gpio_v2_line_request request;
struct gpio_v2_line_values data;

// initialize static member.
// initialize static members.
int GPIOChipCache::fd = -1;
std::map<rf24_gpio_pin_t, gpio_fd> GPIOChipCache::cachedPins = std::map<rf24_gpio_pin_t, gpio_fd>();

void GPIOChipCache::openDevice()
{
Expand Down Expand Up @@ -97,8 +95,8 @@ void GPIO::open(rf24_gpio_pin_t port, int DDR)
}

// check if pin is already in use
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = cachedPins.find(port);
if (pin == cachedPins.end()) { // pin not in use; add it to cached request
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = gpioCache.cachedPins.find(port);
if (pin == gpioCache.cachedPins.end()) { // pin not in use; add it to cached request
request.offsets[0] = port;
request.fd = 0;
}
Expand Down Expand Up @@ -127,25 +125,25 @@ void GPIO::open(rf24_gpio_pin_t port, int DDR)
throw GPIOException(msg);
return;
}
cachedPins.insert(std::pair<rf24_gpio_pin_t, gpio_fd>(port, request.fd));
gpioCache.cachedPins.insert(std::pair<rf24_gpio_pin_t, gpio_fd>(port, request.fd));
}

void GPIO::close(rf24_gpio_pin_t port)
{
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = cachedPins.find(port);
if (pin == cachedPins.end()) {
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = gpioCache.cachedPins.find(port);
if (pin == gpioCache.cachedPins.end()) {
return;
}
if (pin->second > 0) {
::close(pin->second);
}
cachedPins.erase(pin);
gpioCache.cachedPins.erase(pin);
}

int GPIO::read(rf24_gpio_pin_t port)
{
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = cachedPins.find(port);
if (pin == cachedPins.end() || pin->second <= 0) {
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = gpioCache.cachedPins.find(port);
if (pin == gpioCache.cachedPins.end() || pin->second <= 0) {
throw GPIOException("[GPIO::read] pin not initialized! Use GPIO::open() first");
return -1;
}
Expand All @@ -164,8 +162,8 @@ int GPIO::read(rf24_gpio_pin_t port)

void GPIO::write(rf24_gpio_pin_t port, int value)
{
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = cachedPins.find(port);
if (pin == cachedPins.end() || pin->second <= 0) {
std::map<rf24_gpio_pin_t, gpio_fd>::iterator pin = gpioCache.cachedPins.find(port);
if (pin == gpioCache.cachedPins.end() || pin->second <= 0) {
throw GPIOException("[GPIO::write] pin not initialized! Use GPIO::open() first");
return;
}
Expand Down
9 changes: 9 additions & 0 deletions utility/SPIDEV/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <stdexcept>
#include <cstdint>
#include <map>
#include "linux/gpio.h" // gpiochip_info

typedef uint16_t rf24_gpio_pin_t;
Expand All @@ -35,6 +36,8 @@ class GPIOException : public std::runtime_error
}
};

typedef int gpio_fd; // for readability

/// A struct to manage the GPIO chip file descriptor.
/// This struct's destructor should close any cached GPIO pin requests' file descriptors.
struct GPIOChipCache
Expand All @@ -48,6 +51,12 @@ struct GPIOChipCache
/// struct use the same file descriptor.
static int fd;

/// @brief The map of pin numbers to their corresponding file descriptors.
///
/// Because this member is static, all instances (& derivative instances) of this
/// struct use the same mapping.
static std::map<rf24_gpio_pin_t, gpio_fd> cachedPins;

/// Open the File Descriptor for the GPIO chip
void openDevice();

Expand Down
12 changes: 10 additions & 2 deletions utility/SPIDEV/interrupt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,21 @@ int attachInterrupt(rf24_gpio_pin_t pin, int mode, void (*function)(void))
IrqPinCache irqPinCache;
irqPinCache.fd = request.fd;
irqPinCache.function = function;
std::pair<std::map<rf24_gpio_pin_t, IrqPinCache>::iterator, bool> indexPair = irqCache.insert(std::pair<rf24_gpio_pin_t, IrqPinCache>(pin, irqPinCache));

std::pair<std::map<rf24_gpio_pin_t, IrqPinCache>::iterator, bool> indexPair = irqCache.insert(std::pair<rf24_gpio_pin_t, IrqPinCache>(pin, irqPinCache));
if (!indexPair.second) {
// this should not be reached, but indexPair.first needs to be the inserted map element
throw IRQException("[attachInterrupt] Could not cache the IRQ pin with function pointer");
return 0;
}

std::pair<std::map<rf24_gpio_pin_t, gpio_fd>::iterator, bool> gpioPair = irqChipCache.cachedPins.insert(std::pair<rf24_gpio_pin_t, gpio_fd>(pin, request.fd));
if (!gpioPair.second) {
// this should not be reached, but gpioPair.first needs to be the inserted map element
throw IRQException("[attachInterrupt] Could not cache the GPIO pin's file descriptor");
return 0;
}

// create and start thread
pthread_mutex_lock(&irq_mutex);
pthread_create(&indexPair.first->second.id, nullptr, poll_irq, &indexPair.first->second);
Expand All @@ -157,8 +164,9 @@ int detachInterrupt(rf24_gpio_pin_t pin)
}
pthread_cancel(cachedPin->second.id); // send cancel request
pthread_join(cachedPin->second.id, NULL); // wait till thread terminates
close(cachedPin->second.fd);
irqCache.erase(cachedPin);
// reconfigure the pin for basic `digitalRead()`
GPIO::open(pin, GPIO::DIRECTION_IN);
return 1;
}

Expand Down
2 changes: 1 addition & 1 deletion utility/SPIDEV/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct IrqPinCache
int attachInterrupt(rf24_gpio_pin_t pin, int mode, void (*function)(void));

/**
* Will cancel the interrupt thread, close the filehandle and release the pin.
* Will cancel the interrupt thread and re-configure the pin for `digitalRead()` use.
*/
int detachInterrupt(rf24_gpio_pin_t pin);

Expand Down