| // SPDX-License-Identifier: LGPL-2.1-or-later |
| /* |
| * This file is part of libgpiod. |
| * |
| * Copyright (C) 2017-2018 Bartosz Golaszewski <bartekgola@gmail.com> |
| */ |
| |
| /* |
| * More specific variants of the core API and misc functions that don't need |
| * access to neither the internal library data structures nor the kernel UAPI. |
| */ |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <gpiod.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| static bool isuint(const char *str) |
| { |
| for (; *str && isdigit(*str); str++) |
| ; |
| |
| return *str == '\0'; |
| } |
| |
| struct gpiod_chip *gpiod_chip_open_by_name(const char *name) |
| { |
| struct gpiod_chip *chip; |
| char *path; |
| int rv; |
| |
| rv = asprintf(&path, "/dev/%s", name); |
| if (rv < 0) |
| return NULL; |
| |
| chip = gpiod_chip_open(path); |
| free(path); |
| |
| return chip; |
| } |
| |
| struct gpiod_chip *gpiod_chip_open_by_number(unsigned int num) |
| { |
| struct gpiod_chip *chip; |
| char *path; |
| int rv; |
| |
| rv = asprintf(&path, "/dev/gpiochip%u", num); |
| if (!rv) |
| return NULL; |
| |
| chip = gpiod_chip_open(path); |
| free(path); |
| |
| return chip; |
| } |
| |
| struct gpiod_chip *gpiod_chip_open_by_label(const char *label) |
| { |
| struct gpiod_chip_iter *iter; |
| struct gpiod_chip *chip; |
| |
| iter = gpiod_chip_iter_new(); |
| if (!iter) |
| return NULL; |
| |
| gpiod_foreach_chip(iter, chip) { |
| if (strcmp(label, gpiod_chip_label(chip)) == 0) { |
| gpiod_chip_iter_free_noclose(iter); |
| return chip; |
| } |
| } |
| |
| errno = ENOENT; |
| gpiod_chip_iter_free(iter); |
| |
| return NULL; |
| } |
| |
| struct gpiod_chip *gpiod_chip_open_lookup(const char *descr) |
| { |
| struct gpiod_chip *chip; |
| |
| if (isuint(descr)) { |
| chip = gpiod_chip_open_by_number(strtoul(descr, NULL, 10)); |
| } else { |
| chip = gpiod_chip_open_by_label(descr); |
| if (!chip) { |
| if (strncmp(descr, "/dev/", 5)) |
| chip = gpiod_chip_open_by_name(descr); |
| else |
| chip = gpiod_chip_open(descr); |
| } |
| } |
| |
| return chip; |
| } |
| |
| int gpiod_chip_get_lines(struct gpiod_chip *chip, unsigned int *offsets, |
| unsigned int num_offsets, struct gpiod_line_bulk *bulk) |
| { |
| struct gpiod_line *line; |
| unsigned int i; |
| |
| gpiod_line_bulk_init(bulk); |
| |
| for (i = 0; i < num_offsets; i++) { |
| line = gpiod_chip_get_line(chip, offsets[i]); |
| if (!line) |
| return -1; |
| |
| gpiod_line_bulk_add(bulk, line); |
| } |
| |
| return 0; |
| } |
| |
| int gpiod_chip_get_all_lines(struct gpiod_chip *chip, |
| struct gpiod_line_bulk *bulk) |
| { |
| struct gpiod_line_iter *iter; |
| struct gpiod_line *line; |
| |
| gpiod_line_bulk_init(bulk); |
| |
| iter = gpiod_line_iter_new(chip); |
| if (!iter) |
| return -1; |
| |
| gpiod_foreach_line(iter, line) |
| gpiod_line_bulk_add(bulk, line); |
| |
| gpiod_line_iter_free(iter); |
| |
| return 0; |
| } |
| |
| struct gpiod_line * |
| gpiod_chip_find_line(struct gpiod_chip *chip, const char *name) |
| { |
| struct gpiod_line_iter *iter; |
| struct gpiod_line *line; |
| const char *tmp; |
| |
| iter = gpiod_line_iter_new(chip); |
| if (!iter) |
| return NULL; |
| |
| gpiod_foreach_line(iter, line) { |
| tmp = gpiod_line_name(line); |
| if (tmp && strcmp(tmp, name) == 0) { |
| gpiod_line_iter_free(iter); |
| return line; |
| } |
| } |
| |
| errno = ENOENT; |
| gpiod_line_iter_free(iter); |
| |
| return NULL; |
| } |
| |
| int gpiod_chip_find_lines(struct gpiod_chip *chip, |
| const char **names, struct gpiod_line_bulk *bulk) |
| { |
| struct gpiod_line *line; |
| int i; |
| |
| gpiod_line_bulk_init(bulk); |
| |
| for (i = 0; names[i]; i++) { |
| line = gpiod_chip_find_line(chip, names[i]); |
| if (!line) |
| return -1; |
| |
| gpiod_line_bulk_add(bulk, line); |
| } |
| |
| return 0; |
| } |
| |
| int gpiod_line_request_input(struct gpiod_line *line, const char *consumer) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, |
| }; |
| |
| return gpiod_line_request(line, &config, 0); |
| } |
| |
| int gpiod_line_request_output(struct gpiod_line *line, |
| const char *consumer, int default_val) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, |
| }; |
| |
| return gpiod_line_request(line, &config, default_val); |
| } |
| |
| int gpiod_line_request_input_flags(struct gpiod_line *line, |
| const char *consumer, int flags) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, |
| .flags = flags, |
| }; |
| |
| return gpiod_line_request(line, &config, 0); |
| } |
| |
| int gpiod_line_request_output_flags(struct gpiod_line *line, |
| const char *consumer, int flags, |
| int default_val) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, |
| .flags = flags, |
| }; |
| |
| return gpiod_line_request(line, &config, default_val); |
| } |
| |
| static int line_event_request_type(struct gpiod_line *line, |
| const char *consumer, int flags, int type) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = type, |
| .flags = flags, |
| }; |
| |
| return gpiod_line_request(line, &config, 0); |
| } |
| |
| int gpiod_line_request_rising_edge_events(struct gpiod_line *line, |
| const char *consumer) |
| { |
| return line_event_request_type(line, consumer, 0, |
| GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); |
| } |
| |
| int gpiod_line_request_falling_edge_events(struct gpiod_line *line, |
| const char *consumer) |
| { |
| return line_event_request_type(line, consumer, 0, |
| GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); |
| } |
| |
| int gpiod_line_request_both_edges_events(struct gpiod_line *line, |
| const char *consumer) |
| { |
| return line_event_request_type(line, consumer, 0, |
| GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); |
| } |
| |
| int gpiod_line_request_rising_edge_events_flags(struct gpiod_line *line, |
| const char *consumer, |
| int flags) |
| { |
| return line_event_request_type(line, consumer, flags, |
| GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); |
| } |
| |
| int gpiod_line_request_falling_edge_events_flags(struct gpiod_line *line, |
| const char *consumer, |
| int flags) |
| { |
| return line_event_request_type(line, consumer, flags, |
| GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); |
| } |
| |
| int gpiod_line_request_both_edges_events_flags(struct gpiod_line *line, |
| const char *consumer, int flags) |
| { |
| return line_event_request_type(line, consumer, flags, |
| GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); |
| } |
| |
| int gpiod_line_request_bulk_input(struct gpiod_line_bulk *bulk, |
| const char *consumer) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, |
| }; |
| |
| return gpiod_line_request_bulk(bulk, &config, 0); |
| } |
| |
| int gpiod_line_request_bulk_output(struct gpiod_line_bulk *bulk, |
| const char *consumer, |
| const int *default_vals) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, |
| }; |
| |
| return gpiod_line_request_bulk(bulk, &config, default_vals); |
| } |
| |
| static int line_event_request_type_bulk(struct gpiod_line_bulk *bulk, |
| const char *consumer, |
| int flags, int type) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = type, |
| .flags = flags, |
| }; |
| |
| return gpiod_line_request_bulk(bulk, &config, 0); |
| } |
| |
| int gpiod_line_request_bulk_rising_edge_events(struct gpiod_line_bulk *bulk, |
| const char *consumer) |
| { |
| return line_event_request_type_bulk(bulk, consumer, 0, |
| GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); |
| } |
| |
| int gpiod_line_request_bulk_falling_edge_events(struct gpiod_line_bulk *bulk, |
| const char *consumer) |
| { |
| return line_event_request_type_bulk(bulk, consumer, 0, |
| GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); |
| } |
| |
| int gpiod_line_request_bulk_both_edges_events(struct gpiod_line_bulk *bulk, |
| const char *consumer) |
| { |
| return line_event_request_type_bulk(bulk, consumer, 0, |
| GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); |
| } |
| |
| int gpiod_line_request_bulk_input_flags(struct gpiod_line_bulk *bulk, |
| const char *consumer, int flags) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_INPUT, |
| .flags = flags, |
| }; |
| |
| return gpiod_line_request_bulk(bulk, &config, 0); |
| } |
| |
| int gpiod_line_request_bulk_output_flags(struct gpiod_line_bulk *bulk, |
| const char *consumer, int flags, |
| const int *default_vals) |
| { |
| struct gpiod_line_request_config config = { |
| .consumer = consumer, |
| .request_type = GPIOD_LINE_REQUEST_DIRECTION_OUTPUT, |
| .flags = flags, |
| }; |
| |
| return gpiod_line_request_bulk(bulk, &config, default_vals); |
| } |
| |
| int gpiod_line_request_bulk_rising_edge_events_flags( |
| struct gpiod_line_bulk *bulk, |
| const char *consumer, int flags) |
| { |
| return line_event_request_type_bulk(bulk, consumer, flags, |
| GPIOD_LINE_REQUEST_EVENT_RISING_EDGE); |
| } |
| |
| int gpiod_line_request_bulk_falling_edge_events_flags( |
| struct gpiod_line_bulk *bulk, |
| const char *consumer, int flags) |
| { |
| return line_event_request_type_bulk(bulk, consumer, flags, |
| GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE); |
| } |
| |
| int gpiod_line_request_bulk_both_edges_events_flags( |
| struct gpiod_line_bulk *bulk, |
| const char *consumer, int flags) |
| { |
| return line_event_request_type_bulk(bulk, consumer, flags, |
| GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES); |
| } |
| |
| struct gpiod_line *gpiod_line_get(const char *device, unsigned int offset) |
| { |
| struct gpiod_chip *chip; |
| struct gpiod_line *line; |
| |
| chip = gpiod_chip_open_lookup(device); |
| if (!chip) |
| return NULL; |
| |
| line = gpiod_chip_get_line(chip, offset); |
| if (!line) { |
| gpiod_chip_close(chip); |
| return NULL; |
| } |
| |
| return line; |
| } |
| |
| struct gpiod_line *gpiod_line_find(const char *name) |
| { |
| struct gpiod_chip_iter *iter; |
| struct gpiod_chip *chip; |
| struct gpiod_line *line; |
| |
| iter = gpiod_chip_iter_new(); |
| if (!iter) |
| return NULL; |
| |
| gpiod_foreach_chip(iter, chip) { |
| line = gpiod_chip_find_line(chip, name); |
| if (line) { |
| gpiod_chip_iter_free_noclose(iter); |
| return line; |
| } |
| |
| if (errno != ENOENT) |
| goto out; |
| } |
| |
| errno = ENOENT; |
| |
| out: |
| gpiod_chip_iter_free(iter); |
| |
| return NULL; |
| } |
| |
| void gpiod_line_close_chip(struct gpiod_line *line) |
| { |
| struct gpiod_chip *chip = gpiod_line_get_chip(line); |
| |
| gpiod_chip_close(chip); |
| } |