|
#include <gpiod.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <poll.h> |
|
|
|
#define CHIP_BLOCKS 8 |
|
#define MAX_LINES 4 |
|
#define EVENT_BUF_SIZE 1024 |
|
|
|
struct gpiod_chip **open_gpio_chips(int *num_chips) { |
|
struct gpiod_chip **chips = calloc(CHIP_BLOCKS, sizeof(struct gpiod_chip *)); |
|
|
|
for (int i = 0;; i++) { |
|
char path[EVENT_BUF_SIZE]; |
|
snprintf(path, sizeof(path), "/dev/gpiochip%d", i); |
|
printf("Opening GPIO №%d…\n", i); |
|
chips[i] = gpiod_chip_open(path); |
|
if (!chips[i]) |
|
break; |
|
(*num_chips)++; |
|
if (i > 0 && *num_chips % CHIP_BLOCKS == 0) { |
|
chips = realloc(chips, sizeof(struct gpio_chip *) * (i / CHIP_BLOCKS + 1)); |
|
} |
|
} |
|
return chips; |
|
} |
|
|
|
const unsigned int INKY_BUTTONS[] = {5, 6, 16, 24}; |
|
enum { |
|
INKY_BUTTON_A = 5, |
|
INKY_BUTTON_B = 6, |
|
INKY_BUTTON_C = 16, |
|
INKY_BUTTON_D = 24, |
|
INKY_BUTTON_INVALID = -1 |
|
}; |
|
|
|
const char INKY_GPIO_CONSUMER[] = "inky_buttons_example"; |
|
#define INKY_DEBOUNCE_PERIOD_MICROS 250 |
|
|
|
struct gpiod_line_request **request_lines(struct gpiod_chip **chips, int num_chips, int *num_requests, struct pollfd *pollfds, const unsigned int offsets[]) { |
|
struct gpiod_line_request **requests = calloc(num_chips, sizeof(struct gpiod_line_request *)); |
|
|
|
for (int i = 0; i < num_chips; i++) { |
|
struct gpiod_line_config *line_cfg = gpiod_line_config_new(); |
|
struct gpiod_line_settings *settings = gpiod_line_settings_new(); |
|
struct gpiod_request_config *req_cfg = gpiod_request_config_new(); |
|
|
|
if (!line_cfg || !settings || !req_cfg) { |
|
perror("Failed to allocate line config structures"); |
|
exit(1); |
|
} |
|
|
|
gpiod_request_config_set_consumer(req_cfg, INKY_GPIO_CONSUMER); |
|
struct gpiod_chip_info *chip_info = gpiod_chip_get_info(chips[i]); |
|
|
|
gpiod_line_settings_set_edge_detection(settings, GPIOD_LINE_EDGE_BOTH); |
|
gpiod_line_settings_set_debounce_period_us(settings, INKY_DEBOUNCE_PERIOD_MICROS); |
|
gpiod_line_settings_set_bias(settings, GPIOD_LINE_BIAS_PULL_UP); |
|
gpiod_line_config_add_line_settings(line_cfg, offsets, MAX_LINES, settings); |
|
|
|
struct gpiod_line_request *req; |
|
printf("Requesting lines on chip %d\n", i); |
|
req = gpiod_chip_request_lines(chips[i], req_cfg, line_cfg); |
|
if (!req) { |
|
fprintf(stderr, "Could not request lines (at chip %d)\n", i); |
|
continue; |
|
} |
|
requests[*num_requests] = req; |
|
pollfds[*num_requests].fd = gpiod_line_request_get_fd(req); |
|
pollfds[*num_requests].events = POLLIN; |
|
(*num_requests)++; |
|
|
|
gpiod_request_config_free(req_cfg); |
|
gpiod_line_settings_free(settings); |
|
gpiod_line_config_free(line_cfg); |
|
} |
|
|
|
return requests; |
|
} |
|
|
|
void print_event(int offset, enum gpiod_edge_event_type type) { |
|
printf("Inky Button "); |
|
switch (offset) { |
|
case INKY_BUTTON_A: |
|
printf("A"); |
|
break; |
|
case INKY_BUTTON_B: |
|
printf("B"); |
|
break; |
|
case INKY_BUTTON_C: |
|
printf("C"); |
|
break; |
|
case INKY_BUTTON_D: |
|
printf("D"); |
|
break; |
|
default: |
|
perror("Unrecognized button?"); |
|
} |
|
if (type == GPIOD_EDGE_EVENT_FALLING_EDGE) { |
|
printf(" pressed.\n"); |
|
} else { |
|
printf(" released.\n"); |
|
} |
|
} |
|
|
|
void listen_for_events(struct gpiod_chip **chips, struct gpiod_line_request **requests, int num_requests, struct pollfd *pollfds) { |
|
struct gpiod_edge_event_buffer *event_buffer = gpiod_edge_event_buffer_new(EVENT_BUF_SIZE); |
|
if (!event_buffer) { |
|
perror("Event buffer alloc failed"); |
|
exit(1); |
|
} |
|
|
|
printf("Listening… Press A, B, C or D.\n"); |
|
|
|
while (1) { |
|
poll(pollfds, num_requests, -1); |
|
for (int i = 0; i < num_requests; i++) { |
|
if (pollfds[i].revents & POLLIN) { |
|
struct gpiod_edge_event *event; |
|
int num_events; |
|
num_events = gpiod_line_request_read_edge_events(requests[i], event_buffer, EVENT_BUF_SIZE); |
|
if (num_events < 0) { |
|
perror("Failed to read events"); |
|
continue; |
|
} |
|
for (int j = 0; j < num_events; j++) { |
|
event = gpiod_edge_event_buffer_get_event(event_buffer, j); |
|
int offset = gpiod_edge_event_get_line_offset(event); |
|
enum gpiod_edge_event_type type = gpiod_edge_event_get_event_type(event); |
|
print_event(offset, type); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
int main(int argc, char **argv) { |
|
struct gpiod_chip **chips; |
|
struct gpiod_line_request **requests; |
|
struct pollfd pollfds[8]; |
|
int num_chips = 0, num_requests = 0; |
|
|
|
chips = open_gpio_chips(&num_chips); |
|
requests = request_lines(chips, num_chips, &num_requests, pollfds, INKY_BUTTONS); |
|
listen_for_events(chips, requests, num_requests, pollfds); |
|
|
|
return 0; |
|
} |