| // SPDX-License-Identifier: LGPL-2.1-or-later |
| /* |
| * This file is part of libgpiod. |
| * |
| * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com> |
| */ |
| |
| /* GPIO chip and line iterators. */ |
| |
| #include <dirent.h> |
| #include <gpiod.h> |
| #include <string.h> |
| |
| struct gpiod_chip_iter { |
| struct gpiod_chip **chips; |
| unsigned int num_chips; |
| unsigned int offset; |
| }; |
| |
| struct gpiod_line_iter { |
| struct gpiod_line **lines; |
| unsigned int num_lines; |
| unsigned int offset; |
| }; |
| |
| static int dir_filter(const struct dirent *dir) |
| { |
| return !strncmp(dir->d_name, "gpiochip", 8); |
| } |
| |
| static void free_dirs(struct dirent **dirs, unsigned int num_dirs) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < num_dirs; i++) |
| free(dirs[i]); |
| free(dirs); |
| } |
| |
| struct gpiod_chip_iter *gpiod_chip_iter_new(void) |
| { |
| struct gpiod_chip_iter *iter; |
| struct dirent **dirs; |
| int i, num_chips; |
| |
| num_chips = scandir("/dev", &dirs, dir_filter, alphasort); |
| if (num_chips < 0) |
| return NULL; |
| |
| iter = malloc(sizeof(*iter)); |
| if (!iter) |
| goto err_free_dirs; |
| |
| iter->num_chips = num_chips; |
| iter->offset = 0; |
| |
| if (num_chips == 0) { |
| iter->chips = NULL; |
| return iter; |
| } |
| |
| iter->chips = calloc(num_chips, sizeof(*iter->chips)); |
| if (!iter->chips) |
| goto err_free_iter; |
| |
| for (i = 0; i < num_chips; i++) { |
| iter->chips[i] = gpiod_chip_open_by_name(dirs[i]->d_name); |
| if (!iter->chips[i]) |
| goto err_close_chips; |
| } |
| |
| free_dirs(dirs, num_chips); |
| |
| return iter; |
| |
| err_close_chips: |
| for (i = 0; i < num_chips; i++) { |
| if (iter->chips[i]) |
| gpiod_chip_close(iter->chips[i]); |
| } |
| |
| free(iter->chips); |
| |
| err_free_iter: |
| free(iter); |
| |
| err_free_dirs: |
| free_dirs(dirs, num_chips); |
| |
| return NULL; |
| } |
| |
| void gpiod_chip_iter_free(struct gpiod_chip_iter *iter) |
| { |
| if (iter->offset > 0 && iter->offset < iter->num_chips) |
| gpiod_chip_close(iter->chips[iter->offset - 1]); |
| gpiod_chip_iter_free_noclose(iter); |
| } |
| |
| void gpiod_chip_iter_free_noclose(struct gpiod_chip_iter *iter) |
| { |
| unsigned int i; |
| |
| for (i = iter->offset; i < iter->num_chips; i++) { |
| if (iter->chips[i]) |
| gpiod_chip_close(iter->chips[i]); |
| } |
| |
| if (iter->chips) |
| free(iter->chips); |
| |
| free(iter); |
| } |
| |
| struct gpiod_chip *gpiod_chip_iter_next(struct gpiod_chip_iter *iter) |
| { |
| if (iter->offset > 0) { |
| gpiod_chip_close(iter->chips[iter->offset - 1]); |
| iter->chips[iter->offset - 1] = NULL; |
| } |
| |
| return gpiod_chip_iter_next_noclose(iter); |
| } |
| |
| struct gpiod_chip *gpiod_chip_iter_next_noclose(struct gpiod_chip_iter *iter) |
| { |
| return iter->offset < (iter->num_chips) |
| ? iter->chips[iter->offset++] : NULL; |
| } |
| |
| struct gpiod_line_iter *gpiod_line_iter_new(struct gpiod_chip *chip) |
| { |
| struct gpiod_line_iter *iter; |
| unsigned int i; |
| |
| iter = malloc(sizeof(*iter)); |
| if (!iter) |
| return NULL; |
| |
| iter->num_lines = gpiod_chip_num_lines(chip); |
| iter->offset = 0; |
| |
| iter->lines = calloc(iter->num_lines, sizeof(*iter->lines)); |
| if (!iter->lines) { |
| free(iter); |
| return NULL; |
| } |
| |
| for (i = 0; i < iter->num_lines; i++) { |
| iter->lines[i] = gpiod_chip_get_line(chip, i); |
| if (!iter->lines[i]) { |
| free(iter->lines); |
| free(iter); |
| return NULL; |
| } |
| } |
| |
| return iter; |
| } |
| |
| void gpiod_line_iter_free(struct gpiod_line_iter *iter) |
| { |
| free(iter->lines); |
| free(iter); |
| } |
| |
| struct gpiod_line *gpiod_line_iter_next(struct gpiod_line_iter *iter) |
| { |
| return iter->offset < (iter->num_lines) |
| ? iter->lines[iter->offset++] : NULL; |
| } |