/* * * Embedded Linux library * * Copyright (C) 2019 Geanix. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #define _GNU_SOURCE #include #include #include #include #include #include #include #include "private.h" #include "strv.h" #include "useful.h" #include "gpio.h" struct l_gpio_chip { int fd; char *name; char *label; uint32_t n_lines; }; struct l_gpio_writer { int fd; uint32_t n_offsets; }; struct l_gpio_reader { int fd; uint32_t n_offsets; }; static bool chip_has_line_label(const char *chip_name, const char *line_label) { struct l_gpio_chip *chip; bool has_label; chip = l_gpio_chip_new(chip_name); if (!chip) return false; has_label = l_gpio_chip_find_line_offset(chip, line_label, NULL); l_gpio_chip_free(chip); return has_label; } LIB_EXPORT char **l_gpio_chips_with_line_label(const char *line_label) { struct dirent *entry; DIR *dp; char **chips = NULL; dp = opendir("/sys/bus/gpio/devices"); if (dp == NULL) return NULL; while ((entry = readdir(dp))) { if (entry->d_type != DT_LNK) continue; if (!l_str_has_prefix(entry->d_name, "gpiochip")) continue; if (chip_has_line_label(entry->d_name, line_label)) chips = l_strv_append(chips, entry->d_name); } closedir(dp); return chips; } LIB_EXPORT struct l_gpio_chip *l_gpio_chip_new(const char *chip_name) { struct l_gpio_chip *chip; struct gpiochip_info info; char *path; int fd; int ret; if (unlikely(!chip_name)) return NULL; path = l_strdup_printf("/dev/%s", chip_name); fd = open(path, O_RDONLY | O_CLOEXEC); l_free(path); if (fd < 0) return NULL; memset(&info, 0, sizeof(info)); ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info); if (ret < 0) { close(fd); return NULL; } chip = l_new(struct l_gpio_chip, 1); chip->fd = fd; chip->n_lines = info.lines; chip->label = l_strndup(info.label, sizeof(info.label)); chip->name = l_strdup(chip_name); return chip; } LIB_EXPORT const char *l_gpio_chip_get_label(struct l_gpio_chip *chip) { if (unlikely(!chip)) return NULL; return chip->label; } LIB_EXPORT const char *l_gpio_chip_get_name(struct l_gpio_chip *chip) { if (unlikely(!chip)) return NULL; return chip->name; } LIB_EXPORT uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip) { if (unlikely(!chip)) return 0; return chip->n_lines; } LIB_EXPORT void l_gpio_chip_free(struct l_gpio_chip *chip) { if (unlikely(!chip)) return; if (chip->fd >= 0) close(chip->fd); l_free(chip->name); l_free(chip->label); l_free(chip); } LIB_EXPORT bool l_gpio_chip_find_line_offset(struct l_gpio_chip *chip, const char *line_label, uint32_t *line_offset) { struct gpioline_info info; uint32_t i; if (unlikely(!chip)) return false; if (unlikely(!line_label)) return false; for (i = 0; i < chip->n_lines; i++) { memset(&info, 0, sizeof(info)); info.line_offset = i; if (ioctl(chip->fd, GPIO_GET_LINEINFO_IOCTL, &info) < 0) return false; if (strcmp(info.name, line_label) != 0) continue; if (line_offset) *line_offset = i; return true; } return false; } LIB_EXPORT char *l_gpio_chip_get_line_label(struct l_gpio_chip *chip, uint32_t offset) { struct gpioline_info info; if (unlikely(!chip)) return NULL; if (unlikely(offset >= chip->n_lines)) return NULL; memset(&info, 0, sizeof(info)); info.line_offset = offset; if (ioctl(chip->fd, GPIO_GET_LINEINFO_IOCTL, &info) < 0) return NULL; return l_strdup(info.name); } LIB_EXPORT char *l_gpio_chip_get_line_consumer(struct l_gpio_chip *chip, uint32_t offset) { struct gpioline_info info; if (unlikely(!chip)) return NULL; if (unlikely(offset >= chip->n_lines)) return NULL; memset(&info, 0, sizeof(info)); info.line_offset = offset; if (ioctl(chip->fd, GPIO_GET_LINEINFO_IOCTL, &info) < 0) return NULL; return l_strdup(info.consumer); } LIB_EXPORT struct l_gpio_writer *l_gpio_writer_new(struct l_gpio_chip *chip, const char *consumer, uint32_t n_offsets, const uint32_t offsets[], const uint32_t values[]) { struct l_gpio_writer *writer; struct gpiohandle_request request; uint32_t i; if (unlikely(!chip)) return NULL; if (unlikely(n_offsets == 0 || n_offsets > GPIOHANDLES_MAX)) return NULL; if (unlikely(!offsets)) return NULL; memset(&request, 0, sizeof(request)); l_strlcpy(request.consumer_label, consumer, 32); request.lines = n_offsets; request.flags = GPIOHANDLE_REQUEST_OUTPUT; for (i = 0; i < n_offsets; i++) { if (offsets[i] >= chip->n_lines) return NULL; request.lineoffsets[i] = offsets[i]; request.default_values[i] = values[i]; } if (ioctl(chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &request) < 0) return NULL; if (request.fd <= 0) return NULL; writer = l_new(struct l_gpio_writer, 1); writer->fd = request.fd; writer->n_offsets = n_offsets; return writer; } LIB_EXPORT void l_gpio_writer_free(struct l_gpio_writer *writer) { if (unlikely(!writer)) return; if (writer->fd >= 0) close(writer->fd); l_free(writer); } LIB_EXPORT bool l_gpio_writer_set(struct l_gpio_writer *writer, uint32_t n_values, const uint32_t values[]) { struct gpiohandle_data data; uint32_t i; if (unlikely(!writer)) return false; if (unlikely(!values)) return false; if (unlikely(n_values != writer->n_offsets)) return false; for (i = 0; i < n_values; i++) data.values[i] = values[i]; if (ioctl(writer->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data) < 0) return false; return true; } LIB_EXPORT struct l_gpio_reader *l_gpio_reader_new(struct l_gpio_chip *chip, const char *consumer, uint32_t n_offsets, const uint32_t offsets[]) { struct l_gpio_reader *reader; struct gpiohandle_request request; uint32_t i; if (unlikely(!chip)) return NULL; if (unlikely(n_offsets == 0 || n_offsets > GPIOHANDLES_MAX)) return NULL; if (unlikely(!offsets)) return NULL; memset(&request, 0, sizeof(request)); l_strlcpy(request.consumer_label, consumer, 32); request.lines = n_offsets; request.flags = GPIOHANDLE_REQUEST_INPUT; for (i = 0; i < n_offsets; i++) { if (offsets[i] >= chip->n_lines) return NULL; request.lineoffsets[i] = offsets[i]; } if (ioctl(chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &request) < 0) return NULL; if (request.fd <= 0) return NULL; reader = l_new(struct l_gpio_reader, 1); reader->fd = request.fd; reader->n_offsets = n_offsets; return reader; } LIB_EXPORT void l_gpio_reader_free(struct l_gpio_reader *reader) { if (unlikely(!reader)) return; if (reader->fd >= 0) close(reader->fd); l_free(reader); } LIB_EXPORT bool l_gpio_reader_get(struct l_gpio_reader *reader, uint32_t n_values, uint32_t values[]) { struct gpiohandle_data data; uint32_t i; if (unlikely(!reader)) return false; if (unlikely(n_values != reader->n_offsets)) return false; if (unlikely(!values)) return false; if (ioctl(reader->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data) < 0) return false; for (i = 0; i < n_values; i++) values[i] = data.values[i]; return true; }