I'm trying to use raspberry pi GPIOs through sysfs using the C language, but I keep getting fopen: Permission denied
when trying to change the GPIO line direction.
The issue occurs when trying to open the file "/sys/class/gpio/gpio18/direction"
.
Notes:
Normally I would need to put the pin that I want to use in "/sys/class/gpio/export"
.
And then I need to set its direction (output/input) by writing 1
or 0
to "/sys/class/gpio/gpio18/direction"
.
To achieve that programmatically I use the following code
#include "io.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
int main(int argc, char const* argv[]) {
// export line
FILE* p_gpio_line;
if ((p_gpio_line = fopen("/sys/class/gpio/export", "w")) == NULL) {
printf("Cannot open export file.\n");
perror("fopen");
exit(1);
}
rewind(p_gpio_line);
fwrite("18", sizeof(char), 2, p_gpio_line);
fclose(p_gpio_line);
// set direction
FILE* p_gpio_direction;
if ((p_gpio_direction = fopen("/sys/class/gpio/gpio18/direction", "r+")) == NULL) {
printf("Cannot open direction file.\n");
perror("fopen");
exit(1);
}
return 0;
}
The first time it executes I get
Cannot open direction file.
fopen: Permission denied
The second time it works fine because the GPIO line is exported before the program is executed (from the first execution)
It also works fine if I manually export the GPIO line from the terminal
My comment about perror()
remains in effect, but indeed in your example fopen()
returns EACCES ("Permission denied").
Group gpio
and permissions for /sys/class/gpio
are specific for Raspberry Pi distro. They are set by udev rule in /etc/udev/rules.d/99-com.rules
:
SUBSYSTEM=="gpio", ACTION=="add", PROGRAM="/bin/sh -c 'chgrp -R gpio /sys%p && chmod -R g=u /sys%p'"
Udev rule is applied asynchronously after direction
file appears. You are trying to open the file while permissions aren't yet set.
I'd suggest to use character device GPIO interface with libgpiod instead of deprecated sysfs interface.
If you still want to use sysfs interface, straightforward solution is to retry fopen()
until it succeed (or fail with error other than EACCES, or timeout). For example:
// Open timeout, ms
#define DIR_OPEN_TIMEOUT_MS 3000
// Delay between retries, ms
#define DIR_POLL_DELAY_MS 100
...
// set direction
FILE* p_gpio_direction;
unsigned int retries_left = DIR_OPEN_TIMEOUT_MS / DIR_POLL_DELAY_MS;
while (1)
{
p_gpio_direction = fopen("/sys/class/gpio/gpio18/direction", "r+");
if (p_gpio_direction)
break;
if ((errno != EACCES) || (retries_left-- == 0)) {
printf("Cannot open direction file: %m\n");
exit(1);
}
const struct timespec rqt = {
.tv_sec = DIR_POLL_DELAY_MS / 1000,
.tv_nsec = 1000000L * (DIR_POLL_DELAY_MS % 1000)
};
if (nanosleep(&rqt, NULL) == -1)
perror("nanosleep()");
}
...