diff --git a/sys/Makefile b/sys/Makefile index aa14640edd..e0da84091c 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -133,6 +133,9 @@ endif ifneq (,$(filter cord_ep,$(USEMODULE))) DIRS += net/application_layer/cord/ep endif +ifneq (,$(filter bluetil_%,$(USEMODULE))) + DIRS += net/ble/bluetil +endif DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE)))) diff --git a/sys/include/net/bluetil/ad.h b/sys/include/net/bluetil/ad.h new file mode 100644 index 0000000000..59ba6379bd --- /dev/null +++ b/sys/include/net/bluetil/ad.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_bluetil_ad BLE Advertising Data (AD) Processing + * @ingroup net + * @brief Generic BLE advertising and scan request data processing + * + * This module provides functionality for user friendly reading and writing of + * BLE advertising and scan request buffers. + * + * This module is independent from any BLE stack. + * + * @{ + * + * @file + * @brief Interface for the generic BLE advertising data processing module + * + * @author Hauke Petersen + */ + +#ifndef NET_BLUETIL_AD_H +#define NET_BLUETIL_AD_H + +#include +#include +#include + +#include "net/ble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Static initializer for the advertising data structure + */ +#define BLUETIL_AD_INIT(b,p,s) { .buf = b, .pos = p, .size = s } + +/** + * @brief Default flags to set when advertising BLE devices + */ +#define BLUETIL_AD_FLAGS_DEFAULT (BLE_GAP_DISCOVERABLE | \ + BLE_GAP_FLAG_BREDR_NOTSUP) + +/** + * @brief Return values used by the bluetil_ad module + */ +enum { + BLUETIL_AD_OK = 0, /**< everything went as expected */ + BLUETIL_AD_NOTFOUND = -1, /**< entry not found */ + BLUETIL_AD_NOMEM = -2, /**< insufficient memory to write field */ +}; + +/** + * @brief Struct used for returning the contents of a selected field + */ +typedef struct { + uint8_t *data; /**< pointer a field's payload */ + size_t len; /**< length of the payload */ +} bluetil_ad_data_t; + +/** + * @brief Descriptor for a buffer containing advertising data + */ +typedef struct { + uint8_t *buf; /**< buffer containing the advertising data */ + size_t pos; /**< current write position in the buffer */ + size_t size; /**< overall length of the buffer */ +} bluetil_ad_t; + +/** + * @brief Initialize the given advertising data descriptor + * + * @param[out] ad advertising data descriptor + * @param[in] buf buffer to point to + * @param[in] pos current fill position of @p buf + * @param[in] size size of @p buf + */ +void bluetil_ad_init(bluetil_ad_t *ad, void *buf, size_t pos, size_t size); + +/** + * @brief Find a specific field in the given advertising data + * + * @param[in] ad advertising data descriptor + * @param[in] type field type to look for + * @param[out] data position and length of the field's payload + * + * @return BLUETIL_AD_OK if field was found + * @return BLUETIL_AD_NOTFOUND if field was not found + */ +int bluetil_ad_find(const bluetil_ad_t *ad, + uint8_t type, bluetil_ad_data_t *data); + +/** + * @brief Find the given field and copy its payload into a string + * + * The resulting string is `\0` terminated. If the resulting string is too large + * to fit into the given buffer, it will be truncated to the maximum possible + * size (but still including the `\0` at the end). + * + * @param[in] ad advertising data descriptor + * @param[in] type field type to look for + * @param[out] str resulting string is written to this buffer + * @param[in] str_len maximum number of bytes to write to @p str, including + * the `\0` character + * + * @return BLUETIL_AD_OK if the field was found and copied + * @return BLUETIL_AD_NOTFOUND if the given field was not found + */ +int bluetil_ad_find_str(const bluetil_ad_t *ad, uint8_t type, + char *str, size_t str_len); + +/** + * @brief Add a new field to the given advertising data + * + * @param[out] ad advertising data descriptor + * @param[in] type field type to add + * @param[in] data payload for the field + * @param[in] data_len length of the payload in bytes + * + * @return BLUETIL_AD_OK if the new field was added + * @return BLUETIL_AD_NOMEM if there is not enough space to write add field + */ +int bluetil_ad_add(bluetil_ad_t *ad, uint8_t type, + const void *data, size_t data_len); + +/** + * @brief Convenience function to add the "flags" field + * + * @param[out] ad advertising data descriptor + * @param[in] flags flags to set + * + * @return BLUETIL_AD_OK if the flags field was added + * @return BLUETIL_AD_NOMEM if there is not enough space to write add field + */ +static inline int bluetil_ad_add_flags(bluetil_ad_t *ad, uint8_t flags) +{ + return bluetil_ad_add(ad, BLE_GAP_AD_FLAGS, &flags, 1); +} + +/** + * @brief Convenience function to add the "full name" field + * + * While the given name must be `\0` terminated, the termination character is + * not written to the advertising data. + * + * @param[out] ad advertising data descriptor + * @param[in] name name to set + * + * @return BLUETIL_AD_OK if the name field was added + * @return BLUETIL_AD_NOMEM if there is not enough space to add the name field + */ +static inline int bluetil_ad_add_name(bluetil_ad_t *ad, const char *name) +{ + return bluetil_ad_add(ad, BLE_GAP_AD_NAME, name, strlen(name)); +} + +/** + * @brief Convenience function for initializing the advertising data + * descriptor and directly adding the flags field + * + * Most users will want to set the (mandatory) flags field right after + * initializing the advertising context, so this function provides a small + * shortcut. + * + * @param[out] ad advertising data descriptor + * @param[out] buf buffer to write advertising data into + * @param[in] buf_len size of @p buf in bytes + * @param[in] flags flags to set, typically BLUETIL_AD_FLAGS_DEFAULT + * + * @return BLUETIL_AD_OK on successful initialization with flags field + * @return BLUETIL_AD_NOMEM if given buffer is too small + */ +static inline int bluetil_ad_init_with_flags(bluetil_ad_t *ad, + void *buf, size_t buf_len, + uint8_t flags) +{ + bluetil_ad_init(ad, buf, 0, buf_len); + return bluetil_ad_add_flags(ad, flags); +} + +/* add more convenience functions on demand... */ + +#ifdef __cplusplus +} +#endif + +#endif /* NET_BLUETIL_AD_H */ +/** @} */ diff --git a/sys/net/ble/bluetil/Makefile b/sys/net/ble/bluetil/Makefile new file mode 100644 index 0000000000..52667fba6b --- /dev/null +++ b/sys/net/ble/bluetil/Makefile @@ -0,0 +1,3 @@ +DIRS += $(filter bluetil_%,$(USEMODULE)) + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/ble/bluetil/bluetil_ad/Makefile b/sys/net/ble/bluetil/bluetil_ad/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/net/ble/bluetil/bluetil_ad/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/ble/bluetil/bluetil_ad/bluetil_ad.c b/sys/net/ble/bluetil/bluetil_ad/bluetil_ad.c new file mode 100644 index 0000000000..f07a955166 --- /dev/null +++ b/sys/net/ble/bluetil/bluetil_ad/bluetil_ad.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup net_bluetil_ad + * @{ + * + * @file + * @brief Implementation of the generic BLE advertising data processing + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "assert.h" +#include "net/bluetil/ad.h" + +#define POS_TYPE (1U) +#define POS_DATA (2U) + +void bluetil_ad_init(bluetil_ad_t *ad, void *buf, size_t pos, size_t size) +{ + assert(ad); + assert(buf); + + ad->buf = buf; + ad->pos = pos; + ad->size = size; +} + +int bluetil_ad_find(const bluetil_ad_t *ad, uint8_t type, + bluetil_ad_data_t *data) +{ + assert(ad); + assert(data); + + unsigned pos = 0; + + while ((pos + POS_TYPE) < ad->pos) { + uint8_t len = ad->buf[pos]; + + if (ad->buf[pos + POS_TYPE] == type) { + data->data = ad->buf + pos + POS_DATA; + data->len = len - 1; /* take away the type field */ + return BLUETIL_AD_OK; + } + + pos += (len + 1); + } + + return BLUETIL_AD_NOTFOUND; +} + +int bluetil_ad_find_str(const bluetil_ad_t *ad, uint8_t type, + char *str, size_t str_len) +{ + bluetil_ad_data_t f; + int res = bluetil_ad_find(ad, type, &f); + if (res != BLUETIL_AD_OK) { + return res; + } + + size_t len = (f.len >= str_len) ? (str_len - 1) : f.len; + memcpy(str, f.data, len); + str[len] = '\0'; + + return BLUETIL_AD_OK; +} + +int bluetil_ad_add(bluetil_ad_t *ad, uint8_t field_type, + const void *data, size_t data_len) +{ + assert(ad); + + if ((ad->pos + 2 + data_len) > ad->size) { + return BLUETIL_AD_NOMEM; + } + ad->buf[ad->pos++] = data_len + 1; + ad->buf[ad->pos++] = field_type; + memcpy(&ad->buf[ad->pos], data, data_len); + ad->pos += data_len; + + return BLUETIL_AD_OK; +}