[PATCH] genl: Deallocate genl family name
by Ossama Othman
---
ell/genl.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/ell/genl.c b/ell/genl.c
index ea9d182..0f65db2 100644
--- a/ell/genl.c
+++ b/ell/genl.c
@@ -496,6 +496,7 @@ static void family_watch_free(void *data)
if (watch->destroy)
watch->destroy(watch->user_data);
+ l_free(watch->name);
l_free(watch);
}
--
2.20.1
2 years, 10 months
[PATCH] tools: Add gpio utility
by Martin Hundebøll
---
.gitignore | 1 +
Makefile.am | 5 +-
tools/gpio.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 269 insertions(+), 1 deletion(-)
create mode 100644 tools/gpio.c
diff --git a/.gitignore b/.gitignore
index d34127b..d60c2e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,4 @@ tools/certchain-verify
tools/genl-discover
tools/genl-watch
tools/genl-request
+tools/gpio
diff --git a/Makefile.am b/Makefile.am
index 072867f..c39e0e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -326,7 +326,7 @@ examples_glib_eventloop_LDADD = ell/libell-private.la @GLIB_LIBS@
examples_dhcp_client_LDADD = ell/libell-private.la
noinst_PROGRAMS += tools/certchain-verify tools/genl-discover \
- tools/genl-watch tools/genl-request
+ tools/genl-watch tools/genl-request tools/gpio
tools_certchain_verify_SOURCES = tools/certchain-verify.c
tools_certchain_verify_LDADD = ell/libell-private.la
@@ -339,6 +339,9 @@ tools_genl_watch_LDADD = ell/libell-private.la
tools_genl_request_SOURCES = tools/genl-request.c
tools_genl_request_LDADD = ell/libell-private.la
+tools_gpio_SOURCES = tools/gpio.c
+tools_gpio_LDADD = ell/libell-private.la
+
EXTRA_DIST = ell/ell.sym \
$(unit_test_data_files) unit/gencerts.cnf unit/plaintext.txt
diff --git a/tools/gpio.c b/tools/gpio.c
new file mode 100644
index 0000000..f2bdb1f
--- /dev/null
+++ b/tools/gpio.c
@@ -0,0 +1,264 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <ell/ell.h>
+
+static int print_usage(void)
+{
+ fprintf(stderr,
+ "usage: %1$s find <name>\n"
+ " %1$s get (<chip> <line>|<label>)\n"
+ " %1$s set (<chip> <line>|<label>) <value>\n"
+ " %1$s chip <chip>\n"
+ " %1$s line (<chip> <line>|<label>)\n",
+ program_invocation_short_name);
+
+ return -1;
+}
+
+static bool find_line(const char *line_label, struct l_gpio_chip **chip,
+ uint32_t *offset)
+{
+ struct l_gpio_chip *c;
+ char **chip_names = NULL;
+ uint32_t o;
+
+ if (!unlikely(line_label))
+ return false;
+
+ chip_names = l_gpio_chips_with_line_label(line_label);
+ if (!chip_names || l_strv_length(chip_names) != 1)
+ return false;
+
+ c = l_gpio_chip_new(chip_names[0]);
+ l_strfreev(chip_names);
+
+ if (!c)
+ false;
+
+ if (!l_gpio_chip_find_line_offset(c, line_label, &o)) {
+ l_gpio_chip_free(c);
+ return false;
+ }
+
+ if (chip)
+ *chip = c;
+
+ if (offset)
+ *offset = o;
+
+ return true;
+
+}
+
+static int find(int argc, char **argv)
+{
+ struct l_gpio_chip *chip;
+ uint32_t offset;
+
+ if (argc != 2)
+ return print_usage();
+
+ if (!find_line(argv[1], &chip, &offset))
+ return -1;
+
+ printf("chip: %s\n", l_gpio_chip_get_name(chip));
+ printf("line: %u\n", offset);
+
+ l_gpio_chip_free(chip);
+
+ return 0;
+}
+
+static int get(int argc, char **argv)
+{
+ struct l_gpio_chip *chip;
+ struct l_gpio_reader *reader;
+ uint32_t offset;
+ uint32_t value;
+ bool res;
+
+ if (argc == 2) {
+ if (!find_line(argv[1], &chip, &offset))
+ return -1;
+ } else if (argc == 3) {
+ chip = l_gpio_chip_new(argv[1]);
+ if (!chip)
+ return -1;
+
+ offset = strtoul(argv[2], NULL, 0);
+ } else {
+ return print_usage();
+ }
+
+ printf("chip: %s\n", l_gpio_chip_get_name(chip));
+ printf("line: %u\n", offset);
+
+ reader = l_gpio_reader_new(chip, "gpio-test", 1, &offset);
+ if (!reader) {
+ l_gpio_chip_free(chip);
+ return -1;
+ }
+
+ res = l_gpio_reader_get(reader, 1, &value);
+ l_gpio_reader_free(reader);
+ l_gpio_chip_free(chip);
+
+ if (!res)
+ return -1;
+
+ printf("val: %u\n", value);
+
+ return 0;
+}
+
+static int set(int argc, char **argv)
+{
+ struct l_gpio_chip *chip;
+ struct l_gpio_writer *writer;
+ uint32_t offset;
+ uint32_t value;
+ bool res;
+
+ if (argc == 3) {
+ if (!find_line(argv[1], &chip, &offset))
+ return -1;
+
+ value = strtol(argv[2], NULL, 0);
+ } else if (argc == 4) {
+ chip = l_gpio_chip_new(argv[1]);
+ if (!chip)
+ return -1;
+
+ offset = strtoul(argv[2], NULL, 0);
+ value = strtol(argv[3], NULL, 0);
+ } else {
+ return print_usage();
+ }
+
+ printf("chip: %s\n", l_gpio_chip_get_name(chip));
+ printf("line: %u\n", offset);
+
+ writer = l_gpio_writer_new(chip, "gpio-test", 1, &offset, &value);
+ l_gpio_chip_free(chip);
+
+ if (!writer)
+ return -1;
+
+ res = l_gpio_writer_set(writer, 1, &value);
+ l_gpio_writer_free(writer);
+
+ if (!res)
+ return -1;
+
+ printf("val: %u\n", value);
+
+ return 0;
+}
+
+static int chip(int argc, char **argv)
+{
+ struct l_gpio_chip *chip;
+
+ if (argc != 2)
+ return print_usage();
+
+ chip = l_gpio_chip_new(argv[1]);
+ if (!chip)
+ return -1;
+
+ printf("chip: %s\n", l_gpio_chip_get_name(chip));
+ printf("label: %s\n", l_gpio_chip_get_label(chip));
+ printf("lines: %u\n", l_gpio_chip_get_num_lines(chip));
+
+ l_gpio_chip_free(chip);
+
+ return 0;
+}
+
+static int line(int argc, char **argv)
+{
+ struct l_gpio_chip *chip;
+ uint32_t offset;
+ char *consumer;
+ char *label;
+
+ if (argc == 2) {
+ if (!find_line(argv[1], &chip, &offset))
+ return -1;
+ } else if (argc == 3) {
+ chip = l_gpio_chip_new(argv[1]);
+ if (!chip)
+ return -1;
+
+ offset = strtoul(argv[2], NULL, 0);
+ } else {
+ return print_usage();
+ }
+
+ label = l_gpio_chip_get_line_label(chip, offset);
+ consumer = l_gpio_chip_get_line_consumer(chip, offset);
+
+ printf("chip: %s\n", l_gpio_chip_get_name(chip));
+ printf("line: %u\n", offset);
+ printf("label: %s\n", label);
+ printf("consumer: %s\n", consumer);
+
+ l_free(consumer);
+ l_free(label);
+ l_gpio_chip_free(chip);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ const char *cmd;
+
+ if (argc < 2)
+ return print_usage();
+
+ cmd = argv[1];
+ argc--;
+ argv++;
+
+ if (strcmp(cmd, "get") == 0)
+ return get(argc, argv);
+ else if (strcmp(cmd, "set") == 0)
+ return set(argc, argv);
+ else if (strcmp(cmd, "find") == 0)
+ return find(argc, argv);
+ else if (strcmp(cmd, "chip") == 0)
+ return chip(argc, argv);
+ else if (strcmp(cmd, "line") == 0)
+ return line(argc, argv);
+ else
+ return print_usage();
+}
--
2.22.0
2 years, 11 months
[PATCH] gpio: fix clang -Wpointer-bool-conversion warning
by Ossama Othman
Remove a check that incorrectly attempted to determine if the "name"
field of a struct gpioline_info object was NULL. It relied on a
pointer-to-bool conversion that didn't work as expected since the
"name" field is an array, not a pointer. The address of an array
always evaluates to true when converted to a boolean. In this case,
there is no need for a NULL pointer check.
---
ell/gpio.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/ell/gpio.c b/ell/gpio.c
index bd0e9c9..c470ba9 100644
--- a/ell/gpio.c
+++ b/ell/gpio.c
@@ -189,9 +189,6 @@ LIB_EXPORT bool l_gpio_chip_find_line_offset(struct l_gpio_chip *chip,
if (ioctl(chip->fd, GPIO_GET_LINEINFO_IOCTL, &info) < 0)
return false;
- if (!info.name)
- continue;
-
if (strcmp(info.name, line_label) != 0)
continue;
--
2.20.1
2 years, 11 months
[PATCH] dhcp: Reset lease on client stop
by Tim Kourt
The un-reset lease variable used to be freed multiple times
when the client is explicitly stopped before being destroyed.
==12374== Invalid read of size 8
==12374== at 0x46C889: _dhcp_lease_free (dhcp-lease.c:47)
==12374== by 0x46B5F2: l_dhcp_client_stop (dhcp.c:1220)
==12374== by 0x46B61D: l_dhcp_client_destroy (dhcp.c:1011)
---
ell/dhcp.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/ell/dhcp.c b/ell/dhcp.c
index f57f13b..57d5ae2 100644
--- a/ell/dhcp.c
+++ b/ell/dhcp.c
@@ -1218,6 +1218,8 @@ LIB_EXPORT bool l_dhcp_client_stop(struct l_dhcp_client *client)
CLIENT_ENTER_STATE(DHCP_STATE_INIT);
_dhcp_lease_free(client->lease);
+ client->lease = NULL;
+
return true;
}
--
2.13.6
2 years, 11 months
[PATCHv4] gpio: add get/set helpers for GPIO lines
by Martin Hundebøll
The Linux kernel GPIO api operates with chips, lines, handles, and events.
The chip and line structures represent info about gpio chips, and gpio
lines, respectively. They are used to e.g. lookup a line with a certain
name, and/or line flags.
The handle structure is used to "obtain" a handle to one or more gpio
lines on a chip. Until the file descriptor in this handle is closed, the
gpio lines cannot be used by others. The same file descriptor is used
when setting or getting the line values (one can also set the initial
value when obtaining handles for output lines).
The event structure is used to get a file descriptor that can be used
with select/poll to wait for changes in line levels.
This commit adds support for setting and getting values for one or more
gpio lines. So far three structures are implemented:
l_gpio_chip
l_gpio_writer
l_gpio_reader
The chip structure is used to look up line info, and to find line
offsets for a certain label. The chip and offset is then used to
instantiate writers and readers.
The writer and reader are created with one of more offsets on a certain
chip. For the writer case, the offsets are accompanied with the default
output values to set when requesting the gpio lines.
Functionality that could be implemented, but is postponed until the need
arises includes:
* waiting for events
* one-shot wrappers that don't hold on to gpio lines
---
Changes since v3:
* let lookup-by-label return multiple gpio chips
* implemented lookup of line offsets
* use separate reader/writer objects
* make the multi-line case the common use
Changes since v2:
* added internal reference counted structure to hold primary fd
* added l_gpio_line structure to represent a single line
* added function to lookup gpio line by label
Changes since v1:
* added gpiochip info and corresponding getters
* changed "line" to "line_num" a few places
Changes since RFC:
* added gpio.h to ell.h
* changed copyright to Geanix
* open gpiochip in l_gpio_chip_new()
* open gpiochip read-only
* added input checks
* clear ioctl structs with memset instead of = {0}
* reorder error-paths
Makefile.am | 6 +-
ell/ell.h | 1 +
ell/ell.sym | 22 +++
ell/gpio.c | 415 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/gpio.h | 81 ++++++++++
5 files changed, 523 insertions(+), 2 deletions(-)
create mode 100644 ell/gpio.c
create mode 100644 ell/gpio.h
diff --git a/Makefile.am b/Makefile.am
index 91299d4..072867f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -52,7 +52,8 @@ pkginclude_HEADERS = ell/ell.h \
ell/cert.h \
ell/ecc.h \
ell/ecdh.h \
- ell/time.h
+ ell/time.h \
+ ell/gpio.h
lib_LTLIBRARIES = ell/libell.la
@@ -123,7 +124,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/ecc-external.c \
ell/ecc.c \
ell/ecdh.c \
- ell/time.c
+ ell/time.c \
+ ell/gpio.c
ell_libell_la_LDFLAGS = -no-undefined \
-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
diff --git a/ell/ell.h b/ell/ell.h
index 691bdb2..c5cb1f2 100644
--- a/ell/ell.h
+++ b/ell/ell.h
@@ -60,3 +60,4 @@
#include <ell/ecc.h>
#include <ell/ecdh.h>
#include <ell/time.h>
+#include <ell/gpio.h>
diff --git a/ell/ell.sym b/ell/ell.sym
index e720322..99dae01 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -503,6 +503,28 @@ global:
l_ecdh_generate_shared_secret;
/* time */
l_time_now;
+ /* gpio */
+ l_gpio_chips_with_line_label;
+ l_gpio_chip_new;
+ l_gpio_chip_free;
+ l_gpio_chip_find_line_offset;
+ l_gpio_chip_get_label;
+ l_gpio_chip_get_name;
+ l_gpio_chip_get_num_lines;
+ l_gpio_chip_get_line_label;
+ l_gpio_chip_get_line_consumer;
+ l_gpio_writer_new;
+ l_gpio_writer_one_new;
+ l_gpio_writer_free;
+ l_gpio_writer_set;
+ l_gpio_writer_set_one;
+ l_gpio_writer_get_label;
+ l_gpio_reader_new;
+ l_gpio_reader_one_new;
+ l_gpio_reader_free;
+ l_gpio_reader_get;
+ l_gpio_reader_get_one;
+ l_gpio_reader_get_label;
local:
*;
};
diff --git a/ell/gpio.c b/ell/gpio.c
new file mode 100644
index 0000000..6655539
--- /dev/null
+++ b/ell/gpio.c
@@ -0,0 +1,415 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2018 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
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <linux/gpio.h>
+
+#include "private.h"
+#include "strv.h"
+#include "util.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 (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 (!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 (!chip)
+ return NULL;
+
+ return chip->label;
+}
+
+LIB_EXPORT const char *l_gpio_chip_get_name(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return NULL;
+
+ return chip->name;
+}
+
+LIB_EXPORT uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return 0;
+
+ return chip->n_lines;
+}
+
+LIB_EXPORT void l_gpio_chip_free(struct l_gpio_chip *chip)
+{
+ if (!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 (!chip)
+ return false;
+
+ if (!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 (!info.name)
+ continue;
+
+ 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 (!chip)
+ return NULL;
+
+ if (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 (!chip)
+ return NULL;
+
+ if (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 (!chip)
+ return NULL;
+
+ if (n_offsets == 0 || n_offsets > GPIOHANDLES_MAX)
+ return NULL;
+
+ if (!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 struct l_gpio_writer *l_gpio_writer_one_new(struct l_gpio_chip *chip,
+ const char *consumer,
+ const uint32_t offset,
+ const uint32_t value)
+{
+ return l_gpio_writer_new(chip, consumer, 1, &offset, &value);
+}
+
+LIB_EXPORT void l_gpio_writer_free(struct l_gpio_writer *writer)
+{
+ if (!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 (!writer)
+ return false;
+
+ if (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 bool l_gpio_writer_set_one(struct l_gpio_writer *writer, uint32_t value)
+{
+ return l_gpio_writer_set(writer, 1, &value);
+}
+
+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 (!chip)
+ return NULL;
+
+ if (n_offsets == 0 || n_offsets > GPIOHANDLES_MAX)
+ return NULL;
+
+ if (!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 struct l_gpio_reader *l_gpio_reader_one_new(struct l_gpio_chip *chip,
+ const char *consumer,
+ const uint32_t offset)
+{
+ return l_gpio_reader_new(chip, consumer, 1, &offset);
+}
+
+LIB_EXPORT void l_gpio_reader_free(struct l_gpio_reader *reader)
+{
+ if (!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 (!reader)
+ return false;
+
+ if (n_values != reader->n_offsets)
+ 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;
+}
+
+LIB_EXPORT bool l_gpio_reader_get_one(struct l_gpio_reader *reader,
+ uint32_t *value)
+{
+ return l_gpio_reader_get(reader, 1, value);
+}
diff --git a/ell/gpio.h b/ell/gpio.h
new file mode 100644
index 0000000..fb71c13
--- /dev/null
+++ b/ell/gpio.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2018 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
+ *
+ */
+
+#ifndef __ELL_GPIO_H
+#define __ELL_GPIO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct l_gpio_chip;
+struct l_gpio_writer;
+struct l_gpio_reader;
+
+char **l_gpio_chips_with_line_label(const char *line_label);
+struct l_gpio_chip *l_gpio_chip_new(const char *chip_name);
+void l_gpio_chip_free(struct l_gpio_chip *chip);
+const char *l_gpio_chip_get_label(struct l_gpio_chip *chip);
+const char *l_gpio_chip_get_name(struct l_gpio_chip *chip);
+uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip);
+bool l_gpio_chip_find_line_offset(struct l_gpio_chip *chip,
+ const char *line_label,
+ uint32_t *line_offset);
+char *l_gpio_chip_get_line_label(struct l_gpio_chip *chip, uint32_t offset);
+char *l_gpio_chip_get_line_consumer(struct l_gpio_chip *chip, uint32_t offset);
+
+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 *l_gpio_writer_one_new(struct l_gpio_chip *chip,
+ const char *consumer,
+ const uint32_t offset,
+ const uint32_t value);
+void l_gpio_writer_free(struct l_gpio_writer *writer);
+bool l_gpio_writer_set(struct l_gpio_writer *writer, uint32_t n_values,
+ const uint32_t values[]);
+bool l_gpio_writer_set_one(struct l_gpio_writer *writer, uint32_t n_value);
+const char *l_gpio_writer_get_label(struct l_gpio_writer *writer);
+
+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 *l_gpio_reader_one_new(struct l_gpio_chip *chip,
+ const char *consumer,
+ const uint32_t offset);
+void l_gpio_reader_free(struct l_gpio_reader *reader);
+bool l_gpio_reader_get(struct l_gpio_reader *reader, uint32_t n_values,
+ uint32_t values[]);
+bool l_gpio_reader_get_one(struct l_gpio_reader *reader, uint32_t *value);
+const char *l_gpio_reader_get_label(struct l_gpio_reader *reader);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_GPIO_H */
--
2.21.0
2 years, 11 months
[PATCHv5] gpio: add get/set helpers for GPIO lines
by Martin Hundebøll
The Linux kernel GPIO api operates with chips, lines, handles, and events.
The chip and line structures represent info about gpio chips, and gpio
lines, respectively. They are used to e.g. lookup a line with a certain
name, and/or line flags.
The handle structure is used to "obtain" a handle to one or more gpio
lines on a chip. Until the file descriptor in this handle is closed, the
gpio lines cannot be used by others. The same file descriptor is used
when setting or getting the line values (one can also set the initial
value when obtaining handles for output lines).
The event structure is used to get a file descriptor that can be used
with select/poll to wait for changes in line levels.
This commit adds support for setting and getting values for one or more
gpio lines. So far three structures are implemented:
l_gpio_chip
l_gpio_writer
l_gpio_reader
The chip structure is used to look up line info, and to find line
offsets for a certain label. The chip and offset is then used to
instantiate writers and readers.
The writer and reader are created with one of more offsets on a certain
chip. For the writer case, the offsets are accompanied with the default
output values to set when requesting the gpio lines.
Functionality that could be implemented, but is postponed until the need
arises includes:
* waiting for events
* one-shot wrappers that don't hold on to gpio lines
---
Changes since v4:
* use unlikely() when verifying parameters
* verify *values parameter in _get() and _set()
* no helpers for single line uses
* check for file type and prefix when scanning devices
* update copyright years
Changes since v3:
* let lookup-by-label return multiple gpio chips
* implemented lookup of line offsets
* use separate reader/writer objects
* make the multi-line case the common use
Changes since v2:
* added internal reference counted structure to hold primary fd
* added l_gpio_line structure to represent a single line
* added function to lookup gpio line by label
Changes since v1:
* added gpiochip info and corresponding getters
* changed "line" to "line_num" a few places
Changes since RFC:
* added gpio.h to ell.h
* changed copyright to Geanix
* open gpiochip in l_gpio_chip_new()
* open gpiochip read-only
* added input checks
* clear ioctl structs with memset instead of = {0}
* reorder error-paths
Makefile.am | 6 +-
ell/ell.h | 1 +
ell/ell.sym | 18 +++
ell/gpio.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/gpio.h | 72 ++++++++++
5 files changed, 496 insertions(+), 2 deletions(-)
create mode 100644 ell/gpio.c
create mode 100644 ell/gpio.h
diff --git a/Makefile.am b/Makefile.am
index 91299d4..072867f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -52,7 +52,8 @@ pkginclude_HEADERS = ell/ell.h \
ell/cert.h \
ell/ecc.h \
ell/ecdh.h \
- ell/time.h
+ ell/time.h \
+ ell/gpio.h
lib_LTLIBRARIES = ell/libell.la
@@ -123,7 +124,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/ecc-external.c \
ell/ecc.c \
ell/ecdh.c \
- ell/time.c
+ ell/time.c \
+ ell/gpio.c
ell_libell_la_LDFLAGS = -no-undefined \
-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
diff --git a/ell/ell.h b/ell/ell.h
index 691bdb2..c5cb1f2 100644
--- a/ell/ell.h
+++ b/ell/ell.h
@@ -60,3 +60,4 @@
#include <ell/ecc.h>
#include <ell/ecdh.h>
#include <ell/time.h>
+#include <ell/gpio.h>
diff --git a/ell/ell.sym b/ell/ell.sym
index e720322..ffa11d6 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -503,6 +503,24 @@ global:
l_ecdh_generate_shared_secret;
/* time */
l_time_now;
+ /* gpio */
+ l_gpio_chips_with_line_label;
+ l_gpio_chip_new;
+ l_gpio_chip_free;
+ l_gpio_chip_find_line_offset;
+ l_gpio_chip_get_label;
+ l_gpio_chip_get_name;
+ l_gpio_chip_get_num_lines;
+ l_gpio_chip_get_line_label;
+ l_gpio_chip_get_line_consumer;
+ l_gpio_writer_new;
+ l_gpio_writer_free;
+ l_gpio_writer_set;
+ l_gpio_writer_get_label;
+ l_gpio_reader_new;
+ l_gpio_reader_free;
+ l_gpio_reader_get;
+ l_gpio_reader_get_label;
local:
*;
};
diff --git a/ell/gpio.c b/ell/gpio.c
new file mode 100644
index 0000000..88bf7a4
--- /dev/null
+++ b/ell/gpio.c
@@ -0,0 +1,401 @@
+/*
+ *
+ * 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
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <linux/gpio.h>
+
+#include "private.h"
+#include "strv.h"
+#include "util.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 (!info.name)
+ continue;
+
+ 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;
+}
diff --git a/ell/gpio.h b/ell/gpio.h
new file mode 100644
index 0000000..0523cb0
--- /dev/null
+++ b/ell/gpio.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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
+ *
+ */
+
+#ifndef __ELL_GPIO_H
+#define __ELL_GPIO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct l_gpio_chip;
+struct l_gpio_writer;
+struct l_gpio_reader;
+
+char **l_gpio_chips_with_line_label(const char *line_label);
+struct l_gpio_chip *l_gpio_chip_new(const char *chip_name);
+void l_gpio_chip_free(struct l_gpio_chip *chip);
+const char *l_gpio_chip_get_label(struct l_gpio_chip *chip);
+const char *l_gpio_chip_get_name(struct l_gpio_chip *chip);
+uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip);
+bool l_gpio_chip_find_line_offset(struct l_gpio_chip *chip,
+ const char *line_label,
+ uint32_t *line_offset);
+char *l_gpio_chip_get_line_label(struct l_gpio_chip *chip, uint32_t offset);
+char *l_gpio_chip_get_line_consumer(struct l_gpio_chip *chip, uint32_t offset);
+
+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[]);
+void l_gpio_writer_free(struct l_gpio_writer *writer);
+bool l_gpio_writer_set(struct l_gpio_writer *writer, uint32_t n_values,
+ const uint32_t values[]);
+const char *l_gpio_writer_get_label(struct l_gpio_writer *writer);
+
+struct l_gpio_reader *l_gpio_reader_new(struct l_gpio_chip *chip,
+ const char *consumer,
+ uint32_t n_offsets,
+ const uint32_t offsets[]);
+void l_gpio_reader_free(struct l_gpio_reader *reader);
+bool l_gpio_reader_get(struct l_gpio_reader *reader, uint32_t n_values,
+ uint32_t values[]);
+const char *l_gpio_reader_get_label(struct l_gpio_reader *reader);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_GPIO_H */
--
2.22.0
2 years, 11 months
[PATCH] acinclude: Correct CFLAGS variable name typo.
by Ossama Othman
The "-Wvariadic-macros" compiler command line option was appended to
the "CFALGS" variables instead of "CFLAGS", which prevented it from
being used when building ELL. Change "CFALGS" to "CFLAGS".
---
acinclude.m4 | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/acinclude.m4 b/acinclude.m4
index cad86ec..1bd01d0 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -60,7 +60,7 @@ AC_DEFUN([COMPILER_FLAGS], [
CFLAGS+=" -Wredundant-decls"
CFLAGS+=" -Wswitch-enum"
CFLAGS+=" -Wtype-limits"
- CFALGS+=" -Wvariadic-macros"
+ CFLAGS+=" -Wvariadic-macros"
CFLAGS+=" -Wformat -Wformat-security"
if ( $CC -v 2>/dev/null | grep "gcc version" ); then
CFLAGS+=" -Wcast-align"
--
2.20.1
2 years, 11 months
[PATCH] dbus-client: Introduce l_dbus_client_add_proxy
by Tim Kourt
Not all DBus services expose their objects trough ObjectManager,
therefore the usage of such objects through the current client
proxy API is impossible. This patch allows to manually add
a proxy object for the well-known DBus object.
---
ell/dbus-client.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
ell/dbus-client.h | 3 +++
2 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/ell/dbus-client.c b/ell/dbus-client.c
index d2eb70f..852cf3b 100644
--- a/ell/dbus-client.c
+++ b/ell/dbus-client.c
@@ -37,6 +37,7 @@ struct l_dbus_client {
unsigned int removed_watch;
char *service;
uint32_t objects_call;
+ uint32_t add_proxy_call;
l_dbus_watch_func_t connect_cb;
void *connect_cb_data;
@@ -647,8 +648,11 @@ LIB_EXPORT void l_dbus_client_destroy(struct l_dbus_client *client)
client->proxy_cb_data_destroy(client->proxy_cb_data);
if (client->objects_call)
- l_dbus_cancel(client->dbus, client->objects_call)
-;
+ l_dbus_cancel(client->dbus, client->objects_call);
+
+ if (client->add_proxy_call)
+ l_dbus_cancel(client->dbus, client->add_proxy_call);
+
l_queue_destroy(client->proxies,
(l_queue_destroy_func_t)dbus_proxy_destroy);
@@ -730,3 +734,73 @@ LIB_EXPORT bool l_dbus_client_set_proxy_handlers(struct l_dbus_client *client,
return true;
}
+
+struct add_proxy_data {
+ struct l_dbus_client *client;
+ char *path;
+ char *interafce;
+};
+
+static void add_proxy_data_destroy(void *user_data)
+{
+ struct add_proxy_data *data = user_data;
+
+ l_free(data->path);
+ l_free(data->interafce);
+ l_free(data);
+}
+
+static void add_proxy_reply(struct l_dbus_message *message, void *user_data)
+{
+ struct add_proxy_data *data = user_data;
+ struct l_dbus_message_iter properties;
+
+ data->client->add_proxy_call = 0;
+
+ if (l_dbus_message_is_error(message))
+ return;
+
+ l_dbus_message_get_arguments(message, "a{sv}", &properties);
+
+ parse_interface(data->client, data->path, data->interafce, &properties);
+}
+
+static void add_proxy_setup(struct l_dbus_message *message, void *user_data)
+{
+ struct add_proxy_data *data = user_data;
+
+ l_dbus_message_set_arguments(message, "s", data->interafce);
+}
+
+LIB_EXPORT bool l_dbus_client_add_proxy(struct l_dbus_client *client,
+ const char *path,
+ const char *interface)
+{
+ struct add_proxy_data *data;
+
+ if (unlikely(!client || !path || !interface))
+ return false;
+
+ if (find_proxy(client, path, interface))
+ return false;
+
+ data = l_new(struct add_proxy_data, 1);
+ data->client = client;
+ data->path = l_strdup(path);
+ data->interafce = l_strdup(interface);
+
+ client->add_proxy_call =
+ l_dbus_method_call(client->dbus, client->service, path,
+ L_DBUS_INTERFACE_PROPERTIES,
+ "GetAll", add_proxy_setup,
+ add_proxy_reply, data,
+ add_proxy_data_destroy);
+
+ if (!client->add_proxy_call) {
+ add_proxy_data_destroy(data);
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/ell/dbus-client.h b/ell/dbus-client.h
index 699d824..939a6bf 100644
--- a/ell/dbus-client.h
+++ b/ell/dbus-client.h
@@ -72,6 +72,9 @@ bool l_dbus_client_set_proxy_handlers(struct l_dbus_client *client,
l_dbus_client_property_function_t property_changed,
void *user_data, l_dbus_destroy_func_t destroy);
+bool l_dbus_client_add_proxy(struct l_dbus_client *client, const char *path,
+ const char *interface);
+
const char *l_dbus_proxy_get_path(struct l_dbus_proxy *proxy);
const char *l_dbus_proxy_get_interface(struct l_dbus_proxy *proxy);
--
2.13.6
2 years, 11 months
[PATCH] unit: Add tests for AES-CMAC hashing
by Brian Gix
The two added tests come from the Bluetooth Mesh v1.0 specifications
sample data section.
---
unit/test-checksum.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 74 insertions(+)
diff --git a/unit/test-checksum.c b/unit/test-checksum.c
index f21d0bb..9cba88d 100644
--- a/unit/test-checksum.c
+++ b/unit/test-checksum.c
@@ -180,6 +180,75 @@ static void test_updatev(const void *data)
l_checksum_free(checksum);
}
+struct aes_cmac_test_vector {
+ char *plaintext;
+ char *key;
+ char *ciphertext;
+};
+
+/* Hash AES_CMAC tests based on Bluetooth Mesh published sample data */
+static const struct aes_cmac_test_vector aes_cmac_test1 = {
+ .plaintext = "74657374",
+ .key = "00000000000000000000000000000000",
+ .ciphertext = "b73cefbd641ef2ea598c2b6efb62f79c",
+};
+
+static const struct aes_cmac_test_vector aes_cmac_test2 = {
+ .plaintext = "f7a2a44f8e8a8029064f173ddc1e2b00",
+ .key = "4f90480c1871bfbffd16971f4d8d10b1",
+ .ciphertext = "2ea6467aa3378c4c545eda62935b9b86",
+};
+
+static void test_aes_cmac(const void *data)
+{
+ struct l_checksum *checksum;
+ char *encbuf;
+ size_t encbuflen;
+ char *decbuf;
+ size_t decbuflen;
+ int r;
+ bool success;
+ const struct aes_cmac_test_vector *tv = data;
+
+ size_t ptlen;
+ uint8_t *pt = l_util_from_hexstring(tv->plaintext, &ptlen) ?:
+ (uint8_t[]) {};
+ size_t keylen;
+ uint8_t *key = l_util_from_hexstring(tv->key, &keylen);
+ size_t ctlen;
+ uint8_t *ct = l_util_from_hexstring(tv->ciphertext, &ctlen) ?:
+ (uint8_t[]) {};
+
+ encbuflen = ctlen;
+ encbuf = alloca(encbuflen);
+ memset(encbuf, 0, encbuflen);
+ decbuflen = ptlen;
+ decbuf = alloca(decbuflen);
+ memset(decbuf, 0, decbuflen);
+
+ checksum = l_checksum_new_cmac_aes(key, keylen);
+ assert(checksum);
+
+ success = l_checksum_update(checksum, pt, ptlen);
+ assert(success);
+
+ ctlen = l_checksum_get_digest(checksum, encbuf, encbuflen);
+ assert(ctlen == encbuflen);
+
+ r = memcmp(encbuf, ct, ctlen);
+ assert(!r);
+
+ l_checksum_free(checksum);
+
+ if (ptlen)
+ l_free(pt);
+
+ l_free(key);
+
+ if (ctlen)
+ l_free(ct);
+}
+
int main(int argc, char *argv[])
{
l_test_init(&argc, &argv);
@@ -202,5 +271,10 @@ int main(int argc, char *argv[])
if (l_checksum_is_supported(L_CHECKSUM_SHA256, false))
l_test_add("sha256-1", test_sha256, NULL);
+ if (l_checksum_cmac_aes_supported()) {
+ l_test_add("aes-cmac-1", test_aes_cmac, &aes_cmac_test1);
+ l_test_add("aes-cmac-2", test_aes_cmac, &aes_cmac_test2);
+ }
+
return l_test_run();
}
--
2.14.5
2 years, 11 months
[PATCHv2] gpio: add simple get/set helpers for GPIO lines
by Martin Hundebøll
The Linux kernel GPIO api operates with chips, lines, handles, and
events.
The chip and line structures represent info about gpio chips, and
gpio lines, respectively. They are used to e.g. lookup a line with a
certain name, and/or line flags.
The handle structure is used to "obtain" a handle to one or more gpio
lines on a chip. Until the file descriptor in this handle is closed, the
gpio lines cannot be used by others. The same file descriptor is used
when setting or getting the line values (one can also set the initial
value when obtaining handles for output lines).
The event structure is used to get a file descriptor that can be used
with select/poll to wait for changes in line levels.
This commit add simple support for setting and getting the value for a
single gpio line. It does so by obtaining a line handle to get/set the
value, and then release the handle immediately again.
Functionality that could be implemented, but is postponed until the need
arises includes:
* looking up a gpio line by its name
* setting/getting multiple gpio lines with a single function
* waiting for events
* holding on to handles
Some of the above probably require adding structures to represent gpio
lines and events, while handles should be private to the class.
---
Changes since v1:
* added gpiochip info and corresponding getters
* changed "line" to "line_num" a few places
Changes since RFC:
* added gpio.h to ell.h
* changed copyright to Geanix
* open gpiochip in l_gpio_chip_new()
* open gpiochip read-only
* added input checks
* clear ioctl structs with memset instead of = {0}
* reorder error-paths
Makefile.am | 6 +-
ell/ell.h | 1 +
ell/ell.sym | 7 +++
ell/gpio.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/gpio.h | 49 +++++++++++++++
5 files changed, 236 insertions(+), 2 deletions(-)
create mode 100644 ell/gpio.c
create mode 100644 ell/gpio.h
diff --git a/Makefile.am b/Makefile.am
index 8401972..0ecb9a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,7 +51,8 @@ pkginclude_HEADERS = ell/ell.h \
ell/dhcp.h \
ell/cert.h \
ell/ecc.h \
- ell/ecdh.h
+ ell/ecdh.h \
+ ell/gpio.h
lib_LTLIBRARIES = ell/libell.la
@@ -119,7 +120,8 @@ ell_libell_la_SOURCES = $(linux_headers) \
ell/ecc.h \
ell/ecc-external.c \
ell/ecc.c \
- ell/ecdh.c
+ ell/ecdh.c \
+ ell/gpio.c
ell_libell_la_LDFLAGS = -no-undefined \
-Wl,--version-script=$(top_srcdir)/ell/ell.sym \
diff --git a/ell/ell.h b/ell/ell.h
index aab6417..fb1dd79 100644
--- a/ell/ell.h
+++ b/ell/ell.h
@@ -59,3 +59,4 @@
#include <ell/cert.h>
#include <ell/ecc.h>
#include <ell/ecdh.h>
+#include <ell/gpio.h>
diff --git a/ell/ell.sym b/ell/ell.sym
index 841bc49..793e4c3 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -463,6 +463,13 @@ global:
/* ecdh */
l_ecdh_generate_key_pair;
l_ecdh_generate_shared_secret;
+ /* gpio */
+ l_gpio_chip_new;
+ l_gpio_chip_free;
+ l_gpio_chip_get_label;
+ l_gpio_chip_get_num_lines;
+ l_gpio_chip_get_line_value;
+ l_gpio_chip_set_line_value;
local:
*;
};
diff --git a/ell/gpio.c b/ell/gpio.c
new file mode 100644
index 0000000..83032f9
--- /dev/null
+++ b/ell/gpio.c
@@ -0,0 +1,175 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2018 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
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <linux/gpio.h>
+
+#include "util.h"
+#include "gpio.h"
+#include "private.h"
+
+struct l_gpio_chip {
+ int fd;
+ char *label;
+ uint32_t num_lines;
+};
+
+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 ret;
+
+ if (!chip_name)
+ return NULL;
+
+ chip = l_new(struct l_gpio_chip, 1);
+
+ path = l_strdup_printf("/dev/%s", chip_name);
+ chip->fd = open(path, O_RDONLY | O_CLOEXEC);
+ l_free(path);
+
+ if (chip->fd < 0) {
+ l_free(chip);
+ return NULL;
+ }
+
+ memset(&info, 0, sizeof(info));
+
+ ret = ioctl(chip->fd, GPIO_GET_CHIPINFO_IOCTL, &info);
+ if (ret < 0) {
+ l_free(chip);
+ return NULL;
+ }
+
+ if (info.label)
+ chip->label = l_strndup(info.label, sizeof(info.label));
+
+ chip->num_lines = info.lines;
+
+ return chip;
+}
+
+LIB_EXPORT void l_gpio_chip_free(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return;
+
+ if (chip->fd >= 0)
+ close(chip->fd);
+
+ l_free(chip->label);
+ l_free(chip);
+}
+
+LIB_EXPORT const char *l_gpio_chip_get_label(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return NULL;
+
+ return chip->label;
+}
+
+LIB_EXPORT uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip)
+{
+ if (!chip)
+ return 0;
+
+ return chip->num_lines;
+}
+
+LIB_EXPORT bool l_gpio_chip_get_line_value(struct l_gpio_chip *chip,
+ uint32_t line_num, bool *value)
+{
+ struct gpiohandle_request req;
+ struct gpiohandle_data data;
+ int ret;
+
+ if (!chip)
+ return false;
+
+ if (line_num >= chip->num_lines)
+ return false;
+
+ if (chip->fd < 0)
+ return false;
+
+ memset(&req, 0, sizeof(req));
+ req.lineoffsets[0] = line_num;
+ req.lines = 1;
+ req.flags = GPIOHANDLE_REQUEST_INPUT;
+
+ ret = ioctl(chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+ if (ret < 0 || req.fd <= 0)
+ return false;
+
+ memset(&data, 0, sizeof(data));
+
+ ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
+
+ close(req.fd);
+
+ if (ret < 0)
+ return false;
+
+ if (value)
+ *value = !!data.values[0];
+
+ return true;
+}
+
+LIB_EXPORT bool l_gpio_chip_set_line_value(struct l_gpio_chip *chip,
+ uint32_t line_num, bool value)
+{
+ struct gpiohandle_request req;
+ int ret;
+
+ if (!chip)
+ return false;
+
+ if (line_num >= chip->num_lines)
+ return false;
+
+ if (chip->fd < 0)
+ return false;
+
+ memset(&req, 0, sizeof(req));
+ req.lineoffsets[0] = line_num;
+ req.lines = 1;
+ req.flags = GPIOHANDLE_REQUEST_OUTPUT;
+ req.default_values[0] = value;
+
+ ret = ioctl(chip->fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
+ if (ret < 0 || req.fd <= 0)
+ return false;
+
+ close(req.fd);
+
+ return true;
+}
diff --git a/ell/gpio.h b/ell/gpio.h
new file mode 100644
index 0000000..9f4ae6a
--- /dev/null
+++ b/ell/gpio.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Embedded Linux library
+ *
+ * Copyright (C) 2011-2018 Intel Corporation. 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
+ *
+ */
+
+#ifndef __ELL_GPIO_H
+#define __ELL_GPIO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct l_gpio_chip;
+
+struct l_gpio_chip *l_gpio_chip_new(const char *chip_name);
+void l_gpio_chip_free(struct l_gpio_chip *chip);
+
+const char *l_gpio_chip_get_label(struct l_gpio_chip *chip);
+uint32_t l_gpio_chip_get_num_lines(struct l_gpio_chip *chip);
+bool l_gpio_chip_get_line_value(struct l_gpio_chip *chip, uint32_t line_num,
+ bool *value);
+bool l_gpio_chip_set_line_value(struct l_gpio_chip *chip, uint32_t line_num,
+ bool value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ELL_GPIO_H */
--
2.20.0
2 years, 11 months