diff --git a/src/gpio.c b/src/gpio.c index ad74f34..318e013 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -8,6 +8,8 @@ #include "gpio.h" #include +#include +#include /* Those defines are used for legacy config files only */ #define GPIO_CHIP1_LABEL "1c20800.pinctrl" @@ -133,6 +135,90 @@ struct gpiod_line *gpio_get_input_line(struct EG25Manager *manager, int chip, in return gpio_line; } +static int gpio_chip_dir_filter(const struct dirent *entry) +{ + struct stat sb; + int ret = 0; + char *path; + + if (asprintf(&path, "/dev/%s", entry->d_name) < 0) + return 0; + + if ((lstat(path, &sb) == 0) && (!S_ISLNK(sb.st_mode)) && + gpiod_is_gpiochip_device(path)) + ret = 1; + + free(path); + + return ret; +} + +int gpio_all_chip_paths(char ***paths_ptr) +{ + int i, j, num_chips, ret = 0; + struct dirent **entries; + char **paths; + + num_chips = scandir("/dev/", &entries, gpio_chip_dir_filter, alphasort); + if (num_chips < 0) + g_error("gpio: unable to scan /dev: %s", g_strerror(errno)); + + paths = calloc(num_chips, sizeof(*paths)); + if (paths == NULL) + g_error("gpio: out of memory"); + + for (i = 0; i < num_chips; i++) { + if (asprintf(&paths[i], "/dev/%s", entries[i]->d_name) < 0) { + for (j = 0; j < i; j++) + free(paths[j]); + + free(paths); + return 0; + } + } + + *paths_ptr = paths; + ret = num_chips; + + for (i = 0; i < num_chips; i++) + free(entries[i]); + + free(entries); + return ret; +} + +struct gpiod_chip *gpio_chip_open_by_label(const char *label) +{ + int num_chips, i; + char **paths; + const char *clabel; + struct gpiod_chip *chip; + struct gpiod_chip_info *cinfo; + + num_chips = gpio_all_chip_paths(&paths); + + for (i = 0; i < num_chips; i++) { + chip = gpiod_chip_open(paths[i]); + if (!chip) + continue; + + cinfo = gpiod_chip_get_info(chip); + if (!cinfo) + goto clean_chip_open; + + clabel = gpiod_chip_info_get_label(cinfo); + + if (strcmp(label, clabel) == 0) { + return chip; + } + +clean_chip_open: + gpiod_chip_close(chip); + } + + return NULL; +} + int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) { int i; @@ -169,7 +255,7 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) toml_datum_t data = toml_string_at(chipslist, i); if (!data.ok) continue; - manager->gpiochip[i] = gpiod_chip_open_by_label(data.u.s); + manager->gpiochip[i] = gpio_chip_open_by_label(data.u.s); if (!manager->gpiochip[i]) g_error("Unable to find GPIO chip '%s'", data.u.s); } @@ -220,11 +306,11 @@ int gpio_init(struct EG25Manager *manager, toml_table_t *config[]) guint offset, chipidx, gpio_idx; /* Legacy config file, only used on the OG PinePhone */ - manager->gpiochip[0] = gpiod_chip_open_by_label(GPIO_CHIP1_LABEL); + manager->gpiochip[0] = gpio_chip_open_by_label(GPIO_CHIP1_LABEL); if (!manager->gpiochip[0]) g_error("Unable to open GPIO chip " GPIO_CHIP1_LABEL); - manager->gpiochip[1] = gpiod_chip_open_by_label(GPIO_CHIP2_LABEL); + manager->gpiochip[1] = gpio_chip_open_by_label(GPIO_CHIP2_LABEL); if (!manager->gpiochip[1]) g_error("Unable to open GPIO chip " GPIO_CHIP2_LABEL);