Skip to content
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

Bug fix for GPIO under recent Linux kernel versions #1562

Open
wants to merge 3 commits into
base: development
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ $(ARDUINO): $(ARDUINO_LIB_OBJS)

# Gateway Build
$(GATEWAY): $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS)
$(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS)
$(CXX) $(LDFLAGS) -o $@ $(GATEWAY_OBJECTS) $(ARDUINO_LIB_OBJS) $(LATE_LDFLAGS)

# Include all .d files
-include $(DEPS)
Expand Down
3 changes: 2 additions & 1 deletion configure
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ signing=none
signing_request_signatures=false
encryption=false

params="SOC CFLAGS CXXFLAGS CPPFLAGS LDFLAGS PREFIX CC CXX ARDUINO_LIB_DIR BUILDDIR BINDIR GATEWAY_DIR INIT_SYSTEM SPI_DRIVER TYPE"
params="SOC CFLAGS CXXFLAGS CPPFLAGS LDFLAGS LATE_LDFLAGS PREFIX CC CXX ARDUINO_LIB_DIR BUILDDIR BINDIR GATEWAY_DIR INIT_SYSTEM SPI_DRIVER TYPE"

for opt do
if [ "$opt" = "-h" ] || [ "$opt" = "--help" ]; then
Expand Down Expand Up @@ -707,6 +707,7 @@ fi

LDFLAGS="-pthread $LDFLAGS"
CPPFLAGS="$CPUFLAGS $CPPFLAGS"
LATE_LDFLAGS="-Wl,-Bstatic -lgpiod -Wl,-Bdynamic"

printf " ${OK} CPPFLAGS: $CPPFLAGS\n"
printf " ${OK} CXXFLAGS: $CXXFLAGS\n"
Expand Down
94 changes: 34 additions & 60 deletions hal/architecture/Linux/drivers/core/interrupt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
struct ThreadArgs {
void (*func)();
int gpioPin;
struct gpiod_line *line;
};

volatile bool interruptsEnabled = true;
Expand Down Expand Up @@ -87,10 +88,10 @@ int piHiPri(const int pri)
void *interruptHandler(void *args)
{
int fd;
struct pollfd polls;
char c;
struct ThreadArgs *arguments = (struct ThreadArgs *)args;
int gpioPin = arguments->gpioPin;
struct gpiod_line *line = arguments->line;
void (*func)() = arguments->func;
delete arguments;

Expand All @@ -101,28 +102,26 @@ void *interruptHandler(void *args)
return NULL;
}

// Setup poll structure
polls.fd = fd;
polls.events = POLLPRI | POLLERR;

while (1) {
// Wait for it ...
int ret = poll(&polls, 1, -1);
// New version
int ret = gpiod_line_event_wait(line, NULL);
if (ret < 0) {
logError("Error waiting for interrupt: %s\n", strerror(errno));
break;
}
// Do a dummy read to clear the interrupt
// A one character read appars to be enough.
if (lseek (fd, 0, SEEK_SET) < 0) {
logError("Interrupt handler error: %s\n", strerror(errno));
break;
}
if (read (fd, &c, 1) < 0) {
logError("Interrupt handler error: %s\n", strerror(errno));
break;
struct gpiod_line_event event;
if (gpiod_line_event_read(line, &event) == 0) {
if (event.event_type == GPIOD_LINE_EVENT_RISING_EDGE) {
logInfo("RISING Edge\n");
} else {
logInfo("FALLING Edge\n");
}
}

// Call user function.
logError("Calling user function\n");

pthread_mutex_lock(&intMutex);
if (interruptsEnabled) {
pthread_mutex_unlock(&intMutex);
Expand All @@ -131,6 +130,8 @@ void *interruptHandler(void *args)
pthread_mutex_unlock(&intMutex);
}
}
// Adding gpiod closing instructions
gpiod_line_release(line);

close(fd);

Expand All @@ -153,72 +154,53 @@ void attachInterrupt(uint8_t gpioPin, void (*func)(), uint8_t mode)
usleep(1000);
}

