/* * 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 #include #include #include #include #include #include #include #include #include #include #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(¢erBTN); 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(¢erBTN, Gpio_Pins_BTN_CENTER_BCM, inputPinFlags, "button_center")!=0) { return -1; } #if 0 /* testing only */ gpio_list(GPIO_DEV_NAME); #endif return 0; }