blob: bec8a8a65021f5fcef22c51c6b5114300b955fc7 [file] [log] [blame]
/*
*
* 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 <config.h>
#endif
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/gpio.h>
#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;
}