#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>Jonas Altrock ew20b126@technikum-wien.at
This file is the same as in assignment 2!
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#include "gpio.h"Internal structure to keep the information for one GPIO together.
typedef struct gpio_file {
char *direction_path;
FILE *direction;
char *value_path;
FILE *value;
} gpio_t;The classic sysfs
has been deprecated in Linux kernel, so we use the new character device
paths in /sys/bus/gpio.
That GPIO 87 is on gpiochip2 we could find out by either looking for a
directory named gpio87 in the gpio folders or by checking the data sheet
am3358.pdf and figuring out that each of the GPIO chips provides 32 pins,
meaning 87 = 64 + 23 is in the third chip (0-31, 32-63, 64-95), so index 2.
gpio_t gpios[2] = {
{
.direction_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio87/direction",
.value_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio87/value",
},
{
.direction_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio89/direction",
.value_path = "/sys/bus/gpio/devices/gpiochip2/gpio/gpio89/value",
}
};Originally I only opened the files and let Linux close the FDs at the
end of the program, but the lecturers did not like it, so now there is
an atexit cleanup.
void _gpio_deinit()
{
for (int i = 0; i < 2; i++) {
fclose(gpios[i].direction);
fclose(gpios[i].value);
}
}We open the direction and value files for the two GPIOs we need to use
and store them in an internal array.
int gpio_init()
{
for (int i = 0; i < 2; i++) {
if (NULL == (gpios[i].direction = fopen(gpios[i].direction_path, "w+"))) {
perror("Could not open GPIO direction file");
return -1;
}
if (NULL == (gpios[i].value = fopen(gpios[i].value_path, "w+"))) {
perror("Could not open GPIO value file");
return -1;
}
}
atexit(_gpio_deinit);
return 0;
}Write to a GPIO file (direction or value). We flush the output buffer to
make sure the kernel sees our new value. We use fprintf so that we do
not need to know how long the string we write is.
(write would need that.)
void _gpio_write(FILE *file, const char *value)
{
fprintf(file, "%s\n", value);
fflush(file);
}Read from the GPIO as a string - we know we only need to read 1 or 0 for
value and out or in for direction, so read max 3 characters.
void _gpio_read(FILE *file, char *value)
{
rewind(file);
fscanf(file, "%3s", value);
}Direction can only be out or in.
enum Direction gpio_get_dir(enum GPIO gpio)
{
char str[4];
_gpio_read(gpios[gpio].direction, str);
if (str[0] == 'o') {
return OUT;
}
return IN;
}void gpio_set_dir(enum GPIO gpio, enum Direction dir)
{If you don’t remember, condition ? if_true : if_false is the ternary
expression syntax that is like an if/else but evaluates to a value, so
can be on the right hand side of an assignment or in the position of a
function parameter.
const char *d = dir == OUT ? "out" : "in";
_gpio_write(gpios[gpio].direction, d);
}enum Value gpio_get_value(enum GPIO gpio)
{
char str[4] = "";
_gpio_read(gpios[gpio].value, str);
if (str[0] == '0') {
return OFF;
}
return ON;
}void gpio_set_value(enum GPIO gpio, enum Value value)
{
const char *s = value == ON ? "1" : "0";
_gpio_write(gpios[gpio].value, s);
}