Advanced Distributed Systems module at HSLU
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

345 lines
9.5 KiB

/*
* Copyright (c) 2022, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/* see
* https://blog.lxsang.me/post/id/33
* https://embeddedbits.org/new-linux-kernel-gpio-user-space-interface/
* https://www.ics.com/blog/gpio-programming-exploring-libgpiod-library
* https://lloydrochester.com/post/hardware/libgpiod-intro-rpi/
* */
#include "gpio.h"
/* include API header for the new interface */
#include <linux/gpio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdint.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/poll.h>
#define GPIO_DEV_NAME "/dev/gpiochip0"
/* HAT v7 requires pull-ups enabled. In case if using older headers, define locally the new flags */
#ifndef GPIOHANDLE_REQUEST_BIAS_PULL_UP /* supported with latest libs: if not defined, maybe using older header files? Let's assume the library is a recent one */
#define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5)
#define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6)
#define GPIOHANDLE_REQUEST_BIAS_DISABLE (1UL << 7)
#endif
typedef struct {
struct gpiohandle_request rq; /* request data structure */
struct gpiohandle_data data;
} Gpio_Desc_t;
static Gpio_Desc_t yellowLED, redLED, greenLED;
#if PL_HAT_VERSION<=6
static Gpio_Desc_t blueLED;
#endif
static Gpio_Desc_t upBTN, downBTN, leftBTN, rightBTN, centerBTN;
static void gpio_list(const char *dev_name) {
struct gpiochip_info info;
struct gpioline_info line_info;
int fd, ret;
fd = open(dev_name, O_RDONLY);
if (fd < 0) {
printf("Unabled to open %s: %s", dev_name, strerror(errno));
return;
}
ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info);
if (ret == -1) {
printf("Unable to get chip info from ioctl: %s", strerror(errno));
close(fd);
return;
}
printf("Chip name: %s\n", info.name);
printf("Chip label: %s\n", info.label);
printf("Number of lines: %d\n", info.lines);
for (int i = 0; i < info.lines; i++) {
line_info.line_offset = i;
ret = ioctl(fd, GPIO_GET_LINEINFO_IOCTL, &line_info);
if (ret == -1) {
printf("Unable to get line info from offset %d: %s", i, strerror(errno));
} else {
printf("offset: %d, name: %s, consumer: %s. Flags:\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\t[%s]\n",
i,
line_info.name,
line_info.consumer,
(line_info.flags & GPIOLINE_FLAG_IS_OUT) ? "OUTPUT" : "INPUT",
(line_info.flags & GPIOLINE_FLAG_ACTIVE_LOW) ? "ACTIVE_LOW" : "ACTIVE_HIGHT",
(line_info.flags & GPIOLINE_FLAG_OPEN_DRAIN) ? "OPEN_DRAIN" : "...",
(line_info.flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) ? "PULL-UP" : "...",
(line_info.flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) ? "PULL-DOWN" : "...",
(line_info.flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) ? "PULL-DISABLE" : "...",
(line_info.flags & GPIOLINE_FLAG_OPEN_SOURCE) ? "OPENSOURCE" : "...",
(line_info.flags & GPIOLINE_FLAG_KERNEL) ? "KERNEL" : "");
}
}
close(fd);
}
static void gpio_write(const char *dev_name, int offset, uint8_t value) {
struct gpiohandle_request rq;
struct gpiohandle_data data;
int fd, ret;
printf("Write value %d to GPIO at offset %d (OUTPUT mode) on chip %s\n", value, offset, dev_name);
fd = open(dev_name, O_RDONLY);
if (fd < 0) {
printf("Unabled to open %s: %s", dev_name, strerror(errno));
return;
}
rq.lineoffsets[0] = offset;
rq.flags = GPIOHANDLE_REQUEST_OUTPUT;
rq.lines = 1;
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
close(fd);
if (ret == -1) {
printf("Unable to line handle from ioctl : %s", strerror(errno));
return;
}
data.values[0] = value;
ret = ioctl(rq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret == -1) {
printf("Unable to set line value using ioctl : %s", strerror(errno));
} else {
usleep(2000000); /* wait 2 sec */
}
close(rq.fd);
}
static void writePin(Gpio_Desc_t *io, uint8_t value) {
int ret;
io->data.values[0] = value;
ret = ioctl(io->rq.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &io->data);
if (ret == -1) {
printf("Unable to set line value using ioctl : %s", strerror(errno));
}
}
static bool readPin(Gpio_Desc_t *io) {
int ret;
bool result = false;
ret = ioctl(io->rq.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &io->data);
if (ret == -1) {
printf("Unable to get line value using ioctl : %s", strerror(errno));
result = false;
} else {
result = io->data.values[0]!=0;
}
return result;
}
static int gpio_read(const char *dev_name, int offset)
{
struct gpiohandle_request rq;
struct gpiohandle_data data;
int fd, ret;
int result = -1;
fd = open(dev_name, O_RDONLY);
if (fd < 0)
{
printf("Unabled to open %s: %s", dev_name, strerror(errno));
return -1;
}
rq.lineoffsets[0] = offset;
rq.flags = GPIOHANDLE_REQUEST_INPUT;
rq.lines = 1;
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &rq);
close(fd);
if (ret == -1)
{
printf("Unable to get line handle from ioctl : %s", strerror(errno));
return -1;
}
ret = ioctl(rq.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
if (ret == -1)
{
printf("Unable to get line value using ioctl : %s", strerror(errno));
result = -1;
}
else
{
printf("Value of GPIO at offset %d (INPUT mode) on chip %s: %d\n", offset, dev_name, data.values[0]);
result = data.values[0];
}
close(rq.fd);
return result;
}
static void gpio_poll(const char *dev_name, int offset)
{
struct gpioevent_request rq;
struct pollfd pfd;
int fd, ret;
fd = open(dev_name, O_RDONLY);
if (fd < 0)
{
printf("Unabled to open %s: %s", dev_name, strerror(errno));
return;
}
rq.lineoffset = offset;
rq.eventflags = GPIOEVENT_EVENT_RISING_EDGE;
ret = ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &rq);
close(fd);
if (ret == -1)
{
printf("Unable to get line event from ioctl : %s", strerror(errno));
close(fd);
return;
}
pfd.fd = rq.fd;
pfd.events = POLLIN;
ret = poll(&pfd, 1, -1);
if (ret == -1)
{
printf("Error while polling event from GPIO: %s", strerror(errno));
}
else if (pfd.revents & POLLIN)
{
printf("Rising edge event on GPIO offset: %d, of %s\n", offset, dev_name);
}
close(rq.fd);
}
bool Gpio_ReadPin(Gpio_Pins_e pin) {
bool val;
//return gpio_read(GPIO_DEV_NAME, pin);
switch(pin) {
case Gpio_Pins_BTN_UP_BCM:
val = readPin(&upBTN);
break;
case Gpio_Pins_BTN_DOWN_BCM:
val = readPin(&downBTN);
break;
case Gpio_Pins_BTN_LEFT_BCM:
val = readPin(&leftBTN);
break;
case Gpio_Pins_BTN_RIGHT_BCM:
val = readPin(&rightBTN);
break;
case Gpio_Pins_BTN_CENTER_BCM:
val = readPin(&centerBTN);
break;
default:
val = false;
break;
}
return val;
}
void Gpio_WritePin(Gpio_Pins_e pin, bool val) {
switch(pin) {
#if PL_HAT_VERSION<=6
case Gpio_Pins_LED_BLUE_PIN_BCM:
writePin(&blueLED, val);
break;
#endif
case Gpio_Pins_LED_RED_PIN_BCM:
writePin(&redLED, val);
break;
case Gpio_Pins_LED_GREEN_PIN_BCM:
writePin(&greenLED, val);
break;
case Gpio_Pins_LED_YELLOW_PIN_BCM:
writePin(&yellowLED, val);
break;
default:
break;
}
}
int Gpio_Deinit(void) {
#if PL_HAT_VERSION<=6
close(blueLED.rq.fd);
#endif
close(redLED.rq.fd);
close(greenLED.rq.fd);
close(yellowLED.rq.fd);
close(upBTN.rq.fd);
close(downBTN.rq.fd);
close(leftBTN.rq.fd);
close(rightBTN.rq.fd);
close(centerBTN.rq.fd);
return 0;
}
static int RequestPin(Gpio_Desc_t *desc, Gpio_Pins_e pin, int lineRequestFlags, const char *label) {
int fd, ret;
fd = open(GPIO_DEV_NAME, O_RDONLY);
if (fd < 0) {
printf("Unabled to open %s: %s", GPIO_DEV_NAME, strerror(errno));
return -1;
}
desc->rq.lineoffsets[0] = pin;
desc->rq.flags = lineRequestFlags;
memcpy(desc->rq.default_values, &desc->data, sizeof(desc->rq.default_values));
strcpy(desc->rq.consumer_label, label);
desc->rq.lines = 1;
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &desc->rq);
close(fd);
if (ret == -1) {
printf("Unable to get line handle from ioctl : %s", strerror(errno));
return -1;
}
return 0;
}
int Gpio_Init(void) {
#if GPIO_HAT_VERSION<=6
if (RequestPin(&blueLED, Gpio_Pins_LED_BLUE_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_blue")!=0) {
return -1;
}
#endif
if (RequestPin(&redLED, Gpio_Pins_LED_RED_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_red")!=0) {
return -1;
}
if (RequestPin(&greenLED, Gpio_Pins_LED_GREEN_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_green")!=0) {
return -1;
}
if (RequestPin(&yellowLED, Gpio_Pins_LED_YELLOW_PIN_BCM, GPIOHANDLE_REQUEST_OUTPUT, "led_yellow")!=0) {
return -1;
}
int inputPinFlags = GPIOHANDLE_REQUEST_INPUT;
#if PL_HAT_VERSION>6
inputPinFlags |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
#endif
if (RequestPin(&upBTN, Gpio_Pins_BTN_UP_BCM, inputPinFlags, "button_up")!=0) {
return -1;
}
if (RequestPin(&downBTN, Gpio_Pins_BTN_DOWN_BCM, inputPinFlags, "button_down")!=0) {
return -1;
}
if (RequestPin(&leftBTN, Gpio_Pins_BTN_LEFT_BCM, inputPinFlags, "button_left")!=0) {
return -1;
}
if (RequestPin(&rightBTN, Gpio_Pins_BTN_RIGHT_BCM, inputPinFlags, "button_right")!=0) {
return -1;
}
if (RequestPin(&centerBTN, Gpio_Pins_BTN_CENTER_BCM, inputPinFlags, "button_center")!=0) {
return -1;
}
#if 0 /* testing only */
gpio_list(GPIO_DEV_NAME);
#endif
return 0;
}