// Export pin for interrupt
if ((fd = fopen("/sys/class/gpio/export", "w")) == NULL) {
logError("attachInterrupt: Unable to export pin %d for interrupt: %s\n", gpioPin, strerror(errno));
exit(1);
}
fprintf(fd, "%d\n", gpioPin);
fclose(fd);
char *chipname = "gpiochip0";
unsigned int line_num = gpioPin;
struct gpiod_line_event event;
struct gpiod_chip *chip;
struct gpiod_line *line;

// Wait a bit the system to create /sys/class/gpio/gpio<GPIO number>
usleep(1000);

snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/direction", gpioPin) ;
if ((fd = fopen (fName, "w")) == NULL) {
logError("attachInterrupt: Unable to open GPIO direction interface for pin %d: %s\n",
gpioPin, strerror(errno));
exit(1) ;
chip = gpiod_chip_open_by_name(chipname);
if (!chip) {
logError("Open chip failed\n");
exit(1);
}
fprintf(fd, "in\n") ;
fclose(fd) ;

snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/edge", gpioPin) ;
if ((fd = fopen(fName, "w")) == NULL) {
logError("attachInterrupt: Unable to open GPIO edge interface for pin %d: %s\n", gpioPin,
strerror(errno));
exit(1) ;
line = gpiod_chip_get_line(chip, line_num);
if (!line) {
logError("Get line failed\n");
exit(1);
}

switch (mode) {
case CHANGE:
fprintf(fd, "both\n");
gpiod_line_request_both_edges_events(line, "gpiointerrupt");
break;
case FALLING:
fprintf(fd, "falling\n");
gpiod_line_request_falling_edge_events(line, "gpiointerrupt");
break;
case RISING:
fprintf(fd, "rising\n");
gpiod_line_request_rising_edge_events(line, "gpiointerrupt");
break;
case NONE:
fprintf(fd, "none\n");
break;
default:
logError("attachInterrupt: Invalid mode\n");
fclose(fd);
return;
}
fclose(fd);

if (sysFds[gpioPin] == -1) {
snprintf(fName, sizeof(fName), "/sys/class/gpio/gpio%d/value", gpioPin);
if ((sysFds[gpioPin] = open(fName, O_RDONLY)) < 0) {
if ((sysFds[gpioPin] = gpiod_line_event_get_fd(line)) < 0) {
logError("Error reading pin %d: %s\n", gpioPin, strerror(errno));
exit(1);
}
}

// Clear any initial pending interrupt
ioctl(sysFds[gpioPin], FIONREAD, &count);
for (int i = 0; i < count; ++i) {
if (read(sysFds[gpioPin], &c, 1) == -1) {
logError("attachInterrupt: failed to read pin status: %s\n", strerror(errno));
}
}

struct ThreadArgs *threadArgs = new struct ThreadArgs;
threadArgs->func = func;
threadArgs->gpioPin = gpioPin;
threadArgs->line = line;

// Create a thread passing the pin and function
pthread_create(threadIds[gpioPin], NULL, interruptHandler, (void *)threadArgs);
Expand All @@ -238,14 +220,6 @@ void detachInterrupt(uint8_t gpioPin)
close(sysFds[gpioPin]);
sysFds[gpioPin] = -1;
}

FILE *fp = fopen("/sys/class/gpio/unexport", "w");
if (fp == NULL) {
logError("Unable to unexport pin %d for interrupt\n", gpioPin);
exit(1);
}
fprintf(fp, "%d", gpioPin);
fclose(fp);
}

void interrupts()
Expand Down
2 changes: 2 additions & 0 deletions hal/architecture/Linux/drivers/core/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

#include <stdint.h>

#include <gpiod.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if this is installed by default on Raspberry Pi OS? If it is not, we should probably document how to install the dependency.


#define CHANGE 1
#define FALLING 2
#define RISING 3
Expand Down