From 42f033afb9a398e3e3d45275701e1c1c3eccc061 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Tue, 20 Apr 2021 14:45:30 +0200 Subject: [PATCH 1/3] pkg/nimble: add RPL-over-BLE impl. (nimble_rpble) --- pkg/nimble/Makefile | 3 + pkg/nimble/Makefile.dep | 7 + pkg/nimble/Makefile.include | 3 + pkg/nimble/contrib/nimble_riot.c | 10 + pkg/nimble/rpble/Makefile | 3 + pkg/nimble/rpble/include/nimble_rpble.h | 181 +++++++++ .../rpble/include/nimble_rpble_params.h | 99 +++++ pkg/nimble/rpble/nimble_rpble.c | 347 ++++++++++++++++++ 8 files changed, 653 insertions(+) create mode 100644 pkg/nimble/rpble/Makefile create mode 100644 pkg/nimble/rpble/include/nimble_rpble.h create mode 100644 pkg/nimble/rpble/include/nimble_rpble_params.h create mode 100644 pkg/nimble/rpble/nimble_rpble.c diff --git a/pkg/nimble/Makefile b/pkg/nimble/Makefile index 11d7c071a7..b3d6ecae26 100644 --- a/pkg/nimble/Makefile +++ b/pkg/nimble/Makefile @@ -86,6 +86,9 @@ nimble_autoconn: nimble_netif: $(QQ)"$(MAKE)" -C $(TDIR)/netif/ +nimble_rpble: + $(QQ)"$(MAKE)" -C $(TDIR)/rpble/ + nimble_scanlist: $(QQ)"$(MAKE)" -C $(TDIR)/scanlist diff --git a/pkg/nimble/Makefile.dep b/pkg/nimble/Makefile.dep index 73f305fe65..07d4caf282 100644 --- a/pkg/nimble/Makefile.dep +++ b/pkg/nimble/Makefile.dep @@ -67,6 +67,13 @@ ifneq (,$(filter nimble_autoconn,$(USEMODULE))) USEMODULE += bluetil_ad endif +ifneq (,$(filter nimble_rpble,$(USEMODULE))) + USEMODULE += gnrc_rpl + USEMODULE += nimble_netif + USEMODULE += nimble_scanner + USEMODULE += bluetil_ad +endif + ifneq (,$(filter nimble_scanlist,$(USEMODULE))) USEMODULE += nimble_addr USEMODULE += bluetil_ad diff --git a/pkg/nimble/Makefile.include b/pkg/nimble/Makefile.include index f49d57ad8d..8c1beee09b 100644 --- a/pkg/nimble/Makefile.include +++ b/pkg/nimble/Makefile.include @@ -124,6 +124,9 @@ ifneq (,$(filter nimble_netif,$(USEMODULE))) CFLAGS += -DMYNEWT_VAL_BLE_LL_CFG_FEAT_DATA_LEN_EXT=1 endif endif +ifneq (,$(filter nimble_rpble,$(USEMODULE))) + INCLUDES += -I$(RIOTPKG)/nimble/rpble/include +endif ifneq (,$(filter nimble_scanlist,$(USEMODULE))) INCLUDES += -I$(RIOTPKG)/nimble/scanlist/include endif diff --git a/pkg/nimble/contrib/nimble_riot.c b/pkg/nimble/contrib/nimble_riot.c index ed700ff898..32fc005ae0 100644 --- a/pkg/nimble/contrib/nimble_riot.c +++ b/pkg/nimble/contrib/nimble_riot.c @@ -52,6 +52,11 @@ #endif #include "controller/ble_ll.h" +#ifdef MODULE_NIMBLE_RPBLE +#include "nimble_rpble.h" +#include "nimble_rpble_params.h" +#endif + static char _stack_controller[NIMBLE_CONTROLLER_STACKSIZE]; #endif @@ -160,4 +165,9 @@ void nimble_riot_init(void) extern void nimble_autoadv_init(void); nimble_autoadv_init(); #endif + +#ifdef MODULE_NIMBLE_RPBLE + res = nimble_rpble_init(&nimble_rpble_params); + assert(res == 0); +#endif } diff --git a/pkg/nimble/rpble/Makefile b/pkg/nimble/rpble/Makefile new file mode 100644 index 0000000000..5a161632a2 --- /dev/null +++ b/pkg/nimble/rpble/Makefile @@ -0,0 +1,3 @@ +MODULE = nimble_rpble + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/nimble/rpble/include/nimble_rpble.h b/pkg/nimble/rpble/include/nimble_rpble.h new file mode 100644 index 0000000000..08fd71b9db --- /dev/null +++ b/pkg/nimble/rpble/include/nimble_rpble.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2019-2021 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 pkg_nimble_rpble RPL-over-BLE for NimBLE + * @ingroup pkg_nimble + * @brief RPL-over-BLE for Nimble implementation + * + * # About + * This module implements a BLE connection manager the manages BLE connections + * for (multi-hop) IP over BLE networks based on meta data provided by RPL. + * + * + * # Concept + * In their initial state, after power up or reboot, nodes start to scan for + * BLE advertisements containing a specific advertising data (AD) field which + * holds information about RPL DODAGs. For a configured amount of time, the node + * ranks all senders of the received advertising packets based on a given + * metric. After this time, the node selects the best fitting parent based on + * this ranking and tries to connect to that peer. + * + * After a node has successfully opened a connection to its parent node, the + * node starts to advertise its own RPL context data to accept connections + * from potential child nodes. + * + * This approach leads to a BLE network topology that is equal to the IP routing + * topology created by RPL on top. + * + * ## Advertising data structure + * To include RPL context information into (legacy) BLE advertisements, it must + * be encoded into the BLE advertising data format. This implementation uses + * a custom sub-format that is included into the 16-bit UUID Service Data + * (type: 0x16) field (Supplement to Bluetooth Core Specification CSSv8, 1.11). + * The 16-bit UUID in this field is set to the IPSS service ID (0x1820). + * + * The following sub-format is used to encode the RPL context data: + * ``` + * byte 1: instance ID (1b) + * byte 2-17: DODAG ID (16b) + * byte 18: DODAG version (1b) + * byte 19: RPL role (1b) + * byte 20-21: rank (2b) + * byte 22: number of free BLE connection slots (1b) + * ``` + * + * ## Ranking of potential parents + * The currently implemented ranking metric is very simple: the potential parent + * node with the smallest rank is chosen. Additionally, nodes advertise the + * number of free BLE connection slots. In case where multiple nodes advertise + * the same RPL rank, the one with the largest number of open BLE connection + * slots is selected. The idea behind this is to balance the number of BLE + * connections per node, and with that also to balance the RPL DODAG. + * + * + * # Usage + * To use this module, simply include `nimble_rpble` into your build. If nothing + * is explicitly configured, the module will use the default configuration as + * specified in `pkg/nimble/rpble/include/nimble_rpble_params.h`. + * + * Once a node is configured as RPL root (e.g. using the `rpl root ..` shell + * command), it will automatically start to advertise itself. Non-RPL-root nodes + * will automatically scan for potential parent nodes and join the network as + * soon as they find fitting neighbors. + * + * + * ## Implementation status + * In its current state, the implementation only works for environments where a + * single RPL network with a single DODAG are present. The DODAG ID, instance + * ID, and DODAG version are currently pretty much ignored when scanning for + * potential parents. + * + * @{ + * + * @file + * @brief Interface for the nimble_rpble module + * + * @author Hauke Petersen + */ + +#ifndef NIMBLE_RPBLE_H +#define NIMBLE_RPBLE_H + +#include "nimble_netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief rpble configuration parameters + */ +typedef struct { + uint32_t scan_itvl_ms; /**< scan interval when scanning for parents, + * in ms */ + uint32_t scan_win_ms; /**< scan window when scanning for parents, + * in ms */ + uint32_t adv_itvl_ms; /**< advertising interval used when advertising + * RPL context to child nodes, in ms */ + uint32_t conn_scan_itvl_ms; /**< scan interval when connecting to parent, + * in ms */ + uint32_t conn_scan_win_ms; /**< scan window when connecting to parent, in + * ms */ + uint32_t conn_scan_to_ms; /**< timeout when connecting to parent, in ms */ + uint32_t conn_itvl_min_ms; /**< lower bound of connection interval range, + * in ms */ + uint32_t conn_itvl_max_ms; /**< upper bound of connection interval range, + in ms */ + uint16_t conn_latency; /**< used slave latency for parent connection */ + uint32_t conn_super_to_ms; /**< used supervision timeout for parent + * connection, in ms */ + uint32_t eval_itvl_min_ms; /**< amount of time a node searches for + * potential parents, lower bound in ms */ + uint32_t eval_itvl_max_ms; /**< amount of time a node searches for + * potential parents, upper bound in ms */ +} nimble_rpble_cfg_t; + +/** + * @brief RPL DODAG information + */ +typedef struct { + uint8_t inst_id; /**< instance ID */ + uint8_t dodag_id[16]; /**< DODAG ID */ + uint8_t version; /**< DODAG version */ + uint8_t role; /**< RPL role of the node */ + uint16_t rank; /**< the node's rank in the DODAG */ +} nimble_rpble_ctx_t; + +/** + * @brief Initialize the nimble_rpble module with the given parameters + * + * @note This function must be called only once, typically during system + * initialization + * + * @param[in] cfg configuration parameters + * + * @return 0 on success + */ +int nimble_rpble_init(const nimble_rpble_cfg_t *cfg); + +/** + * @brief Update the used timing parameters + * + * @param[in] cfg configuration parameters + * + * @return 0 on success + */ +int nimble_rpble_param_update(const nimble_rpble_cfg_t *cfg); + +/** + * @brief Register a callback that is called on BLE events + * + * The registered callback function is a simple pass-through of nimble_netif + * events. The callback is executed in the context of NimBLE's host thread. + * + * @param[in] cb event callback to register, set to NULL to remove + */ +int nimble_rpble_eventcb(nimble_netif_eventcb_t cb); + +/** + * @brief Update the current RPL context + * + * @note This function is meant to be called only by the RPL implementation + * + * @param[in] ctx current DODAG state + + * @return 0 on success + * @return -EALREADY if the given context did not change + */ +int nimble_rpble_update(const nimble_rpble_ctx_t *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* NIMBLE_RPBLE_H */ +/** @} */ diff --git a/pkg/nimble/rpble/include/nimble_rpble_params.h b/pkg/nimble/rpble/include/nimble_rpble_params.h new file mode 100644 index 0000000000..4f665f3595 --- /dev/null +++ b/pkg/nimble/rpble/include/nimble_rpble_params.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 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 pkg_nimble_rpble + * + * @{ + * @file + * @brief Default configuration for the nimble_netif_rpble module + * + * @author Hauke Petersen + */ + +#ifndef NIMBLE_RPBLE_PARAMS_H +#define NIMBLE_RPBLE_PARAMS_H + +#include "nimble_rpble.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Default parameters used for the nimble_netif_rpble module + * @{ + */ +#ifndef NIMBLE_RPBLE_SCAN_ITVL_MS +#define NIMBLE_RPBLE_SCAN_ITVL_MS 1200U +#endif +#ifndef NIMBLE_RPBLE_SCAN_WIN_MS +#define NIMBLE_RPBLE_SCAN_WIN_MS 120U +#endif + +#ifndef NIMBLE_RPBLE_ADV_ITVL_MS +#define NIMBLE_RPBLE_ADV_ITVL_MS 100U +#endif + +#ifndef NIMBLE_RPBLE_CONN_SCAN_ITVL_MS +#define NIMBLE_RPBLE_CONN_SCAN_ITVL_MS 120U +#endif +#ifndef NIMBLE_RPBLE_CONN_SCAN_WIN_MS +#define NIMBLE_RPBLE_CONN_SCAN_WIN_MS 120U +#endif +#ifndef NIMBLE_RPBLE_CONN_SCAN_TO_MS +#define NIMBLE_RPBLE_CONN_SCAN_TO_MS 360U +#endif +#ifndef NIMBLE_RPBLE_CONN_ITVL_MIN_MS +#define NIMBLE_RPBLE_CONN_ITVL_MIN_MS 90U +#endif +#ifndef NIMBLE_RPBLE_CONN_ITVL_MAX_MS +#define NIMBLE_RPBLE_CONN_ITVL_MAX_MS 110U +#endif +#ifndef NIMBLE_RPBLE_CONN_LATENCY +#define NIMBLE_RPBLE_CONN_LATENCY 0 +#endif +#ifndef NIMBLE_RPBLE_CONN_SUPER_TO_MS +#define NIMBLE_RPBLE_CONN_SUPER_TO_MS 1650U +#endif + +#ifndef NIMBLE_RPBLE_EVAL_ITVL_MIN_MS +#define NIMBLE_RPBLE_EVAL_ITVL_MIN_MS 12000U +#endif +#ifndef NIMBLE_RPBLE_EVAL_ITVL_MAX_MS +#define NIMBLE_RPBLE_EVAL_ITVL_MAX_MS 13000U +#endif + +#ifndef NIMBLE_RPBLE_PARAMS +#define NIMBLE_RPBLE_PARAMS \ + { .scan_itvl_ms = NIMBLE_RPBLE_SCAN_ITVL_MS, \ + .scan_win_ms = NIMBLE_RPBLE_SCAN_WIN_MS, \ + .adv_itvl_ms = NIMBLE_RPBLE_ADV_ITVL_MS, \ + .conn_scan_itvl_ms = NIMBLE_RPBLE_CONN_SCAN_ITVL_MS, \ + .conn_scan_win_ms = NIMBLE_RPBLE_CONN_SCAN_WIN_MS, \ + .conn_scan_to_ms = NIMBLE_RPBLE_CONN_SCAN_TO_MS, \ + .conn_itvl_min_ms = NIMBLE_RPBLE_CONN_ITVL_MIN_MS, \ + .conn_itvl_max_ms = NIMBLE_RPBLE_CONN_ITVL_MAX_MS, \ + .conn_latency = NIMBLE_RPBLE_CONN_LATENCY, \ + .conn_super_to_ms = NIMBLE_RPBLE_CONN_SUPER_TO_MS, \ + .eval_itvl_min_ms = NIMBLE_RPBLE_EVAL_ITVL_MIN_MS, \ + .eval_itvl_max_ms = NIMBLE_RPBLE_EVAL_ITVL_MAX_MS } +#endif +/**@}*/ + +/** + * @brief nimble_netif_rpble configuration + */ +static const nimble_rpble_cfg_t nimble_rpble_params = NIMBLE_RPBLE_PARAMS; + +#ifdef __cplusplus +} +#endif + +#endif /* NIMBLE_RPBLE_PARAMS_H */ +/** @} */ diff --git a/pkg/nimble/rpble/nimble_rpble.c b/pkg/nimble/rpble/nimble_rpble.c new file mode 100644 index 0000000000..f964871ef0 --- /dev/null +++ b/pkg/nimble/rpble/nimble_rpble.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2019-2021 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 pkg_nimble_rpble + * @{ + * + * @file + * @brief RPL-over-BLE (rpble) implementation for NimBLE and GNRC + * + * @author Hauke Petersen + * + * @} + */ + +#include +#include + +#include "random.h" +#include "net/bluetil/ad.h" +#include "net/bluetil/addr.h" +#include "net/gnrc/rpl.h" + +#include "nimble_netif.h" +#include "nimble_netif_conn.h" +#include "nimble_rpble.h" +#include "nimble_scanner.h" +#include "host/ble_gap.h" +#include "nimble/nimble_port.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/* parent state */ +#define PARENT_NONE -1 +#define PARENT_ROOT -2 +#define SCORE_NONE 0U + +/* RPL meta data context fields */ +#define AD_SVC_DATA_LEN 23 +#define POS_INST_ID 2 +#define POS_DODAG_ID 3 +#define POS_VERSION 19 +#define POS_RANK 20 +#define POS_FREE_SLOTS 22 + +/* keep the timing parameters for connections and advertisements */ +static struct ble_gap_adv_params _adv_params = { 0 }; +static struct ble_gap_conn_params _conn_params = { 0 }; +static uint32_t _conn_scan_to; /* in ms */ + +/* local RPL context */ +static nimble_rpble_ctx_t _local_rpl_ctx; +static int _current_parent = PARENT_NONE; +struct { + ble_addr_t addr; /**< address of highest scored potential parent */ + uint16_t score; /* 0 := not used, larger is better! */ +} _psel = { { 0 }, SCORE_NONE }; + +/* eval event used for periodical state updates */ +static uint32_t _eval_itvl; +static struct ble_npl_callout _evt_eval; + +static nimble_netif_eventcb_t _eventcb = NULL; + +static uint16_t _psel_score(uint16_t rank, uint8_t free) +{ + return (UINT16_MAX - rank + free); +} + +static void _children_accept(void) +{ + int res; + (void)res; + + /* stop advertising while we are updating the context */ + nimble_netif_accept_stop(); + + /* we only start advertising (accepting) if we do have an active parent and + * if we have resources for new connections */ + if ((_current_parent == PARENT_NONE) || + (nimble_netif_conn_count(NIMBLE_NETIF_UNUSED) == 0)) { + return; + } + + /* compile RPL meta data into AD service data TLV field */ + uint8_t sd[AD_SVC_DATA_LEN]; + byteorder_htolebufs(sd, BLE_GATT_SVC_IPSS); + sd[POS_INST_ID] = _local_rpl_ctx.inst_id; + memcpy(&sd[POS_DODAG_ID], _local_rpl_ctx.dodag_id, 16); + sd[POS_VERSION] = _local_rpl_ctx.version; + byteorder_htolebufs(&sd[POS_RANK], _local_rpl_ctx.rank); + sd[POS_FREE_SLOTS] = (uint8_t)nimble_netif_conn_count(NIMBLE_NETIF_UNUSED); + + /* generate the new advertising data */ + bluetil_ad_t ad; + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + res = bluetil_ad_init_with_flags(&ad, buf, BLE_HS_ADV_MAX_SZ, + BLUETIL_AD_FLAGS_DEFAULT); + assert(res == BLUETIL_AD_OK); + res = bluetil_ad_add(&ad, BLE_GAP_AD_SERVICE_DATA, sd, sizeof(sd)); + assert(res == BLUETIL_AD_OK); + + /* start advertising this node */ + res = nimble_netif_accept(ad.buf, ad.pos, &_adv_params); + assert(res == NIMBLE_NETIF_OK); +} + +static void _on_scan_evt(uint8_t type, + const ble_addr_t *addr, int8_t rssi, + const uint8_t *ad, size_t ad_len) +{ + int res; + + /* filter out all non-connectible advertisements */ + if (type != BLE_HCI_ADV_TYPE_ADV_IND) { + return; + } + + /* check if scanned node does actually speak rpble */ + bluetil_ad_data_t sd_field; + bluetil_ad_t ads = { .buf = (uint8_t *)ad, .pos = ad_len, .size = ad_len }; + res = bluetil_ad_find(&ads, BLE_GAP_AD_SERVICE_DATA, &sd_field); + if (res != BLUETIL_AD_OK) { + return; + } + if ((sd_field.len != AD_SVC_DATA_LEN) || + (byteorder_lebuftohs(sd_field.data) != BLE_GATT_SVC_IPSS)) { + return; + } + + /** + * @note Here we need to improve the filtering: so far, we consider every + * node we see that is capable of rplbe to be a parent. We should + * however also filter for instance ID and possibly the DODAG ID as + * well. On top, we should probably only consider nodes with >= + * version as parent + */ + + /* score and compare advertising peer */ + uint16_t rank = byteorder_lebuftohs(&sd_field.data[POS_RANK]); + uint8_t free = sd_field.data[POS_FREE_SLOTS]; + uint16_t score = _psel_score(rank, free); + + /* our currently preferred parent might have updated its score in the mean + * time, so we need to check that */ + if (memcmp(&_psel.addr, addr, sizeof(ble_addr_t)) == 0) { + _psel.score = score; + return; + } + + /* we consider only parents with a lower rank and remember the one with the + * best score */ + if (((_local_rpl_ctx.rank == 0) || (_local_rpl_ctx.rank > rank)) && + (score > _psel.score)) { + _psel.score = score; + memcpy(&_psel.addr, addr, sizeof(ble_addr_t)); + } +} + +static void _parent_find(void) +{ + _psel.score = SCORE_NONE; + nimble_scanner_start(); + ble_npl_callout_reset(&_evt_eval, _eval_itvl); +} + +static void _parent_find_stop(void) +{ + ble_npl_callout_stop(&_evt_eval); + nimble_scanner_stop(); +} + +static void _parent_connect(struct ble_npl_event *ev) +{ + (void)ev; + + /* for now, we only try to connect to a parent if we have none */ + assert(_current_parent == PARENT_NONE); + /* just in case this event is triggered while we were configured to be the + * RPL root */ + if (_local_rpl_ctx.role == GNRC_RPL_ROOT_NODE) { + return; + } + + /* reset timer and stop scanner */ + _parent_find_stop(); + + if (_psel.score == SCORE_NONE) { + /* no potential parent found, restarting search for one */ + _parent_find(); + return; + } + + /* try to connect to parent */ + int res = nimble_netif_connect(&_psel.addr, &_conn_params, _conn_scan_to); + if (res < 0) { + _parent_find(); + return; + } + _current_parent = res; +} + +static void _on_netif_evt(int handle, nimble_netif_event_t event, + const uint8_t *addr) +{ + (void)addr; + + switch (event) { + case NIMBLE_NETIF_CONNECTED_MASTER: + /* parent selected */ + assert(_current_parent == handle); + /* send a DIS once connected to a (new) parent) */ + gnrc_rpl_send_DIS(NULL, (ipv6_addr_t *) &ipv6_addr_all_rpl_nodes, + NULL, 0); + break; + case NIMBLE_NETIF_CONNECTED_SLAVE: + /* child added */ + _children_accept(); + break; + case NIMBLE_NETIF_CLOSED_MASTER: + /* parent lost */ + nimble_netif_accept_stop(); + _current_parent = PARENT_NONE; + /* back to 0, now we need to find a new parent... */ + _parent_find(); + break; + case NIMBLE_NETIF_CLOSED_SLAVE: + /* child lost */ + _children_accept(); + break; + case NIMBLE_NETIF_ABORT_MASTER: + /* parent selection aborted */ + nimble_netif_accept_stop(); + _current_parent = PARENT_NONE; + _parent_find(); + break; + case NIMBLE_NETIF_ABORT_SLAVE: + /* child selection aborted */ + _children_accept(); + break; + default: + /* nothing to do for all other events */ + break; + } + + /* pass events to high-level user if activated */ + if (_eventcb) { + _eventcb(handle, event, addr); + } +} + +int nimble_rpble_init(const nimble_rpble_cfg_t *cfg) +{ + /* initialize state */ + memset(&_local_rpl_ctx, 0, sizeof(_local_rpl_ctx)); + + /* initialize the eval event */ + ble_npl_callout_init(&_evt_eval, nimble_port_get_dflt_eventq(), + _parent_connect, NULL); + + return nimble_rpble_param_update(cfg); +} + +int nimble_rpble_param_update(const nimble_rpble_cfg_t *cfg) +{ + assert(cfg); + + if (_current_parent == PARENT_NONE) { + _parent_find_stop(); + } + else { + nimble_netif_accept_stop(); + } + + uint32_t itvl = random_uint32_range(cfg->eval_itvl_min_ms, + cfg->eval_itvl_max_ms); + ble_npl_time_ms_to_ticks(itvl, &_eval_itvl); + + _adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + _adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + _adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(cfg->adv_itvl_ms); + _adv_params.itvl_max = _adv_params.itvl_min; + + _conn_params.scan_itvl = BLE_GAP_SCAN_ITVL_MS(cfg->conn_scan_itvl_ms); + _conn_params.scan_window = BLE_GAP_SCAN_WIN_MS(cfg->conn_scan_win_ms); + _conn_params.latency = cfg->conn_latency; + _conn_params.supervision_timeout = + BLE_GAP_SUPERVISION_TIMEOUT_MS(cfg->conn_super_to_ms); + _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS(cfg->conn_itvl_min_ms); + _conn_params.itvl_max = BLE_GAP_CONN_ITVL_MS(cfg->conn_itvl_max_ms); + _conn_scan_to = cfg->conn_scan_to_ms; + + /* register event callback */ + nimble_netif_eventcb(_on_netif_evt); + + /* configure scanner */ + struct ble_gap_disc_params scan_params = { 0 }; + scan_params.itvl = BLE_GAP_SCAN_ITVL_MS(cfg->scan_itvl_ms); + scan_params.window = BLE_GAP_SCAN_WIN_MS(cfg->scan_win_ms); + scan_params.passive = 1; + scan_params.filter_duplicates = 1; + nimble_scanner_init(&scan_params, _on_scan_evt); + + /* start to look for parents */ + if (_current_parent == PARENT_NONE) { + _parent_find(); + } else { + _children_accept(); + } + + return 0; +} + +int nimble_rpble_eventcb(nimble_netif_eventcb_t cb) +{ + _eventcb = cb; + return 0; +} + +int nimble_rpble_update(const nimble_rpble_ctx_t *ctx) +{ + assert(ctx != NULL); + int ret = 0; + + /* if the update context is equal to what we have, ignore it */ + if (memcmp(&_local_rpl_ctx, ctx, sizeof(nimble_rpble_ctx_t)) == 0) { + ret = -EALREADY; + } + else { + /* save rpl context for future reference */ + memcpy(&_local_rpl_ctx, ctx, sizeof(nimble_rpble_ctx_t)); + if (ctx->role == GNRC_RPL_ROOT_NODE) { + _current_parent = PARENT_ROOT; + _parent_find_stop(); + } + } + + /* advertise the updated context */ + _children_accept(); + return ret; +} From f5f28347c2bd09570b9e285c87ef072585587ff1 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Tue, 20 Apr 2021 14:46:15 +0200 Subject: [PATCH 2/3] net/gnrc/rpl: add hooks for nimble_rpble --- sys/include/net/gnrc/rpl/rpble.h | 55 +++++++++++++++++++++++ sys/net/gnrc/routing/rpl/gnrc_rpl.c | 2 + sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c | 2 + 3 files changed, 59 insertions(+) create mode 100644 sys/include/net/gnrc/rpl/rpble.h diff --git a/sys/include/net/gnrc/rpl/rpble.h b/sys/include/net/gnrc/rpl/rpble.h new file mode 100644 index 0000000000..2240302408 --- /dev/null +++ b/sys/include/net/gnrc/rpl/rpble.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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_gnrc_rpl + * @{ + * + * @file + * @brief Glue code linking RPL with the NimBLE rpble connection manager + * + * @author Hauke Petersen + */ + +#ifndef NET_GNRC_RPL_RPBLE_H +#define NET_GNRC_RPL_RPBLE_H + +#include +#if IS_USED(MODULE_NIMBLE_RPBLE) +#include "nimble_rpble.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if IS_USED(MODULE_NIMBLE_RPBLE) +static inline void gnrc_rpl_rpble_update(const gnrc_rpl_dodag_t *dodag) +{ + nimble_rpble_ctx_t ctx; + ctx.inst_id = dodag->instance->id; + memcpy(ctx.dodag_id, &dodag->dodag_id, 16); + ctx.version = dodag->version; + ctx.rank = dodag->my_rank; + ctx.role = dodag->node_status; + nimble_rpble_update(&ctx); +} +#else +static inline void gnrc_rpl_rpble_update(const gnrc_rpl_dodag_t *dodag) +{ + (void)dodag; + /* do nothing here */ +} +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* NET_GNRC_RPL_RPBLE_H */ +/** @} */ diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl.c b/sys/net/gnrc/routing/rpl/gnrc_rpl.c index 1b4c7c80e2..f2b0f3749d 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl.c @@ -35,6 +35,7 @@ #include "gnrc_rpl_internal/globals.h" #include "net/gnrc/rpl.h" +#include "net/gnrc/rpl/rpble.h" #ifdef MODULE_GNRC_RPL_P2P #include "net/gnrc/rpl/p2p.h" #include "net/gnrc/rpl/p2p_dodag.h" @@ -165,6 +166,7 @@ gnrc_rpl_instance_t *gnrc_rpl_root_init(uint8_t instance_id, const ipv6_addr_t * trickle_start(gnrc_rpl_pid, &dodag->trickle, GNRC_RPL_MSG_TYPE_TRICKLE_MSG, (1 << dodag->dio_min), dodag->dio_interval_doubl, dodag->dio_redun); + gnrc_rpl_rpble_update(dodag); return inst; } diff --git a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c index 806c606be7..8a0ead7fcb 100644 --- a/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c +++ b/sys/net/gnrc/routing/rpl/gnrc_rpl_dodag.c @@ -29,6 +29,7 @@ #include "utlist.h" #include "net/gnrc/rpl.h" +#include "net/gnrc/rpl/rpble.h" #ifdef MODULE_GNRC_RPL_P2P #include "net/gnrc/rpl/p2p.h" #include "net/gnrc/rpl/p2p_dodag.h" @@ -359,6 +360,7 @@ static gnrc_rpl_parent_t *_gnrc_rpl_find_preferred_parent(gnrc_rpl_dodag_t *doda dodag->my_rank = dodag->instance->of->calc_rank(dodag, 0); if (dodag->my_rank != old_rank) { trickle_reset_timer(&dodag->trickle); + gnrc_rpl_rpble_update(dodag); } LL_FOREACH_SAFE(dodag->parents, elt, tmp) { From f24e3ffb076c91723bec2644174890855aaeef0e Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Tue, 20 Apr 2021 14:46:53 +0200 Subject: [PATCH 3/3] tests/nimble_rpble_gnrc: initial import --- tests/nimble_rpble_gnrc/Makefile | 27 ++++++++++ tests/nimble_rpble_gnrc/Makefile.ci | 4 ++ tests/nimble_rpble_gnrc/main.c | 80 +++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 tests/nimble_rpble_gnrc/Makefile create mode 100644 tests/nimble_rpble_gnrc/Makefile.ci create mode 100644 tests/nimble_rpble_gnrc/main.c diff --git a/tests/nimble_rpble_gnrc/Makefile b/tests/nimble_rpble_gnrc/Makefile new file mode 100644 index 0000000000..63f6d9e8cf --- /dev/null +++ b/tests/nimble_rpble_gnrc/Makefile @@ -0,0 +1,27 @@ +BOARD ?= nrf52dk +include ../Makefile.tests_common + +# include shell support +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# Include GNRC and RPL +USEMODULE += gnrc_netdev_default +USEMODULE += gnrc_netif_single +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_router_default +USEMODULE += gnrc_icmpv6_echo +USEMODULE += gnrc_rpl +USEMODULE += auto_init_gnrc_rpl + +# configure and use Nimble +USEMODULE += bluetil_addr +USEMODULE += nimble_rpble +NIMBLE_MAX_CONN := 3 + +DEVELHELP = 0 + +TEST_ON_CI_WHITELIST += nrf52dk + +include $(RIOTBASE)/Makefile.include diff --git a/tests/nimble_rpble_gnrc/Makefile.ci b/tests/nimble_rpble_gnrc/Makefile.ci new file mode 100644 index 0000000000..4079597a0f --- /dev/null +++ b/tests/nimble_rpble_gnrc/Makefile.ci @@ -0,0 +1,4 @@ +BOARD_INSUFFICIENT_MEMORY := \ + e104-bt5010a-tb \ + e104-bt5011a-tb \ + # diff --git a/tests/nimble_rpble_gnrc/main.c b/tests/nimble_rpble_gnrc/main.c new file mode 100644 index 0000000000..37ffa6526f --- /dev/null +++ b/tests/nimble_rpble_gnrc/main.c @@ -0,0 +1,80 @@ +/* + * 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 examples + * @{ + * + * @file + * @brief Example for showing 6LoWPAN over BLE using NimBLE and GNRC + * + * @author Hauke Petersen + * + * @} + */ + +#include +#include + +#include "msg.h" +#include "shell.h" +#include "nimble_rpble.h" +#include "net/bluetil/addr.h" + +#define MAIN_QUEUE_SIZE (8) +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +static void _dump_evt(const char *text, const uint8_t *addr) +{ + printf("[ble_event] %s (", text); + bluetil_addr_print(addr); + printf(")\n"); +} + +static void _on_rpble_event(int handle, nimble_netif_event_t event, + const uint8_t *addr) +{ + (void)handle; + + switch (event) { + case NIMBLE_NETIF_CONNECTED_MASTER: + _dump_evt("parent selected", addr); + break; + case NIMBLE_NETIF_CONNECTED_SLAVE: + _dump_evt("child added", addr); + break; + case NIMBLE_NETIF_CLOSED_MASTER: + _dump_evt("parent lost", addr); + break; + case NIMBLE_NETIF_CLOSED_SLAVE: + _dump_evt("child lost", addr); + break; + default: + /* not interested in any other BLE events here */ + break; + } + +} + +int main(void) +{ + puts("RPL-over-BLE Example Application"); + + /* register the custom event handler */ + nimble_rpble_eventcb(_on_rpble_event); + + /* we need a message queue for the thread running the shell in order to + * receive potentially fast incoming networking packets (ping6) */ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + + /* start shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +}