[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
[PATCH] cipher: add AES-CMAC hashing support
by Brian Gix
The algorythm is supported in the kernel, and it is used heavily by
Bluetooth Mesh, and is the one Mesh required algorythm missing from ELL.
---
ell/cipher.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
ell/cipher.h | 16 ++++++++++
unit/test-cipher.c | 74 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 178 insertions(+)
diff --git a/ell/cipher.c b/ell/cipher.c
index 7b909df..2f2e482 100644
--- a/ell/cipher.c
+++ b/ell/cipher.c
@@ -90,6 +90,7 @@ struct af_alg_iv {
static uint32_t supported_ciphers;
static uint32_t supported_aead_ciphers;
+static uint32_t supported_hash_ciphers;
struct l_cipher {
int type;
@@ -103,6 +104,11 @@ struct l_aead_cipher {
int decrypt_sk;
};
+struct l_hash_cipher {
+ int type;
+ int encrypt_sk;
+};
+
static int create_alg(const char *alg_type, const char *alg_name,
const void *key, size_t key_length, size_t tag_length)
{
@@ -248,6 +254,42 @@ error_free:
return NULL;
}
+static const char *hash_cipher_type_to_name(enum l_hash_cipher_type type)
+{
+ switch (type) {
+ case L_HASH_CIPHER_AES_CMAC:
+ return "cmac(aes)";
+ }
+
+ return NULL;
+}
+
+LIB_EXPORT struct l_hash_cipher *l_hash_cipher_new(enum l_hash_cipher_type type,
+ const void *key,
+ size_t key_length)
+{
+ struct l_hash_cipher *cipher;
+ const char *uninitialized_var(alg_name);
+
+ if (unlikely(!key))
+ return NULL;
+
+ if (type != L_HASH_CIPHER_AES_CMAC)
+ return NULL;
+
+ cipher = l_new(struct l_hash_cipher, 1);
+ cipher->type = type;
+ alg_name = hash_cipher_type_to_name(type);
+
+ cipher->encrypt_sk = create_alg("hash", alg_name, key, key_length,
+ 0);
+ if (cipher->encrypt_sk >= 0)
+ return cipher;
+
+ l_free(cipher);
+ return NULL;
+}
+
LIB_EXPORT void l_cipher_free(struct l_cipher *cipher)
{
if (unlikely(!cipher))
@@ -270,6 +312,16 @@ LIB_EXPORT void l_aead_cipher_free(struct l_aead_cipher *cipher)
l_free(cipher);
}
+LIB_EXPORT void l_hash_cipher_free(struct l_hash_cipher *cipher)
+{
+ if (unlikely(!cipher))
+ return;
+
+ close(cipher->encrypt_sk);
+
+ l_free(cipher);
+}
+
static ssize_t operate_cipher(int sk, __u32 operation,
const void *in, size_t in_len,
const void *ad, size_t ad_len,
@@ -610,6 +662,20 @@ LIB_EXPORT bool l_aead_cipher_decrypt(struct l_aead_cipher *cipher,
(ssize_t)out_len;
}
+LIB_EXPORT bool l_hash_cipher_encrypt(struct l_hash_cipher *cipher,
+ const void *in, size_t in_len,
+ void *out, size_t out_len)
+{
+ if (unlikely(!cipher))
+ return false;
+
+ if (unlikely(!in) || unlikely(!out))
+ return false;
+
+ return operate_cipher(cipher->encrypt_sk, ALG_OP_ENCRYPT, in, in_len,
+ NULL, 0, NULL, 0, out, out_len) >= 0;
+}
+
static void init_supported()
{
static bool initialized = false;
@@ -617,6 +683,7 @@ static void init_supported()
int sk;
enum l_cipher_type c;
enum l_aead_cipher_type a;
+ enum l_hash_cipher_type h;
if (likely(initialized))
return;
@@ -651,6 +718,17 @@ static void init_supported()
supported_aead_ciphers |= 1 << a;
}
+ strcpy((char *) salg.salg_type, "hash");
+
+ for (h = L_HASH_CIPHER_AES_CMAC; h <= L_HASH_CIPHER_AES_CMAC; h++) {
+ strcpy((char *) salg.salg_name, hash_cipher_type_to_name(h));
+
+ if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0)
+ continue;
+
+ supported_hash_ciphers |= 1 << h;
+ }
+
close(sk);
}
@@ -673,3 +751,13 @@ LIB_EXPORT bool l_aead_cipher_is_supported(enum l_aead_cipher_type type)
return supported_aead_ciphers & (1 << type);
}
+
+LIB_EXPORT bool l_hash_cipher_is_supported(enum l_hash_cipher_type type)
+{
+ if (type != L_HASH_CIPHER_AES_CMAC)
+ return false;
+
+ init_supported();
+
+ return supported_hash_ciphers & (1 << type);
+}
diff --git a/ell/cipher.h b/ell/cipher.h
index 84f2988..a18fbf0 100644
--- a/ell/cipher.h
+++ b/ell/cipher.h
@@ -84,8 +84,24 @@ bool l_aead_cipher_decrypt(struct l_aead_cipher *cipher,
const void *nonce, size_t nonce_len,
void *out, size_t out_len);
+struct l_hash_cipher;
+
+enum l_hash_cipher_type {
+ L_HASH_CIPHER_AES_CMAC = 0,
+};
+
+struct l_hash_cipher *l_hash_cipher_new(enum l_hash_cipher_type type,
+ const void *key, size_t key_length);
+
+void l_hash_cipher_free(struct l_hash_cipher *cipher);
+
+bool l_hash_cipher_encrypt(struct l_hash_cipher *cipher,
+ const void *in, size_t len,
+ void *out, size_t out_len);
+
bool l_cipher_is_supported(enum l_cipher_type type);
bool l_aead_cipher_is_supported(enum l_aead_cipher_type type);
+bool l_hash_cipher_is_supported(enum l_hash_cipher_type type);
#ifdef __cplusplus
}
diff --git a/unit/test-cipher.c b/unit/test-cipher.c
index 3bb7602..7d08f39 100644
--- a/unit/test-cipher.c
+++ b/unit/test-cipher.c
@@ -358,6 +358,75 @@ static void test_aead(const void *data)
l_free(tag);
}
+struct hash_test_vector {
+ enum l_hash_cipher_type type;
+ char *plaintext;
+ char *key;
+ char *ciphertext;
+};
+
+/* Hash AES_CMAC tests based on Bluetooth Mesh published sample data */
+static const struct hash_test_vector hash_test1 = {
+ .type = L_HASH_CIPHER_AES_CMAC,
+ .plaintext = "74657374",
+ .key = "00000000000000000000000000000000",
+ .ciphertext = "b73cefbd641ef2ea598c2b6efb62f79c",
+};
+
+static const struct hash_test_vector hash_test2 = {
+ .type = L_HASH_CIPHER_AES_CMAC,
+ .plaintext = "f7a2a44f8e8a8029064f173ddc1e2b00",
+ .key = "4f90480c1871bfbffd16971f4d8d10b1",
+ .ciphertext = "2ea6467aa3378c4c545eda62935b9b86",
+};
+
+static void test_hash(const void *data)
+{
+ struct l_hash_cipher *cipher;
+ char *encbuf;
+ size_t encbuflen;
+ char *decbuf;
+ size_t decbuflen;
+ int r;
+ bool success;
+ const struct hash_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);
+
+ cipher = l_hash_cipher_new(tv->type, key, keylen);
+ assert(cipher);
+
+ success = l_hash_cipher_encrypt(cipher, pt, ptlen, encbuf, encbuflen);
+ assert(success);
+
+ r = memcmp(encbuf, ct, ctlen);
+ assert(!r);
+
+ l_hash_cipher_free(cipher);
+
+ if (ptlen)
+ l_free(pt);
+
+ l_free(key);
+
+ if (ctlen)
+ l_free(ct);
+}
+
int main(int argc, char *argv[])
{
l_test_init(&argc, &argv);
@@ -388,5 +457,10 @@ int main(int argc, char *argv[])
l_test_add("aes_gcm test 6", test_aead, &gcm_test6);
}
+ if (l_hash_cipher_is_supported(L_HASH_CIPHER_AES_CMAC)) {
+ l_test_add("hash_aes_cmac test 1", test_hash, &hash_test1);
+ l_test_add("hash_aes_cmac test 2", test_hash, &hash_test2);
+ }
+
return l_test_run();
}
--
2.14.5
2 years, 11 months
[PATCH] netlink: Relax sequence check for the group notifications
by Tim Kourt
Some of the RTNL groups such as RTNLGRP_IPV4_IFADDR set the
sequence number in the header of the broadcasted messages.
In order to be able to receive the such messages, we relax the
check and allow the broadcasted messages with non-zero sequence
numbers.
---
ell/netlink.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ell/netlink.c b/ell/netlink.c
index 3d291cf..33d0e5b 100644
--- a/ell/netlink.c
+++ b/ell/netlink.c
@@ -275,7 +275,7 @@ static bool can_read_data(struct l_io *io, void *user_data)
for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, (uint32_t) len);
nlmsg = NLMSG_NEXT(nlmsg, len)) {
- if (group > 0 && nlmsg->nlmsg_seq == 0) {
+ if (group > 0) {
process_broadcast(netlink, group, nlmsg);
continue;
}
--
2.13.6
2 years, 11 months
[PATCH] unit: Fix Clang 8 "-Wstring-plus-int" warning
by Ossama Othman
Declare the fixed string in unit/test-checksum.c as a static array
instead of a string literal to address a warning exhibited by Clang 8
regarding addition of an integer to a string:
unit/test-checksum.c:170:30: warning: adding 'unsigned long' to a
string does not append to the string [-Wstring-plus-int]
iov[1].iov_base = FIXED_STR + FIXED_LEN / 2;
~~~~~~~~~~^~~~~~~~~~~~~~~
unit/test-checksum.c:170:30: note: use array indexing to silence this
warning
iov[1].iov_base = FIXED_STR + FIXED_LEN / 2;
^
& [ ]
Storing the string in an array makes it obvious that pointer
arithmetic is being performed.
---
unit/test-checksum.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/unit/test-checksum.c b/unit/test-checksum.c
index 296395f..f21d0bb 100644
--- a/unit/test-checksum.c
+++ b/unit/test-checksum.c
@@ -28,10 +28,12 @@
#include <ell/ell.h>
-#define FIXED_STR "The quick brown fox jumps over the lazy dog. " \
- "Jackdaws love my big sphinx of quartz. " \
- "Pack my box with five dozen liquor jugs. " \
- "How razorback-jumping frogs can level six piqued gymnasts!"
+static char FIXED_STR[] =
+ "The quick brown fox jumps over the lazy dog. "
+ "Jackdaws love my big sphinx of quartz. "
+ "Pack my box with five dozen liquor jugs. "
+ "How razorback-jumping frogs can level six piqued gymnasts!";
+
#define FIXED_LEN (strlen (FIXED_STR))
static void test_unsupported(const void *data)
--
2.20.1
2 years, 11 months
l_dbus_message_set_arguments
by Gix, Brian
When creating a response to a D-Bus message with
reply = l_dbus_message_new_method_return(msg);
where the original method has an empty (void) return list, is it neccessary to set the arguments:
l_dbus_message_set_arguments(reply, "");
or can I just:
return l_dbus_message_new_method_return(msg);
from within the message handler? (without setting non-existant arguments)
3 years