diff --git a/pkg/nimble/Makefile b/pkg/nimble/Makefile index d2e94311e7..7c9f0c8713 100644 --- a/pkg/nimble/Makefile +++ b/pkg/nimble/Makefile @@ -88,3 +88,6 @@ nimble_scanlist: nimble_scanner: "$(MAKE)" -C $(TDIR)/scanner + +nimble_statconn: + "$(MAKE)" -C $(TDIR)/statconn diff --git a/pkg/nimble/Makefile.dep b/pkg/nimble/Makefile.dep index f968ea40c6..6eefc0746a 100644 --- a/pkg/nimble/Makefile.dep +++ b/pkg/nimble/Makefile.dep @@ -54,6 +54,11 @@ ifneq (,$(filter nimble_scanlist,$(USEMODULE))) USEMODULE += bluetil_ad endif +ifneq (,$(filter nimble_statconn,$(USEMODULE))) + USEMODULE += nimble_netif + USEMODULE += nimble_addr +endif + ifneq (,$(filter nimble_netif,$(USEMODULE))) FEATURES_REQUIRED += ble_nimble_netif USEMODULE += l2util diff --git a/pkg/nimble/Makefile.include b/pkg/nimble/Makefile.include index 9e96117603..4209adcf9b 100644 --- a/pkg/nimble/Makefile.include +++ b/pkg/nimble/Makefile.include @@ -108,3 +108,6 @@ endif ifneq (,$(filter nimble_scanner,$(USEMODULE))) INCLUDES += -I$(RIOTPKG)/nimble/scanner/include endif +ifneq (,$(filter nimble_statconn,$(USEMODULE))) + INCLUDES += -I$(RIOTPKG)/nimble/statconn/include +endif diff --git a/pkg/nimble/contrib/nimble_riot.c b/pkg/nimble/contrib/nimble_riot.c index 39594b5756..b7cc31ca7a 100644 --- a/pkg/nimble/contrib/nimble_riot.c +++ b/pkg/nimble/contrib/nimble_riot.c @@ -35,6 +35,10 @@ #include "services/ipss/ble_svc_ipss.h" #endif +#ifdef MODULE_NIMBLE_STATCONN +#include "nimble_statconn.h" +#endif + #if defined(MODULE_NIMBLE_AUTOCONN) && !defined(MODULE_NIMBLE_AUTOCONN_NOAUTOINIT) #include "nimble_autoconn.h" #include "nimble_autoconn_params.h" @@ -127,6 +131,10 @@ void nimble_riot_init(void) ble_svc_ipss_init(); #endif +#ifdef MODULE_NIMBLE_STATCONN + nimble_statconn_init(); +#endif + #if defined(MODULE_NIMBLE_AUTOCONN) && !defined(MODULE_NIMBLE_AUTOCONN_NOAUTOINIT) ble_gatts_start(); /* CAUTION: this must be called after nimble_netif_init() and also only diff --git a/pkg/nimble/statconn/Makefile b/pkg/nimble/statconn/Makefile new file mode 100644 index 0000000000..6e7ff7467c --- /dev/null +++ b/pkg/nimble/statconn/Makefile @@ -0,0 +1,3 @@ +MODULE = nimble_statconn + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/nimble/statconn/include/nimble_statconn.h b/pkg/nimble/statconn/include/nimble_statconn.h new file mode 100644 index 0000000000..d746168c11 --- /dev/null +++ b/pkg/nimble/statconn/include/nimble_statconn.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2020 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_statconn Statconn + * @ingroup pkg_nimble + * @brief Static connection manager for NimBLE netif that keeps opens + * connections on demand and takes care of keeping them open. + * + * @experimental + * + * # WARNING + * This module is highly experimental! Expect bugs, instabilities and sudden API + * changes :-) + * + * + * # About + * Statconn is the implementation of a static connection manager for the NimBLE + * netif module. It initiates new connections when told and additionally takes + * care of reopening connections in case of connection loss. + * + * + * # Usage + * A node can either be a master, the one initiating the connection, or a + * slave, the one advertising its presence, w.r.t. to BLE connections. To open + * a connection between two nodes, call nimble_statconn_add_slave() with the + * BLE address of the slave node on the designated master node and call + * nimble_statconn_add_master() with the address of the master node on the + * designated slave node. From that point on, statconn will take care of + * opening and maintaining the connection between these two nodes. + * + * + * # Configuration + * This module is configured completely statically. All relevant configuration + * options are set at compile time using a couple of macros. + * + * @{ + * + * @file + * @brief Simple static connection manager for NimBLE netif + * + * @author Hauke Petersen + */ + +#ifndef NIMBLE_STATCONN_H +#define NIMBLE_STATCONN_H + +#include + +#include "nimble_netif.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Advertising interval used when in advertising mode [in ms] + */ +#ifndef NIMBLE_STATCONN_ADV_ITVL_MS +#define NIMBLE_STATCONN_ADV_ITVL_MS (90U) +#endif + +/** + * @brief Scan window used when opening new connections [in ms] + */ +#ifndef NIMBLE_STATCONN_CONN_WIN_MS +#define NIMBLE_STATCONN_CONN_WIN_MS (100U) +#endif + +/** + * @brief Opening a new connection is aborted after this time [in ms] + */ +#ifndef NIMBLE_STATCONN_CONN_TIMEOUT_MS +#define NIMBLE_STATCONN_CONN_TIMEOUT_MS (600U) +#endif + +/** + * @brief Connection interval used when opening a new connection [in ms] + */ +#ifndef NIMBLE_STATCONN_CONN_ITVL_MS +#define NIMBLE_STATCONN_CONN_ITVL_MS (75U) +#endif + +/** + * @brief Slave latency used for new connections [# of connection events] + */ +#ifndef NIMBLE_STATCONN_CONN_LATENCY +#define NIMBLE_STATCONN_CONN_LATENCY (0) +#endif + +/** + * @brief Supervision timeout used for new connections [in ms] + */ +#ifndef NIMBLE_STATCONN_CONN_SUPERTO_MS +#define NIMBLE_STATCONN_CONN_SUPERTO_MS (2500U) +#endif + +/** + * @brief Return codes used by the statconn module + */ +enum { + NIMBLE_STATCONN_OK = 0, /**< all groovy */ + NIMBLE_STATCONN_NOSLOT = -1, /**< no more connection slot available */ + NIMBLE_STATCONN_NOTCONN = -2, /**< given address is not managed */ + NIMBLE_STATCONN_INUSE = -3, /**< given peer is already managed */ +}; + +/** + * @brief Initialize the statconn module + * + * @warning This function **must** only be called once. Typically this is done + * during system initialization via auto-init. + */ +void nimble_statconn_init(void); + +/** + * @brief Register a callback that is called on netif 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, may be NULL + */ +void nimble_statconn_eventcb(nimble_netif_eventcb_t cb); + +/** + * @brief Connect to peer (master) with a given address as slave + * + * Adding a master will make this node advertise itself to wait for an incoming + * connection by that master. + * + * @param[in] addr BLE address of the peer + * + * @return NIMBLE_STATCONN_OK if peer was successfully added + * @return NIMBLE_STATCONN_INUSE if the peer address is already in use + * @return NIMBLE_STATCONN_NOSLOT if no empty connection slot is available + */ +int nimble_statconn_add_master(const uint8_t *addr); + +/** + * @brief Connect to a peer (slave) with a given address as master + * + * @param[in] addr BLE address of the peer + * + * @return NIMBLE_STATCONN_OK if peer was successfully added + * @return NIMBLE_STATCONN_INUSE if the peer address is already in use + * @return NIMBLE_STATCONN_NOSLOT if no empty connection slot is available + */ +int nimble_statconn_add_slave(const uint8_t *addr); + +/** + * @brief Remove the connection to the given peer + * + * @param[in] addr BLE address of the peer + * + * @return NIMBLE_STATCONN_OK if peer was successfully removed + * @return NIMBLE_STATCONN_NOTCONN if given address is not managed + */ +int nimble_statconn_rm(const uint8_t *addr); + +#ifdef __cplusplus +} +#endif + +#endif /* NIMBLE_STATCONN_H */ +/** @} */ diff --git a/pkg/nimble/statconn/nimble_statconn.c b/pkg/nimble/statconn/nimble_statconn.c new file mode 100644 index 0000000000..8f01f0a7c4 --- /dev/null +++ b/pkg/nimble/statconn/nimble_statconn.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2020 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_statconn + * @{ + * + * @file + * @brief Statconn - static connection manager for NimBLE netif + * + * @author Hauke Petersen + * + * @} + */ + +#include "assert.h" +#include "net/bluetil/addr.h" +#include "net/bluetil/ad.h" +#include "nimble_netif.h" +#include "nimble_netif_conn.h" +#include "nimble_statconn.h" + +#include "host/ble_hs.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define UNUSED (0x00) +#define PENDING (0x80) +#define CONNECTED (0x00) +#define ROLE_M (0x01) +#define ROLE_S (0x02) + +typedef struct { + uint8_t addr[BLE_ADDR_LEN]; /**< peer addr, network byte order */ + uint8_t state; /**< internal state */ +} slot_t; + +static const uint8_t _ad[2] = { BLE_GAP_AD_FLAGS, BLUETIL_AD_FLAGS_DEFAULT }; + +static mutex_t _lock = MUTEX_INIT; +static slot_t _slots[NIMBLE_NETIF_MAX_CONN]; + +static struct ble_gap_adv_params _adv_params; +static struct ble_gap_conn_params _conn_params; +static uint32_t _conn_timeout; + +static nimble_netif_eventcb_t _eventcb = NULL; + + +static slot_t *_get_addr(const uint8_t *addr) +{ + for (unsigned i = 0; i < ARRAY_SIZE(_slots); i++) { + if (memcmp(addr, _slots[i].addr, BLE_ADDR_LEN) == 0) { + return &_slots[i]; + } + } + return NULL; +} + +static slot_t *_get_state(uint8_t state) +{ + for (unsigned i = 0; i < ARRAY_SIZE(_slots); i++) { + if (_slots[i].state == state) { + return &_slots[i]; + } + } + return NULL; +} + +static void _activate(uint8_t role) +{ + mutex_lock(&_lock); + slot_t *slot = _get_state(role | PENDING); + + if (slot && (role == ROLE_M)) { + ble_addr_t peer; + peer.type = BLE_ADDR_RANDOM; + bluetil_addr_swapped_cp(slot->addr, peer.val); + nimble_netif_connect(&peer, &_conn_params, _conn_timeout); + } + else if (slot && (role == ROLE_S)) { + nimble_netif_accept(_ad, sizeof(_ad), &_adv_params); + } + mutex_unlock(&_lock); +} + +static void _update(const uint8_t *addr, uint8_t role, uint8_t state) +{ + mutex_lock(&_lock); + slot_t *slot = _get_addr(addr); + if (slot) { + slot->state = (role | state); + } + else { + DEBUG("[statconn] warning: state change on unknown peer address\n"); + } + mutex_unlock(&_lock); + + _activate(role); +} + +static void _on_netif_evt(int handle, nimble_netif_event_t event, + const uint8_t *addr) +{ + switch (event) { + case NIMBLE_NETIF_ACCEPTING: + break; + case NIMBLE_NETIF_ACCEPT_STOP: + break; + case NIMBLE_NETIF_INIT_MASTER: + break; + case NIMBLE_NETIF_INIT_SLAVE: + break; + case NIMBLE_NETIF_CONNECTED_MASTER: + _update(addr, ROLE_M, CONNECTED); + break; + case NIMBLE_NETIF_CONNECTED_SLAVE: + _update(addr, ROLE_S, CONNECTED); + break; + case NIMBLE_NETIF_CLOSED_MASTER: + _update(addr, ROLE_M, PENDING); + break; + case NIMBLE_NETIF_CLOSED_SLAVE: + _update(addr, ROLE_S, PENDING); + break; + case NIMBLE_NETIF_ABORT_MASTER: + _update(addr, ROLE_M, PENDING); + break; + case NIMBLE_NETIF_ABORT_SLAVE: + _update(addr, ROLE_S, PENDING); + break; + case NIMBLE_NETIF_CONN_UPDATED: + break; + default: + /* this should never happen */ + assert(0); + } + + /* pass events to high-level user if someone registered for them */ + if (_eventcb) { + _eventcb(handle, event, addr); + } +} + +static int _be(uint8_t role, const uint8_t *addr) +{ + mutex_lock(&_lock); + slot_t *s = _get_addr(addr); + if (s != NULL) { + mutex_unlock(&_lock); + return NIMBLE_STATCONN_INUSE; + } + s = _get_state(UNUSED); + if (s == NULL) { + mutex_unlock(&_lock); + return NIMBLE_STATCONN_NOSLOT; + } + + s->state = (role | PENDING); + memcpy(s->addr, addr, BLE_ADDR_LEN); + mutex_unlock(&_lock); + _activate(role); + return NIMBLE_STATCONN_OK; +} + +void nimble_statconn_init(void) +{ + memset(_slots, 0, sizeof(_slots)); + + /* set the advertising parameters used */ + _adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + _adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + _adv_params.itvl_min = ((NIMBLE_STATCONN_ADV_ITVL_MS * 1000) / BLE_HCI_ADV_ITVL); + _adv_params.itvl_max = ((NIMBLE_STATCONN_ADV_ITVL_MS * 1000) / BLE_HCI_ADV_ITVL); + _adv_params.channel_map = 0; + _adv_params.filter_policy = 0; + _adv_params.high_duty_cycle = 0; + + /* set connection parameters */ + _conn_params.scan_itvl = ((NIMBLE_STATCONN_CONN_WIN_MS * 1000) / BLE_HCI_SCAN_ITVL); + _conn_params.scan_window = ((NIMBLE_STATCONN_CONN_WIN_MS * 1000) / BLE_HCI_SCAN_ITVL); + _conn_params.itvl_min = ((NIMBLE_STATCONN_CONN_ITVL_MS * 1000) / BLE_HCI_CONN_ITVL); + _conn_params.itvl_max = ((NIMBLE_STATCONN_CONN_ITVL_MS * 1000) / BLE_HCI_CONN_ITVL); + _conn_params.latency = NIMBLE_STATCONN_CONN_LATENCY; + _conn_params.supervision_timeout = (NIMBLE_STATCONN_CONN_SUPERTO_MS / 10); + _conn_params.min_ce_len = 0; + _conn_params.max_ce_len = 0; + _conn_timeout = NIMBLE_STATCONN_CONN_TIMEOUT_MS; + + /* register our event callback */ + nimble_netif_eventcb(_on_netif_evt); +} + +void nimble_statconn_eventcb(nimble_netif_eventcb_t cb) +{ + _eventcb = cb; +} + +int nimble_statconn_add_master(const uint8_t *addr) +{ + return _be(ROLE_S, addr); +} + +int nimble_statconn_add_slave(const uint8_t *addr) +{ + return _be(ROLE_M, addr); +} + +int nimble_statconn_rm(const uint8_t *addr) +{ + mutex_lock(&_lock); + slot_t *s = _get_addr(addr); + if (s == NULL) { + mutex_unlock(&_lock); + return NIMBLE_STATCONN_NOTCONN; + } + uint8_t role = (s->state & ROLE_M) ? ROLE_M : ROLE_S; + + if (role == ROLE_S) { + nimble_netif_accept_stop(); + } + s->state = UNUSED; + memset(s->addr, 0, sizeof(BLE_ADDR_LEN)); + mutex_unlock(&_lock); + + nimble_netif_close(nimble_netif_conn_get_by_addr(addr)); + if (role == ROLE_S) { + _activate(ROLE_S); + } + + return NIMBLE_STATCONN_OK; +} diff --git a/sys/shell/commands/Makefile b/sys/shell/commands/Makefile index d4a6f8d486..aa938d6801 100644 --- a/sys/shell/commands/Makefile +++ b/sys/shell/commands/Makefile @@ -106,6 +106,10 @@ ifneq (,$(filter nimble_netif,$(USEMODULE))) SRC += sc_nimble_netif.c endif +ifneq (,$(filter nimble_statconn,$(USEMODULE))) + SRC += sc_nimble_statconn.c +endif + ifneq (,$(filter suit_coap,$(USEMODULE))) SRC += sc_suit.c endif diff --git a/sys/shell/commands/sc_nimble_netif.c b/sys/shell/commands/sc_nimble_netif.c index 0776bdd713..3d2323c4e0 100644 --- a/sys/shell/commands/sc_nimble_netif.c +++ b/sys/shell/commands/sc_nimble_netif.c @@ -29,7 +29,7 @@ #include "net/bluetil/ad.h" #include "net/bluetil/addr.h" -#ifndef MODULE_NIMBLE_AUTOCONN +#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) #include "nimble_scanlist.h" #include "nimble_scanner.h" #endif @@ -38,7 +38,7 @@ #define DEFAULT_SCAN_DURATION (500U) /* 500ms */ #define DEFAULT_CONN_TIMEOUT (500U) /* 500ms */ -#ifndef MODULE_NIMBLE_AUTOCONN +#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) static const char *_name_to_connect = NULL; static void _scan_for_name(uint8_t type, const ble_addr_t *addr, int8_t rssi, @@ -194,7 +194,7 @@ static void _cmd_info(void) puts(""); } -#ifndef MODULE_NIMBLE_AUTOCONN +#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) static void _cmd_adv(const char *name) { int res; @@ -366,7 +366,7 @@ static int _ishelp(char *argv) void sc_nimble_netif_init(void) { -#ifndef MODULE_NIMBLE_AUTOCONN +#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) /* setup the scanning environment */ nimble_scanlist_init(); @@ -378,7 +378,7 @@ void sc_nimble_netif_init(void) int _nimble_netif_handler(int argc, char **argv) { if ((argc == 1) || _ishelp(argv[1])) { -#ifndef MODULE_NIMBLE_AUTOCONN +#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) printf("usage: %s [help|info|adv|scan|connect|close|update]\n", argv[0]); #else printf("usage: %s [help|info|close|update]\n", argv[0]); @@ -389,7 +389,7 @@ int _nimble_netif_handler(int argc, char **argv) _cmd_info(); } -#ifndef MODULE_NIMBLE_AUTOCONN +#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) else if (memcmp(argv[1], "adv", 3) == 0) { char *name = NULL; if (argc > 2) { diff --git a/sys/shell/commands/sc_nimble_statconn.c b/sys/shell/commands/sc_nimble_statconn.c new file mode 100644 index 0000000000..dc4c746018 --- /dev/null +++ b/sys/shell/commands/sc_nimble_statconn.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 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 sys_shell_commands + * @{ + * + * @file + * @brief Shell commands to control the NimBLE netif statconn connection + * manager + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "net/bluetil/addr.h" +#include "nimble_statconn.h" + +int _nimble_statconn_handler(int argc, char **argv) +{ + if ((argc < 3)) { + printf("usage: %s \n", argv[0]); + return 0; + } + + /* parse address */ + uint8_t addr[BLE_ADDR_LEN]; + if (bluetil_addr_from_str(addr, argv[2]) == NULL) { + puts("err: unable to parse BLE address"); + return 1; + } + + if (strncmp(argv[1], "addm", 4) == 0) { + if (nimble_statconn_add_master(addr) == NIMBLE_STATCONN_OK) { + puts("success: connecting to peer as slave"); + } + else { + puts("err: unable to add peer"); + } + } + else if (strncmp(argv[1], "adds", 4) == 0) { + if (nimble_statconn_add_slave(addr) == NIMBLE_STATCONN_OK) { + puts("success: connecting to peer as master"); + } + else { + puts("err: unable to add peer"); + } + } + else if (strncmp(argv[1], "rm", 2) == 0) { + if (nimble_statconn_rm(addr) == NIMBLE_STATCONN_OK) { + puts("success: closed connection to peer"); + } + else { + puts("err: unable to remove peer"); + } + } + else { + puts("err: unable to parse command"); + return 1; + } + + return 0; +} diff --git a/sys/shell/commands/shell_commands.c b/sys/shell/commands/shell_commands.c index c9466a1290..57cc2d2db7 100644 --- a/sys/shell/commands/shell_commands.c +++ b/sys/shell/commands/shell_commands.c @@ -171,6 +171,10 @@ extern int _loramac_handler(int argc, char **argv); extern int _nimble_netif_handler(int argc, char **argv); #endif +#ifdef MODULE_NIMBLE_STATCONN +extern int _nimble_statconn_handler(int argc, char **argv); +#endif + #ifdef MODULE_SUIT_COAP extern int _suit_handler(int argc, char **argv); #endif @@ -300,6 +304,9 @@ const shell_command_t _shell_command_list[] = { #ifdef MODULE_NIMBLE_NETIF { "ble", "Manage BLE connections for NimBLE", _nimble_netif_handler }, #endif +#ifdef MODULE_NIMBLE_STATCONN + { "statconn", "NimBLE netif statconn", _nimble_statconn_handler}, +#endif #ifdef MODULE_SUIT_COAP { "suit", "Trigger a SUIT firmware update", _suit_handler }, #endif diff --git a/tests/nimble_statconn_gnrc/Makefile b/tests/nimble_statconn_gnrc/Makefile new file mode 100644 index 0000000000..e26a88f540 --- /dev/null +++ b/tests/nimble_statconn_gnrc/Makefile @@ -0,0 +1,22 @@ +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 += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_router_default +USEMODULE += auto_init_gnrc_rpl +USEMODULE += gnrc_rpl +USEMODULE += gnrc_icmpv6_echo + +# Setup Nimble +USEMODULE += nimble_statconn + +TEST_ON_CI_WHITELIST += nrf52dk + +include $(RIOTBASE)/Makefile.include diff --git a/tests/nimble_statconn_gnrc/main.c b/tests/nimble_statconn_gnrc/main.c new file mode 100644 index 0000000000..ffc45a567f --- /dev/null +++ b/tests/nimble_statconn_gnrc/main.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 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 Run IP-over-BLE using the 'statconn' BLE connection manager + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "shell.h" +#include "msg.h" +#include "nimble_statconn.h" +#include "net/bluetil/addr.h" + +#define MAIN_QUEUE_SIZE (8) +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +static void _print_evt(const char *msg, int handle, const uint8_t *addr) +{ + printf("[ble] %s (%i|", msg, handle); + if (addr) { + bluetil_addr_print(addr); + } + else { + printf("n/a"); + } + puts(")"); +} + +static void _on_ble_evt(int handle, nimble_netif_event_t event, + const uint8_t *addr) +{ + switch (event) { + case NIMBLE_NETIF_CONNECTED_MASTER: + _print_evt("CONNECTED master", handle, addr); + break; + case NIMBLE_NETIF_CONNECTED_SLAVE: + _print_evt("CONNECTED slave", handle, addr); + break; + case NIMBLE_NETIF_CLOSED_MASTER: + _print_evt("CLOSED master", handle, addr); + break; + case NIMBLE_NETIF_CLOSED_SLAVE: + _print_evt("CLOSED slave", handle, addr); + break; + case NIMBLE_NETIF_CONN_UPDATED: + _print_evt("UPDATED", handle, addr); + break; + default: + /* do nothing */ + return; + } +} + +int main(void) +{ + puts("IPv6-over-BLE with statconn BLE connection manager"); + + /* we need a message queue for the thread running the shell in order to + * receive potentially fast incoming networking packets */ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + + /* register for BLE events */ + nimble_statconn_eventcb(_on_ble_evt); + + /* start shell */ + puts("All up, running the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should never be reached */ + return 0; +}