diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 9d1cae3e56..5a5c9ab281 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -154,8 +154,11 @@ PSEUDOMODULES += netstats_rpl PSEUDOMODULES += nimble PSEUDOMODULES += nimble_adv_ext PSEUDOMODULES += nimble_autoconn_% +PSEUDOMODULES += nimble_netif_ext PSEUDOMODULES += nimble_phy_coded PSEUDOMODULES += nimble_phy_2mbit +PSEUDOMODULES += nimble_rpble_ext +PSEUDOMODULES += nimble_statconn_ext PSEUDOMODULES += newlib PSEUDOMODULES += newlib_gnu_source PSEUDOMODULES += newlib_nano diff --git a/pkg/nimble/Makefile b/pkg/nimble/Makefile index b51b048a57..832f06411c 100644 --- a/pkg/nimble/Makefile +++ b/pkg/nimble/Makefile @@ -23,7 +23,7 @@ else CFLAGS += -Wno-unused-but-set-variable endif -IGNORE := nimble_autoconn_% nimble_phy_% nimble_adv_ext +IGNORE := nimble_autoconn_% nimble_phy_% nimble_%_ext SUBMODS := $(filter-out $(IGNORE),$(filter nimble_%,$(USEMODULE))) .PHONY: all diff --git a/pkg/nimble/Makefile.dep b/pkg/nimble/Makefile.dep index f9e44d4f98..2388eefc15 100644 --- a/pkg/nimble/Makefile.dep +++ b/pkg/nimble/Makefile.dep @@ -12,6 +12,15 @@ USEMODULE += nimble_riot_contrib # RIOT port USEMODULE += nimble_porting_nimble +# Pull in dependencies based on used features +ifneq (,$(filter ble_phy_coded,$(FEATURES_USED))) + USEMODULE += nimble_phy_coded +endif + +ifneq (,$(filter ble_phy_2mbit,$(FEATURES_USED))) + USEMODULE += nimble_phy_2mbit +endif + # NOTE: this dependency depends on inclusion order, for it to work properly # mynewt-core should be selected as nimble backend as early as possible, # i.e. at the application level. @@ -56,6 +65,10 @@ ifneq (,$(filter nimble_autoadv,$(USEMODULE))) USEMODULE += bluetil_ad endif +ifneq (,$(filter nimble_autoconn_ext,$(USEMODULE))) + USEMODULE += nimble_netif_ext +endif + ifneq (,$(filter nimble_autoconn_%,$(USEMODULE))) USEMODULE += nimble_autoconn endif @@ -67,10 +80,6 @@ ifneq (,$(filter nimble_autoconn,$(USEMODULE))) USEMODULE += bluetil_ad endif -ifneq (,$(filter nimble_phy_%,$(USEMODULE))) - USEMODULE += nimble_adv_ext -endif - ifneq (,$(filter nimble_phy_2mbit,$(USEMODULE))) FEATURES_REQUIRED += ble_phy_2mbit endif @@ -79,6 +88,11 @@ ifneq (,$(filter nimble_phy_coded,$(USEMODULE))) FEATURES_REQUIRED += ble_phy_coded endif +ifneq (,$(filter nimble_rpble_ext,$(USEMODULE))) + USEMODULE += nimble_rpble + USEMODULE += nimble_netif_ext +endif + ifneq (,$(filter nimble_rpble,$(USEMODULE))) USEMODULE += gnrc_rpl USEMODULE += nimble_netif @@ -92,12 +106,22 @@ ifneq (,$(filter nimble_scanlist,$(USEMODULE))) USEMODULE += ztimer_usec endif +ifneq (,$(filter nimble_statconn_ext,$(USEMODULE))) + USEMODULE += nimble_statconn + USEMODULE += nimble_netif_ext +endif + ifneq (,$(filter nimble_statconn,$(USEMODULE))) USEMODULE += random USEMODULE += nimble_netif USEMODULE += nimble_addr endif +ifneq (,$(filter nimble_netif_ext,$(USEMODULE))) + USEMODULE += nimble_netif + USEMODULE += nimble_adv_ext +endif + ifneq (,$(filter nimble_netif,$(USEMODULE))) FEATURES_REQUIRED += ble_nimble_netif USEMODULE += random diff --git a/pkg/nimble/Makefile.include b/pkg/nimble/Makefile.include index 813f0c16ab..2bc10547d7 100644 --- a/pkg/nimble/Makefile.include +++ b/pkg/nimble/Makefile.include @@ -26,6 +26,14 @@ ifneq (,$(filter nimble_controller,$(USEMODULE))) ifneq (,$(filter nimble_drivers_nrf5x,$(USEMODULE))) INCLUDES += $(NIMIBASE)/nimble/drivers/$(CPU_FAM)/include endif + + # Enable additional PHY modes if requested by the build + ifneq (,$(filter ble_phy_2mbit,$(FEATURES_USED))) + CFLAGS += -DMYNEWT_VAL_BLE_LL_CFG_FEAT_LE_2M_PHY=1 + endif + ifneq (,$(filter ble_phy_coded,$(FEATURES_USED))) + CFLAGS += -DMYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CODED_PHY=1 + endif endif # include nimble host headers @@ -94,18 +102,6 @@ ifneq (,$(filter nimble_adv_ext,$(USEMODULE))) endif endif -ifneq (,$(filter nimble_phy_2mbit,$(USEMODULE))) - ifneq (,$(filter nimble_controller,$(USEMODULE))) - CFLAGS += -DMYNEWT_VAL_BLE_LL_CFG_FEAT_LE_2M_PHY=1 - endif -endif - -ifneq (,$(filter nimble_phy_coded,$(USEMODULE))) - ifneq (,$(filter nimble_controller,$(USEMODULE))) - CFLAGS += -DMYNEWT_VAL_BLE_LL_CFG_FEAT_LE_CODED_PHY=1 - endif -endif - ifneq (,$(filter nimble_netif,$(USEMODULE))) INCLUDES += -I$(RIOTPKG)/nimble/netif/include diff --git a/pkg/nimble/autoconn/include/nimble_autoconn.h b/pkg/nimble/autoconn/include/nimble_autoconn.h index d9e23bfb4f..d600b650e2 100644 --- a/pkg/nimble/autoconn/include/nimble_autoconn.h +++ b/pkg/nimble/autoconn/include/nimble_autoconn.h @@ -131,29 +131,31 @@ enum { */ typedef struct { /** amount of time spend in scanning mode [in ms] */ - uint32_t period_scan; + uint32_t period_scan_ms; /** amount of time spend in advertising mode [in ms] */ - uint32_t period_adv; + uint32_t period_adv_ms; /** a random value from 0 to this value is added to the duration of each * scanning and advertising period [in ms] */ - uint32_t period_jitter; + uint32_t period_jitter_ms; /** advertising interval used when in advertising mode [in ms] */ - uint32_t adv_itvl; + uint32_t adv_itvl_ms; /** scan interval applied while in scanning state [in ms] */ - uint32_t scan_itvl; + uint32_t scan_itvl_ms; /** scan window applied while in scanning state [in ms] */ - uint32_t scan_win; + uint32_t scan_win_ms; /** opening a new connection is aborted after this time [in ms] */ - uint32_t conn_timeout; + uint32_t conn_timeout_ms; /** connection interval used when opening a new connection, lower bound. * [in ms] */ - uint32_t conn_itvl_min; + uint32_t conn_itvl_min_ms; /** connection interval, upper bound [in ms] */ - uint32_t conn_itvl_max; - /** slave latency used for new connections [in ms] */ + uint32_t conn_itvl_max_ms; + /** slave latency used for new connections */ uint16_t conn_latency; /** supervision timeout used for new connections [in ms] */ - uint32_t conn_super_to; + uint32_t conn_super_to_ms; + /** BLE PHY mode to use */ + nimble_phy_t phy_mode; /** node ID included in the advertising data, may be NULL */ const char *node_id; } nimble_autoconn_params_t; diff --git a/pkg/nimble/autoconn/include/nimble_autoconn_params.h b/pkg/nimble/autoconn/include/nimble_autoconn_params.h index 67532286f9..121bb03491 100644 --- a/pkg/nimble/autoconn/include/nimble_autoconn_params.h +++ b/pkg/nimble/autoconn/include/nimble_autoconn_params.h @@ -64,24 +64,32 @@ extern "C" { #define NIMBLE_AUTOCONN_CONN_SVTO_MS (2500U) /* 2.5s */ #endif +/** + * @brief Default BLE PHY mode used by autoconn + */ +#ifndef NIMBLE_AUTOCONN_PHY_MODE +#define NIMBLE_AUTOCONN_PHY_MODE NIMBLE_PHY_1M +#endif + #ifndef NIMBLE_AUTOCONN_NODE_ID #define NIMBLE_AUTOCONN_NODE_ID "RIOT-autoconn" #endif #ifndef NIMBLE_AUTOCONN_PARAMS #define NIMBLE_AUTOCONN_PARAMS \ - { .period_scan = NIMBLE_AUTOCONN_PERIOD_SCAN_MS, \ - .period_adv = NIMBLE_AUTOCONN_PERIOD_ADV_MS, \ - .period_jitter = NIMBLE_AUTOCONN_PERIOD_JITTER_MS, \ - .adv_itvl = NIMBLE_AUTOCONN_ADV_ITVL_MS, \ - .scan_itvl = NIMBLE_AUTOCONN_SCAN_ITVL_MS, \ - .scan_win = NIMBLE_AUTOCONN_SCAN_WIN_MS, \ - .conn_timeout = NIMBLE_AUTOCONN_CONN_TIMEOUT_MS, \ - .conn_itvl_min = NIMBLE_AUTOCONN_CONN_ITVL_MIN_MS, \ - .conn_itvl_max = NIMBLE_AUTOCONN_CONN_ITVL_MAX_MS, \ - .conn_latency = NIMBLE_AUTOCONN_CONN_LATENCY, \ - .conn_super_to = NIMBLE_AUTOCONN_CONN_SVTO_MS, \ - .node_id = NIMBLE_AUTOCONN_NODE_ID, } + { .period_scan_ms = NIMBLE_AUTOCONN_PERIOD_SCAN_MS, \ + .period_adv_ms = NIMBLE_AUTOCONN_PERIOD_ADV_MS, \ + .period_jitter_ms = NIMBLE_AUTOCONN_PERIOD_JITTER_MS, \ + .adv_itvl_ms = NIMBLE_AUTOCONN_ADV_ITVL_MS, \ + .scan_itvl_ms = NIMBLE_AUTOCONN_SCAN_ITVL_MS, \ + .scan_win_ms = NIMBLE_AUTOCONN_SCAN_WIN_MS, \ + .conn_timeout_ms = NIMBLE_AUTOCONN_CONN_TIMEOUT_MS, \ + .conn_itvl_min_ms = NIMBLE_AUTOCONN_CONN_ITVL_MIN_MS, \ + .conn_itvl_max_ms = NIMBLE_AUTOCONN_CONN_ITVL_MAX_MS, \ + .conn_latency = NIMBLE_AUTOCONN_CONN_LATENCY, \ + .conn_super_to_ms = NIMBLE_AUTOCONN_CONN_SVTO_MS, \ + .phy_mode = NIMBLE_AUTOCONN_PHY_MODE, \ + .node_id = NIMBLE_AUTOCONN_NODE_ID, } #endif /**@}*/ diff --git a/pkg/nimble/autoconn/nimble_autoconn.c b/pkg/nimble/autoconn/nimble_autoconn.c index ddb9d4e8b2..5598da9d50 100644 --- a/pkg/nimble/autoconn/nimble_autoconn.c +++ b/pkg/nimble/autoconn/nimble_autoconn.c @@ -55,9 +55,8 @@ static volatile uint8_t _enabled = 0; static bluetil_ad_t _ad; static uint8_t _ad_buf[BLE_HS_ADV_MAX_SZ]; -static struct ble_gap_adv_params _adv_params; -static struct ble_gap_conn_params _conn_params; -static uint32_t _conn_timeout; +static nimble_netif_accept_cfg_t _accept_params; +static nimble_netif_connect_cfg_t _conn_params; static struct ble_npl_callout _state_evt; static ble_npl_time_t _timeout_adv_period; @@ -77,7 +76,7 @@ static void _on_state_change(struct ble_npl_event *ev) /* stop scanning */ nimble_scanner_stop(); /* start advertising/accepting */ - int res = nimble_netif_accept(_ad.buf, _ad.pos, &_adv_params); + int res = nimble_netif_accept(_ad.buf, _ad.pos, &_accept_params); assert((res == 0) || (res == -ENOMEM)); (void)res; @@ -137,11 +136,18 @@ static void _on_scan_evt(uint8_t type, const ble_addr_t *addr, { (void)info; +#if IS_USED(MODULE_NIMBLE_AUTOCONN_EXT) + if ((type != (NIMBLE_SCANNER_EXT_ADV | BLE_HCI_ADV_CONN_MASK)) || + (info->status != BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE)) { + return; + } +#else /* we are only interested in ADV_IND packets, the rest can be dropped right * away */ if (type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND) { return; } +#endif bluetil_ad_t ad = { .buf = (uint8_t *)ad_buf, @@ -157,7 +163,7 @@ static void _on_scan_evt(uint8_t type, const ble_addr_t *addr, nimble_scanner_stop(); DEBUG("[autoconn] SCAN success, initiating connection\n"); _state = STATE_CONN; - int res = nimble_netif_connect(addr, &_conn_params, _conn_timeout); + int res = nimble_netif_connect(addr, &_conn_params); assert(res >= 0); (void)res; } @@ -271,47 +277,66 @@ int nimble_autoconn_update(const nimble_autoconn_params_t *params, } /* scan and advertising period configuration */ - ble_npl_time_ms_to_ticks(params->period_adv, &_timeout_adv_period); - ble_npl_time_ms_to_ticks(params->period_scan, &_timeout_scan_period); - ble_npl_time_ms_to_ticks(params->period_jitter, &_period_jitter); + ble_npl_time_ms_to_ticks(params->period_adv_ms, &_timeout_adv_period); + ble_npl_time_ms_to_ticks(params->period_scan_ms, &_timeout_scan_period); + ble_npl_time_ms_to_ticks(params->period_jitter_ms, &_period_jitter); /* populate the connection parameters */ - _conn_params.scan_itvl = BLE_GAP_SCAN_ITVL_MS(params->scan_win); - _conn_params.scan_window = _conn_params.scan_itvl; - _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS(params->conn_itvl_min); - _conn_params.itvl_max = BLE_GAP_CONN_ITVL_MS(params->conn_itvl_max); - _conn_params.latency = 0; - _conn_params.supervision_timeout = BLE_GAP_SUPERVISION_TIMEOUT_MS( - params->conn_super_to); - _conn_params.min_ce_len = 0; - _conn_params.max_ce_len = 0; - _conn_timeout = params->conn_timeout; + memset(&_conn_params, 0, sizeof(_conn_params)); + _conn_params.scan_itvl_ms = params->scan_itvl_ms; + _conn_params.scan_window_ms = params->scan_win_ms; + _conn_params.conn_itvl_min_ms = params->conn_itvl_min_ms; + _conn_params.conn_itvl_max_ms = params->conn_itvl_max_ms; + _conn_params.conn_supervision_timeout_ms = params->conn_super_to_ms; + _conn_params.conn_slave_latency = params->conn_latency; + _conn_params.timeout_ms = params->conn_timeout_ms; +#if IS_USED(MODULE_NIMBLE_AUTOCONN_EXT) + _conn_params.phy_mode = params->phy_mode; +#else + _conn_params.phy_mode = NIMBLE_PHY_1M; +#endif + _conn_params.own_addr_type = nimble_riot_own_addr_type; /* we use the same values to updated existing connections */ struct ble_gap_upd_params conn_update_params; - conn_update_params.itvl_min = _conn_params.itvl_min; - conn_update_params.itvl_max = _conn_params.itvl_max; - conn_update_params.latency = _conn_params.latency; - conn_update_params.supervision_timeout = _conn_params.supervision_timeout; + conn_update_params.itvl_min = BLE_GAP_CONN_ITVL_MS(params->conn_itvl_min_ms); + conn_update_params.itvl_max = BLE_GAP_CONN_ITVL_MS(params->conn_itvl_max_ms); + conn_update_params.latency = params->conn_latency; + conn_update_params.supervision_timeout = + BLE_GAP_SUPERVISION_TIMEOUT_MS(params->conn_super_to_ms); conn_update_params.min_ce_len = 0; conn_update_params.max_ce_len = 0; /* calculate the used scan parameters */ nimble_scanner_cfg_t scan_params; - scan_params.itvl_ms = params->scan_itvl; - scan_params.win_ms = params->scan_win; - scan_params.flags = NIMBLE_SCANNER_PASSIVE - | NIMBLE_SCANNER_FILTER_DUPS - | NIMBLE_SCANNER_PHY_1M; + scan_params.itvl_ms = params->scan_itvl_ms; + scan_params.win_ms = params->scan_win_ms; + scan_params.flags = (NIMBLE_SCANNER_PASSIVE | NIMBLE_SCANNER_FILTER_DUPS); +#if IS_USED(MODULE_NIMBLE_AUTOCONN_EXT) && IS_USED(MODULE_NIMBLE_PHY_CODED) + if (params->phy_mode == NIMBLE_PHY_CODED) { + scan_params.flags |= NIMBLE_SCANNER_PHY_CODED; + } + else { + scan_params.flags |= NIMBLE_SCANNER_PHY_1M; + } +#else + scan_params.flags |= NIMBLE_SCANNER_PHY_1M; +#endif /* 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 = BLE_GAP_ADV_ITVL_MS(params->adv_itvl); - _adv_params.itvl_max = _adv_params.itvl_min; - _adv_params.channel_map = 0; - _adv_params.filter_policy = 0; - _adv_params.high_duty_cycle = 0; + memset(&_accept_params, 0, sizeof(_accept_params)); +#if IS_USED(MODULE_NIMBLE_AUTOCONN_EXT) + _accept_params.flags = 0; + _accept_params.primary_phy = params->phy_mode; + _accept_params.secondary_phy = params->phy_mode; +#else + _accept_params.flags = NIMBLE_NETIF_FLAG_LEGACY; + _accept_params.primary_phy = NIMBLE_PHY_1M; + _accept_params.secondary_phy = NIMBLE_PHY_1M; +#endif + _accept_params.adv_itvl_ms = params->adv_itvl_ms; + _accept_params.timeout_ms = BLE_HS_FOREVER; + _accept_params.own_addr_type = nimble_riot_own_addr_type; /* initialize the advertising data that will be used */ if (adlen > 0) { diff --git a/pkg/nimble/contrib/include/nimble_riot.h b/pkg/nimble/contrib/include/nimble_riot.h index b61aebd1c3..25d3392a2c 100644 --- a/pkg/nimble/contrib/include/nimble_riot.h +++ b/pkg/nimble/contrib/include/nimble_riot.h @@ -69,6 +69,20 @@ extern "C" { #define NIMBLE_HOST_STACKSIZE (THREAD_STACKSIZE_DEFAULT) #endif +/** + * @brief BLE PHY modes + */ +typedef enum { + NIMBLE_PHY_INVALID, /**< PHY mode invalid */ + NIMBLE_PHY_1M, /**< legacy 1Mbit PHY mode (always supported) */ +#if IS_USED(MODULE_NIMBLE_PHY_2MBIT) + NIMBLE_PHY_2M, /**< 2Mbit PHY mode */ +#endif +#if IS_USED(MODULE_NIMBLE_PHY_CODED) + NIMBLE_PHY_CODED, /**< Coded (long range) PHY mode */ +#endif +} nimble_phy_t; + /** * @brief Export our own address type for later usage */ diff --git a/pkg/nimble/netif/include/nimble_netif.h b/pkg/nimble/netif/include/nimble_netif.h index 9b019ddd40..e955d1a7c5 100644 --- a/pkg/nimble/netif/include/nimble_netif.h +++ b/pkg/nimble/netif/include/nimble_netif.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Freie Universität Berlin + * Copyright (C) 2018-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 @@ -70,6 +70,7 @@ #include #include "net/ble.h" +#include "nimble_riot.h" #include "host/ble_hs.h" @@ -103,6 +104,46 @@ extern "C" { #define NIMBLE_NETIF_MTU (1280U) #endif +/** + * @brief Flags for enabling legacy advertisement and high-duty cycle mode + * when accepting incoming connections + */ +enum { + NIMBLE_NETIF_FLAG_LEGACY = 0x01, /**< use legacy advertising mode */ + NIMBLE_NETIF_FLAG_HD_MODE = 0x02, /**< use high duty cycle mode, only + * valid for direct advertising */ +}; + +/** + * @brief Parameter set used to configure accepting connections (advertising) + */ +typedef struct { + uint8_t flags; /**< flags */ + uint8_t channel_map; /**< specify custom channel map */ + uint8_t own_addr_type; /**< specify our own address type to use */ + int8_t tx_power; /**< specify TX power to be used */ + uint32_t adv_itvl_ms; /**< advertising interval [ms] */ + uint32_t timeout_ms; /**< stop accepting after this time [ms] */ + nimble_phy_t primary_phy; /**< primary PHY mode */ + nimble_phy_t secondary_phy; /**< secondary PHY mode */ +} nimble_netif_accept_cfg_t; + +/** + * @brief Parameter set used to configure connection initiation + */ +typedef struct { + uint16_t scan_itvl_ms; /**< scan interval [ms] */ + uint16_t scan_window_ms; /**< scan window [ms] */ + uint16_t conn_itvl_min_ms; /**< connection interval, lower bound [ms] */ + uint16_t conn_itvl_max_ms; /**< connection interval, upper bound [ms] */ + uint16_t conn_supervision_timeout_ms; /**< supervision timeout [ms] */ + uint16_t conn_slave_latency;/**< slave latency */ + uint32_t timeout_ms; /**< abort connection initiation after this time + * [ms] */ + uint8_t phy_mode; /**< PHY mode used for the connection */ + uint8_t own_addr_type; /**< specify our own address type to use */ +} nimble_netif_connect_cfg_t; + /** * @brief Set to > 0 to enforce different connection intervals for each of the * nodes BLE connections @@ -197,19 +238,18 @@ void nimble_netif_eventcb(nimble_netif_eventcb_t cb); * * @param[in] addr address of the advertising BLE slave, in the NimBLE * addr format (little endian) - * @param[in] conn_params connection (timing) parameters, set to NULL to use - * NimBLEs default parameters - * @param[in] timeout connect timeout [in ms] + * @param[in] cfg connection parameters * * @return the used connection handle on success - * @return -EBUSY if already connected to the given address or if - * a connection setup procedure is in progress + * @return -EBUSY if already connected to the given address or if a connection + * setup procedure is in progress * @return -ENOMEM if no connection context memory is available * @return -ECANCELED if unable to find valid connection interval + * @return -EINVAL if unable to apply given PHY mode + * @return -EIO on all other NimBLE errors */ int nimble_netif_connect(const ble_addr_t *addr, - struct ble_gap_conn_params *conn_params, - uint32_t timeout); + const nimble_netif_connect_cfg_t *cfg); /** * @brief Close the connection with the given handle @@ -219,6 +259,7 @@ int nimble_netif_connect(const ble_addr_t *addr, * @return 0 on success * @return -EINVAL if the handle is invalid * @return -ENOTCONN if context for given handle is not connected + * @return -EIO on all other NimBLE errors */ int nimble_netif_close(int handle); @@ -227,36 +268,39 @@ int nimble_netif_close(int handle); * * @param[in] ad advertising data (in BLE AD format) * @param[in] ad_len length of @p ad in bytes - * @param[in] adv_params advertising (timing) parameters to use + * @param[in] cfg advertising parameters to use * * @return 0 on success * @return -EALREADY if already advertising * @return -ENOMEM on insufficient connection memory + * @return -EINVAL on invalid configuration parameters + * @return -ECANCELED on other errors */ int nimble_netif_accept(const uint8_t *ad, size_t ad_len, - const struct ble_gap_adv_params *adv_params); + const nimble_netif_accept_cfg_t *cfg); /** * @brief Wait for an incoming connection from a specific peer, sending - * directed advertisements (IND_DIR) + * directed advertisements * * @param[in] addr BLE address of the target peer - * @param[in] timeout_ms stop advertising after this time (in ms), set to - * BLE_HS_FOREVER to disable timeout - * @param[in] adv_params advertising (timing) parameters to use + * @param[in] cfg advertising parameters to use * * @return 0 on success * @return -EALREADY if already advertising * @return -ENOMEM on insufficient connection memory + * @return -EINVAL on invalid configuration parameters + * @return -ECANCELED on other errors */ -int nimble_netif_accept_direct(const ble_addr_t *addr, uint32_t timeout_ms, - const struct ble_gap_adv_params *adv_params); +int nimble_netif_accept_direct(const ble_addr_t *addr, + const nimble_netif_accept_cfg_t *cfg); /** * @brief Stop accepting incoming connections (stop advertising) * * * @return 0 on success * @return -EALREADY if not currently advertising + * @return -EIO on other NimBLE errors */ int nimble_netif_accept_stop(void); diff --git a/pkg/nimble/netif/nimble_netif.c b/pkg/nimble/netif/nimble_netif.c index ab307bccb1..7477520c64 100644 --- a/pkg/nimble/netif/nimble_netif.c +++ b/pkg/nimble/netif/nimble_netif.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018-2019 Freie Universität Berlin + * Copyright (C) 2018-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 @@ -55,6 +55,8 @@ #define NIMBLE_NETIF_PRIO GNRC_NETIF_PRIO #endif +#define EXT_ADV_INST 0 + /* thread flag used for signaling transmit readiness */ #define FLAG_TX_UNSTALLED (1u << 13) #define FLAG_TX_NOTCONN (1u << 12) @@ -543,10 +545,14 @@ static int _on_gap_slave_evt(struct ble_gap_event *event, void *arg) /* nothing to do here */ break; case BLE_GAP_EVENT_ADV_COMPLETE: { - uint8_t addr[BLE_ADDR_LEN]; - nimble_netif_conn_free(handle, addr); - _notify(handle, NIMBLE_NETIF_ACCEPT_STOP, addr); + if (conn->state == NIMBLE_NETIF_ADV) { + uint8_t addr[BLE_ADDR_LEN]; + nimble_netif_conn_free(handle, addr); + _notify(handle, NIMBLE_NETIF_ACCEPT_STOP, addr); + } } + case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: + break; default: break; } @@ -577,14 +583,10 @@ void nimble_netif_eventcb(nimble_netif_eventcb_t cb) } int nimble_netif_connect(const ble_addr_t *addr, - struct ble_gap_conn_params *conn_params, - uint32_t timeout) + const nimble_netif_connect_cfg_t *params) { assert(addr); - assert(_eventcb); - - uint16_t itvl_min = 0; - uint16_t itvl_max = 0; + assert(params); /* the netif_conn module expects addresses in network byte order */ uint8_t addrn[BLE_ADDR_LEN]; @@ -602,33 +604,63 @@ int nimble_netif_connect(const ble_addr_t *addr, return -ENOMEM; } - if ((conn_params != NULL) - && (conn_params->itvl_min != conn_params->itvl_max)) { - /* we need to save the min/max intervals in order to restore them - * later on */ - itvl_min = conn_params->itvl_min; - itvl_max = conn_params->itvl_max; - - uint16_t itvl = nimble_netif_conn_gen_itvl(itvl_min, itvl_max); - if (itvl == 0) { - return -ECANCELED; - } - conn_params->itvl_min = itvl; - conn_params->itvl_max = itvl; + /* generate connection interval */ + uint16_t itvl = params->conn_itvl_min_ms; + if (params->conn_itvl_min_ms < params->conn_itvl_max_ms) { + itvl = nimble_netif_conn_gen_itvl(params->conn_itvl_min_ms, + params->conn_itvl_max_ms); + } + if (itvl == 0) { + nimble_netif_conn_free(handle, NULL); + return -ECANCELED; } - int res = ble_gap_connect(nimble_riot_own_addr_type, addr, timeout, - conn_params, _on_gap_master_evt, (void *)handle); - assert(res == 0); - (void)res; + struct ble_gap_conn_params p = { + .scan_itvl = BLE_GAP_SCAN_ITVL_MS(params->scan_itvl_ms), + .scan_window = BLE_GAP_SCAN_WIN_MS(params->scan_window_ms), + .itvl_min = BLE_GAP_CONN_ITVL_MS(itvl), + .itvl_max = BLE_GAP_CONN_ITVL_MS(itvl), + .latency = params->conn_slave_latency, + .supervision_timeout = BLE_GAP_SUPERVISION_TIMEOUT_MS( + params->conn_supervision_timeout_ms), + .min_ce_len = 0, + .max_ce_len = 0, + }; - if (itvl_min != itvl_max) { - conn_params->itvl_min = itvl_min; - conn_params->itvl_max = itvl_max; +#if MYNEWT_VAL_BLE_EXT_ADV + uint8_t phy_mask; + if (params->phy_mode == NIMBLE_PHY_1M) { + phy_mask = BLE_GAP_LE_PHY_1M_MASK; + } +#if IS_USED(MODULE_NIMBLE_PHY_2MBIT) + else if (params->phy_mode == NIMBLE_PHY_2M) { + phy_mask = (BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK); + } +#endif +#if IS_USED(MODULE_NIMBLE_PHY_CODED) + else if (params->phy_mode == NIMBLE_PHY_CODED) { + phy_mask = BLE_GAP_LE_PHY_CODED_MASK; + } +#endif + else { + return -EINVAL; + } + + int res = ble_gap_ext_connect(params->own_addr_type, addr, + params->timeout_ms, phy_mask, &p, &p, &p, + _on_gap_master_evt, (void *)handle); +#else + uint32_t timeout = (params->timeout_ms == 0) ? BLE_HS_FOREVER + : params->timeout_ms; + int res = ble_gap_connect(params->own_addr_type, addr, + timeout, &p, + _on_gap_master_evt, (void *)handle); +#endif + if (res != 0) { + return -EIO; } _notify(handle, NIMBLE_NETIF_INIT_MASTER, addrn); - return handle; } @@ -642,63 +674,168 @@ int nimble_netif_close(int handle) return -ENOTCONN; } - int res = ble_gap_terminate(ble_l2cap_get_conn_handle(conn->coc), - BLE_ERR_REM_USER_CONN_TERM); - assert(res == 0); - (void)res; + int res = ble_gap_terminate(conn->gaphandle, BLE_ERR_REM_USER_CONN_TERM); + if (res != 0) { + return -EIO; + } return 0; } -static int _accept(const uint8_t *ad, size_t ad_len, const ble_addr_t *addr, - uint32_t timeout, - const struct ble_gap_adv_params *adv_params) +#if MYNEWT_VAL_BLE_EXT_ADV +static int _get_phy_hci(uint8_t mode) { - assert(adv_params); + switch (mode) { + case NIMBLE_PHY_1M: + return BLE_HCI_LE_PHY_1M; +#if IS_USED(MODULE_NIMBLE_PHY_2MBIT) + case NIMBLE_PHY_2M: + return BLE_HCI_LE_PHY_2M; +#endif +#if IS_USED(MODULE_NIMBLE_PHY_CODED) + case NIMBLE_PHY_CODED: + return BLE_HCI_LE_PHY_CODED; +#endif + default: + return -1; + } +} +#endif +static int _accept(const uint8_t *ad, size_t ad_len, const ble_addr_t *addr, + const nimble_netif_accept_cfg_t *params) +{ int handle; int res; (void)res; + assert(params); + /* allocate a connection context for incoming connections */ handle = nimble_netif_conn_start_adv(); if (handle < 0) { return handle; } - /* set advertisement data */ - if (ad != NULL) { - res = ble_gap_adv_set_data(ad, (int)ad_len); - assert(res == 0); - } /* remember address if applicable */ if (addr) { nimble_netif_conn_t *conn = nimble_netif_conn_get(handle); bluetil_addr_swapped_cp(addr->val, conn->addr); } - /* remember context and start advertising */ - res = ble_gap_adv_start(nimble_riot_own_addr_type, addr, timeout, - adv_params, _on_gap_slave_evt, (void *)handle); - assert(res == 0); +#if MYNEWT_VAL_BLE_EXT_ADV + struct ble_gap_ext_adv_params p; + memset(&p, 0, sizeof(p)); + + /* figure out PHY modes */ + int phy_pri = _get_phy_hci(params->primary_phy); + int phy_sec = _get_phy_hci(params->secondary_phy); + if ((phy_pri < 0) || (phy_sec < 0)) { + nimble_netif_conn_free(handle, NULL); + return -EINVAL; + } + /* the 2M PHY is not allowed as primary phy, we need to used the 1M PHY + * instead. This is for convenience so uses may define 2M as primary PHY */ + if (phy_pri == BLE_HCI_LE_PHY_2M) { + phy_pri = BLE_HCI_LE_PHY_1M; + } + + if (addr != NULL) { + p.directed = 1; + memcpy(&p.peer, addr, sizeof(p.peer)); + if (params->flags & NIMBLE_NETIF_FLAG_HD_MODE) { + p.high_duty_directed = 1; + } + } + else { + p.connectable = 1; + } + if (params->flags & NIMBLE_NETIF_FLAG_LEGACY) { + p.legacy_pdu = 1; + /* legacy connectable PDUs are always scannable */ + p.scannable = 1; + } + p.itvl_min = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms); + p.itvl_max = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms); + p.channel_map = params->channel_map; + p.own_addr_type = params->own_addr_type; + p.primary_phy = (uint8_t)phy_pri; + p.secondary_phy = (uint8_t)phy_sec; + p.tx_power = params->tx_power; + + res = ble_gap_ext_adv_configure(EXT_ADV_INST, &p, NULL, + _on_gap_slave_evt, (void *)handle); + if (res != 0) { + nimble_netif_conn_free(handle, NULL); + return -EINVAL; + } + + if (ad != NULL) { + struct os_mbuf *data = os_msys_get_pkthdr(ad_len, 0); + if (data == NULL) { + nimble_netif_conn_free(handle, NULL); + return -ENOMEM; + } + res = os_mbuf_append(data, ad, ad_len); + if (res != 0) { + os_mbuf_free_chain(data); + nimble_netif_conn_free(handle, NULL); + return -ENOMEM; + } + res = ble_gap_ext_adv_set_data(EXT_ADV_INST, data); + assert(res == 0); + } + res = ble_gap_ext_adv_start(EXT_ADV_INST, params->timeout_ms / 10, 0); +#else + uint8_t mode = (addr != NULL) ? BLE_GAP_CONN_MODE_DIR + : BLE_GAP_CONN_MODE_UND; + struct ble_gap_adv_params p = { + .conn_mode = mode, + .disc_mode = BLE_GAP_DISC_MODE_GEN, + .itvl_min = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms), + .itvl_max = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms), + .channel_map = params->channel_map, + .filter_policy = 0, + .high_duty_cycle = (params->flags & NIMBLE_NETIF_FLAG_HD_MODE) ? 1 : 0, + }; + + /* set advertisement data, if applicable */ + if (ad != NULL) { + res = ble_gap_adv_set_data(ad, (int)ad_len); + if (res != 0) { + nimble_netif_conn_free(handle, NULL); + return -EINVAL; + } + } + + /* start advertising */ + uint32_t timeout = (params->timeout_ms == 0) ? BLE_HS_FOREVER + : params->timeout_ms; + res = ble_gap_adv_start(params->own_addr_type, addr, timeout, + &p, _on_gap_slave_evt, (void *)handle); +#endif + if (res != 0) { + nimble_netif_conn_free(handle, NULL); + return -ECANCELED; + } _notify(handle, NIMBLE_NETIF_ACCEPTING, _netif.l2addr); - return 0; } int nimble_netif_accept(const uint8_t *ad, size_t ad_len, - const struct ble_gap_adv_params *adv_params) + const nimble_netif_accept_cfg_t *params) { assert(ad != NULL); assert(ad_len > 0); - return _accept(ad, ad_len, NULL, BLE_HS_FOREVER, adv_params); + return _accept(ad, ad_len, NULL, params); } -int nimble_netif_accept_direct(const ble_addr_t *addr, uint32_t timeout, - const struct ble_gap_adv_params *adv_params) +int nimble_netif_accept_direct(const ble_addr_t *addr, + const nimble_netif_accept_cfg_t *params) { - return _accept(NULL, 0, addr, timeout, adv_params); + assert(addr); + return _accept(NULL, 0, addr, params); } int nimble_netif_accept_stop(void) @@ -708,9 +845,15 @@ int nimble_netif_accept_stop(void) return -EALREADY; } - int res = ble_gap_adv_stop(); - assert(res == 0); - (void)res; + int res; +#if MYNEWT_VAL_BLE_EXT_ADV + res = ble_gap_ext_adv_stop(EXT_ADV_INST); +#else + res = ble_gap_adv_stop(); +#endif + if (res != 0) { + return -EIO; + } nimble_netif_conn_free(handle, NULL); _notify(handle, NIMBLE_NETIF_ACCEPT_STOP, _netif.l2addr); diff --git a/pkg/nimble/rpble/include/nimble_rpble.h b/pkg/nimble/rpble/include/nimble_rpble.h index 08fd71b9db..d09c292ad5 100644 --- a/pkg/nimble/rpble/include/nimble_rpble.h +++ b/pkg/nimble/rpble/include/nimble_rpble.h @@ -113,6 +113,7 @@ typedef struct { uint16_t conn_latency; /**< used slave latency for parent connection */ uint32_t conn_super_to_ms; /**< used supervision timeout for parent * connection, in ms */ + nimble_phy_t phy_mode; /**< BLE PHY mode to use */ 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 diff --git a/pkg/nimble/rpble/include/nimble_rpble_params.h b/pkg/nimble/rpble/include/nimble_rpble_params.h index 4f665f3595..6e03512939 100644 --- a/pkg/nimble/rpble/include/nimble_rpble_params.h +++ b/pkg/nimble/rpble/include/nimble_rpble_params.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 Freie Universität Berlin + * 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 @@ -69,6 +69,13 @@ extern "C" { #define NIMBLE_RPBLE_EVAL_ITVL_MAX_MS 13000U #endif +/** + * @brief Default BLE PHY mode used by rpble + */ +#ifndef NIMBLE_RPBLE_PHY_MODE +#define NIMBLE_RPBLE_PHY_MODE NIMBLE_PHY_1M +#endif + #ifndef NIMBLE_RPBLE_PARAMS #define NIMBLE_RPBLE_PARAMS \ { .scan_itvl_ms = NIMBLE_RPBLE_SCAN_ITVL_MS, \ @@ -81,6 +88,7 @@ extern "C" { .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, \ + .phy_mode = NIMBLE_RPBLE_PHY_MODE, \ .eval_itvl_min_ms = NIMBLE_RPBLE_EVAL_ITVL_MIN_MS, \ .eval_itvl_max_ms = NIMBLE_RPBLE_EVAL_ITVL_MAX_MS } #endif diff --git a/pkg/nimble/rpble/nimble_rpble.c b/pkg/nimble/rpble/nimble_rpble.c index 63006689f0..da56a56e6e 100644 --- a/pkg/nimble/rpble/nimble_rpble.c +++ b/pkg/nimble/rpble/nimble_rpble.c @@ -50,9 +50,8 @@ #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 */ +static nimble_netif_accept_cfg_t _accept_params; +static nimble_netif_connect_cfg_t _conn_params; /* local RPL context */ static nimble_rpble_ctx_t _local_rpl_ctx; @@ -107,7 +106,7 @@ static void _children_accept(void) assert(res == BLUETIL_AD_OK); /* start advertising this node */ - res = nimble_netif_accept(ad.buf, ad.pos, &_adv_params); + res = nimble_netif_accept(ad.buf, ad.pos, &_accept_params); assert(res == 0); } @@ -118,10 +117,17 @@ static void _on_scan_evt(uint8_t type, const ble_addr_t *addr, int res; (void)info; +#if IS_USED(MODULE_NIMBLE_RPBLE_EXT) + if ((type != (NIMBLE_SCANNER_EXT_ADV | BLE_HCI_ADV_CONN_MASK)) || + (info->status != BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE)) { + return; + } +#else /* filter out all non-connectible advertisements */ if (type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND) { return; } +#endif /* check if scanned node does actually speak rpble */ bluetil_ad_data_t sd_field; @@ -199,7 +205,7 @@ static void _parent_connect(struct ble_npl_event *ev) } /* try to connect to parent */ - int res = nimble_netif_connect(&_psel.addr, &_conn_params, _conn_scan_to); + int res = nimble_netif_connect(&_psel.addr, &_conn_params); if (res < 0) { _parent_find(); return; @@ -283,19 +289,36 @@ int nimble_rpble_param_update(const nimble_rpble_cfg_t *cfg) 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; + /* accept parameter extraction */ + memset(&_accept_params, 0, sizeof(_accept_params)); +#if IS_USED(MODULE_NIMBLE_RPBLE_EXT) + _accept_params.flags = 0; + _accept_params.primary_phy = cfg->phy_mode; + _accept_params.secondary_phy = cfg->phy_mode; +#else + _accept_params.flags = NIMBLE_NETIF_FLAG_LEGACY; + _accept_params.primary_phy = NIMBLE_PHY_1M; + _accept_params.secondary_phy = NIMBLE_PHY_1M; +#endif + _accept_params.adv_itvl_ms = cfg->adv_itvl_ms; + _accept_params.timeout_ms = BLE_HS_FOREVER; + _accept_params.own_addr_type = nimble_riot_own_addr_type; - _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; + /* connection parameter extraction */ + memset(&_conn_params, 0, sizeof(_conn_params)); + _conn_params.scan_itvl_ms = cfg->conn_scan_itvl_ms; + _conn_params.scan_window_ms = cfg->conn_scan_win_ms; + _conn_params.conn_itvl_min_ms = cfg->conn_itvl_min_ms; + _conn_params.conn_itvl_max_ms = cfg->conn_itvl_max_ms; + _conn_params.conn_supervision_timeout_ms = cfg->conn_super_to_ms; + _conn_params.conn_slave_latency = cfg->conn_latency; + _conn_params.timeout_ms = cfg->conn_scan_to_ms; +#if IS_USED(MODULE_NIMBLE_RPBLE_EXT) + _conn_params.phy_mode = cfg->phy_mode; +#else + _conn_params.phy_mode = NIMBLE_PHY_1M; +#endif + _conn_params.own_addr_type = nimble_riot_own_addr_type; /* register event callback */ nimble_netif_eventcb(_on_netif_evt); @@ -304,9 +327,18 @@ int nimble_rpble_param_update(const nimble_rpble_cfg_t *cfg) nimble_scanner_cfg_t scan_params = { 0 }; scan_params.itvl_ms = cfg->scan_itvl_ms; scan_params.win_ms = cfg->scan_win_ms; - scan_params.flags = NIMBLE_SCANNER_PASSIVE - | NIMBLE_SCANNER_FILTER_DUPS - | NIMBLE_SCANNER_PHY_1M; + scan_params.flags = (NIMBLE_SCANNER_PASSIVE | NIMBLE_SCANNER_FILTER_DUPS); + +#if IS_USED(MODULE_NIMBLE_RPBLE_EXT) && IS_USED(MODULE_NIMBLE_PHY_CODED) + if (cfg->phy_mode == NIMBLE_PHY_CODED) { + scan_params.flags |= NIMBLE_SCANNER_PHY_CODED; + } + else { + scan_params.flags |= NIMBLE_SCANNER_PHY_1M; + } +#else + scan_params.flags |= NIMBLE_SCANNER_PHY_1M; +#endif nimble_scanner_init(&scan_params, _on_scan_evt); /* start to look for parents */ diff --git a/pkg/nimble/scanner/include/nimble_scanner.h b/pkg/nimble/scanner/include/nimble_scanner.h index 02144a4a23..a58d2e6754 100644 --- a/pkg/nimble/scanner/include/nimble_scanner.h +++ b/pkg/nimble/scanner/include/nimble_scanner.h @@ -44,7 +44,9 @@ enum { NIMBLE_SCANNER_LIMITED = 0x02, /**< do limited discovery */ NIMBLE_SCANNER_FILTER_DUPS = 0x04, /**< filter duplicates */ NIMBLE_SCANNER_PHY_1M = 0x10, /**< scan on 1Mbit PHY */ +#if IS_USED(MODULE_NIMBLE_PHY_CODED) NIMBLE_SCANNER_PHY_CODED = 0x20, /**< scan on CODED PHY */ +#endif }; /** diff --git a/pkg/nimble/scanner/nimble_scanner.c b/pkg/nimble/scanner/nimble_scanner.c index ba82cc6807..69d832b2df 100644 --- a/pkg/nimble/scanner/nimble_scanner.c +++ b/pkg/nimble/scanner/nimble_scanner.c @@ -100,9 +100,12 @@ int nimble_scanner_start(void) uint8_t limited = (_scan_flags & NIMBLE_SCANNER_LIMITED) ? 1 : 0; const struct ble_gap_ext_disc_params *uncoded = (_scan_flags & NIMBLE_SCANNER_PHY_1M) ? &_scan_params : NULL; +#if IS_USED(MODULE_NIMBLE_PHY_CODED) const struct ble_gap_ext_disc_params *coded = (_scan_flags & NIMBLE_SCANNER_PHY_CODED) ? &_scan_params : NULL; - +#else + const struct ble_gap_ext_disc_params *coded = NULL; +#endif int32_t dur = (_scan_duration == BLE_HS_FOREVER) ? 0 : _scan_duration / 10; diff --git a/pkg/nimble/statconn/include/nimble_statconn.h b/pkg/nimble/statconn/include/nimble_statconn.h index da39800701..63ef2c3281 100644 --- a/pkg/nimble/statconn/include/nimble_statconn.h +++ b/pkg/nimble/statconn/include/nimble_statconn.h @@ -113,6 +113,21 @@ extern "C" { #define NIMBLE_STATCONN_CONN_SUPERTO_MS (2500U) #endif +/** + * @brief BLE PHY mode used by statconn. This value is only used if statconn + * is used in its extended mode (module `nimble_statconn_ext`) + */ +#ifndef NIMBLE_STATCONN_PHY_MODE +#define NIMBLE_STATCONN_PHY_MODE NIMBLE_PHY_1M +#endif + +/** + * @brief Statconn connection parameters + */ +typedef struct { + nimble_phy_t phy_mode; /**< BLE PHY mode used for the connection */ +} nimble_statconn_cfg_t; + /** * @brief Initialize the statconn module * @@ -138,23 +153,31 @@ void nimble_statconn_eventcb(nimble_netif_eventcb_t cb); * connection by that master. * * @param[in] addr BLE address of the peer + * @param[in] cfg additional connection parameters, set to NULL to apply + * default values * * @return 0 if peer was successfully added * @return -EALREADY if the peer address is already in use * @return -ENOMEM if no empty connection slot is available + * @return -EINVAL if invalid configuration parameters are given */ -int nimble_statconn_add_master(const uint8_t *addr); +int nimble_statconn_add_master(const uint8_t *addr, + const nimble_statconn_cfg_t *cfg); /** * @brief Connect to a peer (slave) with a given address as master * * @param[in] addr BLE address of the peer + * @param[in] cfg additional connection parameters, set to NULL to apply + * default values * * @return 0 if peer was successfully added * @return -EALREADY if the peer address is already in use * @return -ENOMEM if no empty connection slot is available + * @return -EINVAL if invalid configuration parameters are given */ -int nimble_statconn_add_slave(const uint8_t *addr); +int nimble_statconn_add_slave(const uint8_t *addr, + const nimble_statconn_cfg_t *cfg); /** * @brief Remove the connection to the given peer diff --git a/pkg/nimble/statconn/nimble_statconn.c b/pkg/nimble/statconn/nimble_statconn.c index bd99309572..a2134edb3e 100644 --- a/pkg/nimble/statconn/nimble_statconn.c +++ b/pkg/nimble/statconn/nimble_statconn.c @@ -44,6 +44,9 @@ typedef struct { uint8_t addr[BLE_ADDR_LEN]; /**< peer addr, network byte order */ uint8_t state; /**< internal state */ +#if IS_USED(MODULE_NIMBLE_STATCONN_EXT) + nimble_phy_t phy_mode; /**< PHY mode used by this slot */ +#endif } slot_t; static const uint8_t _ad[2] = { BLE_GAP_AD_FLAGS, BLUETIL_AD_FLAGS_DEFAULT }; @@ -51,9 +54,8 @@ 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_accept_cfg_t _accept_params; +static nimble_netif_connect_cfg_t _conn_params; static nimble_netif_eventcb_t _eventcb = NULL; @@ -87,10 +89,17 @@ static void _activate(uint8_t role) peer.type = BLE_ADDR_RANDOM; bluetil_addr_swapped_cp(slot->addr, peer.val); /* try to (re)open the connection */ - nimble_netif_connect(&peer, &_conn_params, _conn_timeout); +#if IS_USED(MODULE_NIMBLE_STATCONN_EXT) + _conn_params.phy_mode = slot->phy_mode; +#endif + nimble_netif_connect(&peer, &_conn_params); } else if (slot && (role == ROLE_S)) { - nimble_netif_accept(_ad, sizeof(_ad), &_adv_params); +#if IS_USED(MODULE_NIMBLE_STATCONN_EXT) + _accept_params.primary_phy = slot->phy_mode; + _accept_params.secondary_phy = slot->phy_mode; +#endif + nimble_netif_accept(_ad, sizeof(_ad), &_accept_params); } mutex_unlock(&_lock); } @@ -153,7 +162,8 @@ static void _on_netif_evt(int handle, nimble_netif_event_t event, } } -static int _be(uint8_t role, const uint8_t *addr) +static int _be(uint8_t role, const uint8_t *addr, + const nimble_statconn_cfg_t *cfg) { mutex_lock(&_lock); slot_t *s = _get_addr(addr); @@ -170,6 +180,18 @@ static int _be(uint8_t role, const uint8_t *addr) s->state = (role | PENDING); memcpy(s->addr, addr, BLE_ADDR_LEN); mutex_unlock(&_lock); + +#if IS_USED(MODULE_NIMBLE_STATCONN_EXT) + if (cfg != NULL) { + s->phy_mode = cfg->phy_mode; + } + else { + s->phy_mode = NIMBLE_STATCONN_PHY_MODE; + } +#else + (void)cfg; +#endif + _activate(role); return 0; } @@ -179,27 +201,31 @@ 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 = BLE_GAP_ADV_ITVL_MS(NIMBLE_STATCONN_ADV_ITVL_MS); - _adv_params.itvl_max = _adv_params.itvl_min; - _adv_params.channel_map = 0; - _adv_params.filter_policy = 0; - _adv_params.high_duty_cycle = 0; + memset(&_accept_params, 0, sizeof(_accept_params)); +#if IS_USED(MODULE_NIMBLE_STATCONN_EXT) + _accept_params.flags = 0; +#else + _accept_params.flags = NIMBLE_NETIF_FLAG_LEGACY; + _accept_params.primary_phy = NIMBLE_PHY_1M; + _accept_params.secondary_phy = NIMBLE_PHY_1M; +#endif + _accept_params.adv_itvl_ms = NIMBLE_STATCONN_ADV_ITVL_MS; + _accept_params.timeout_ms = BLE_HS_FOREVER; + _accept_params.own_addr_type = nimble_riot_own_addr_type; /* set connection parameters */ - _conn_params.scan_itvl = BLE_GAP_SCAN_ITVL_MS(NIMBLE_STATCONN_CONN_WIN_MS); - _conn_params.scan_window = _conn_params.scan_itvl; - _conn_params.latency = NIMBLE_STATCONN_CONN_LATENCY; - _conn_params.supervision_timeout = BLE_GAP_SUPERVISION_TIMEOUT_MS( - NIMBLE_STATCONN_CONN_SUPERTO_MS); - _conn_params.itvl_min = BLE_GAP_CONN_ITVL_MS( - NIMBLE_STATCONN_CONN_ITVL_MIN_MS); - _conn_params.itvl_max = BLE_GAP_CONN_ITVL_MS( - NIMBLE_STATCONN_CONN_ITVL_MAX_MS); - _conn_params.min_ce_len = 0; - _conn_params.max_ce_len = 0; - _conn_timeout = NIMBLE_STATCONN_CONN_TIMEOUT_MS; + memset(&_conn_params, 0, sizeof(_conn_params)); +#if !IS_USED(MODULE_NIMBLE_AUTOCONN_EXT) + _conn_params.phy_mode = NIMBLE_PHY_1M; +#endif + _conn_params.scan_itvl_ms = NIMBLE_STATCONN_CONN_WIN_MS; + _conn_params.scan_window_ms = NIMBLE_STATCONN_CONN_WIN_MS; + _conn_params.conn_itvl_min_ms = NIMBLE_STATCONN_CONN_ITVL_MIN_MS; + _conn_params.conn_itvl_max_ms = NIMBLE_STATCONN_CONN_ITVL_MAX_MS; + _conn_params.conn_supervision_timeout_ms = NIMBLE_STATCONN_CONN_SUPERTO_MS; + _conn_params.conn_slave_latency = NIMBLE_STATCONN_CONN_LATENCY; + _conn_params.timeout_ms = NIMBLE_STATCONN_CONN_TIMEOUT_MS; + _conn_params.own_addr_type = nimble_riot_own_addr_type; /* register our event callback */ nimble_netif_eventcb(_on_netif_evt); @@ -210,14 +236,16 @@ void nimble_statconn_eventcb(nimble_netif_eventcb_t cb) _eventcb = cb; } -int nimble_statconn_add_master(const uint8_t *addr) +int nimble_statconn_add_master(const uint8_t *addr, + const nimble_statconn_cfg_t *cfg) { - return _be(ROLE_S, addr); + return _be(ROLE_S, addr, cfg); } -int nimble_statconn_add_slave(const uint8_t *addr) +int nimble_statconn_add_slave(const uint8_t *addr, + const nimble_statconn_cfg_t *cfg) { - return _be(ROLE_M, addr); + return _be(ROLE_M, addr, cfg); } int nimble_statconn_rm(const uint8_t *addr) diff --git a/sys/shell/commands/sc_nimble_netif.c b/sys/shell/commands/sc_nimble_netif.c index 1709a59a4d..8338b3c37f 100644 --- a/sys/shell/commands/sc_nimble_netif.c +++ b/sys/shell/commands/sc_nimble_netif.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "fmt.h" #include "ztimer.h" @@ -30,17 +31,37 @@ #include "net/bluetil/ad.h" #include "net/bluetil/addr.h" -#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) +#define FULL_CONTROL !IS_USED(MODULE_NIMBLE_AUTOCONN) && \ + !IS_USED(MODULE_NIMBLE_STATCONN) && \ + !IS_USED(MODULE_NIMBLE_RPBLE) + +#if FULL_CONTROL #include "nimble_scanlist.h" #include "nimble_scanner.h" #endif -#define DEFAULT_NODE_NAME "bleRIOT" -#define DEFAULT_SCAN_DURATION (500U) /* 500ms */ -#define DEFAULT_CONN_TIMEOUT (500U) /* 500ms */ +#define DEFAULT_SCAN_DURATION_MS 500U +#define DEFAULT_CONN_TIMEOUT_MS 500U +#define DEFAULT_SCAN_ITVL_MS 100U +#define DEFAULT_CONN_ITVL_MS 75U +#define DEFAULT_TX_POWER 0 /* 0dBm */ +#define DEFAULT_ADV_ITVL_MS 75U -#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) +static const char *_phystr[] = { "N/A", "1M", "2M", "CODED" }; + +#if FULL_CONTROL static const char *_name_to_connect = NULL; +static nimble_netif_connect_cfg_t _connect_params = { + .scan_itvl_ms = DEFAULT_SCAN_ITVL_MS, + .scan_window_ms = DEFAULT_SCAN_ITVL_MS, + .conn_itvl_min_ms = DEFAULT_CONN_ITVL_MS, + .conn_itvl_max_ms = DEFAULT_CONN_ITVL_MS, + .conn_supervision_timeout_ms = DEFAULT_CONN_ITVL_MS * 20, + .conn_slave_latency = 0, + .timeout_ms = 0, /* will be filled later */ + .phy_mode = 0, /* will be filled later */ + .own_addr_type = 0 /* will be filled later */, +}; static void _scan_for_name(uint8_t type, const ble_addr_t *addr, const nimble_scanner_info_t *info, @@ -56,7 +77,7 @@ static void _scan_for_name(uint8_t type, const ble_addr_t *addr, _name_to_connect, strlen(_name_to_connect)); if (res) { nimble_scanner_stop(); - nimble_netif_connect(addr, NULL, DEFAULT_CONN_TIMEOUT); + nimble_netif_connect(addr, &_connect_params); } } @@ -90,12 +111,34 @@ static void _on_ble_evt(int handle, nimble_netif_event_t event, case NIMBLE_NETIF_ABORT_SLAVE: _print_evt("CONNECTION ABORT", handle, addr); break; + case NIMBLE_NETIF_ACCEPT_STOP: + _print_evt("ACCEPT STOP", handle, addr); case NIMBLE_NETIF_CONN_UPDATED: default: /* do nothing */ break; } } + +static uint8_t _parsephy(const char *phy_str) +{ + if (memcmp(phy_str, "1M", 2) == 0) { + return NIMBLE_PHY_1M; + } +#if IS_ACTIVE(MODULE_NIMBLE_PHY_2MBIT) + else if (memcmp(phy_str, "2M", 2) == 0) { + return NIMBLE_PHY_2M; + } +#endif +#if IS_ACTIVE(MODULE_NIMBLE_PHY_CODED) + else if (memcmp(phy_str, "CODED", 5) == 0) { + return NIMBLE_PHY_CODED; + } +#endif + else { + return NIMBLE_PHY_INVALID; + } +} #endif static int _conn_dump(nimble_netif_conn_t *conn, int handle, void *arg) @@ -117,8 +160,20 @@ static int _conn_dump(nimble_netif_conn_t *conn, int handle, void *arg) printf(" "); bluetil_addr_ipv6_l2ll_print(conn->addr); #endif - printf(" (%c,%ums,%ums,%i)", role, itvl, sto, (int)desc.conn_latency); - puts(""); + +#if IS_USED(MODULE_NIMBLE_NETIF_EXT) + uint8_t phy_rx, phy_tx; + (void)phy_tx; + res = ble_gap_read_le_phy(conn->gaphandle, &phy_tx, &phy_rx); + if (res != 0) { + phy_rx = 1; + } +#else + /* when not using extended advertisements we always use the 1M phy mode */ + uint8_t phy_rx = 1; +#endif + printf(" (%c,%ums,%ums,%i,%s)\n", + role, itvl, sto, (int)desc.conn_latency, _phystr[phy_rx]); return 0; } @@ -159,7 +214,7 @@ static void _conn_list(void) if (active > 0) { nimble_netif_conn_foreach(NIMBLE_NETIF_L2CAP_CONNECTED, _conn_dump, NULL); - puts(" (role, conn itvl, superv. timeout, slave latency)"); + puts(" (role, conn itvl, superv. timeout, slave latency, PHY)"); } } @@ -179,6 +234,15 @@ static void _cmd_info(void) #endif puts(""); + printf("Supported PHY modes: 1M"); +#if IS_USED(MODULE_NIMBLE_PHY_2MBIT) + printf(" 2M"); +#endif +#if IS_USED(MODULE_NIMBLE_PHY_CODED) + printf(" CODED"); +#endif + puts(""); + printf(" Free slots: %u/%u\n", free, NIMBLE_NETIF_MAX_CONN); printf("Advertising: "); if (nimble_netif_conn_get_adv() != NIMBLE_NETIF_CONN_INVALID) { @@ -196,99 +260,107 @@ static void _cmd_info(void) puts(""); } -#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) -static void _cmd_adv(const char *name) +#if FULL_CONTROL +static int _cmd_adv(int argc, char **argv, bool legacy) { int res; (void)res; - uint8_t buf[BLE_HS_ADV_MAX_SZ]; - bluetil_ad_t ad; - const struct ble_gap_adv_params _adv_params = { - .conn_mode = BLE_GAP_CONN_MODE_UND, - .disc_mode = BLE_GAP_DISC_MODE_LTD, - .itvl_min = BLE_GAP_ADV_FAST_INTERVAL2_MIN, - .itvl_max = BLE_GAP_ADV_FAST_INTERVAL2_MAX, - }; + const char *name = NULL; + uint8_t addrn[BLE_ADDR_LEN]; + ble_addr_t addr = { .type = nimble_riot_own_addr_type }; + + /* stop sub-command: stop advertising */ + if (memcmp(argv[2], "stop", 4) == 0) { + res = nimble_netif_accept_stop(); + if (res == 0) { + puts("advertising stopped"); + } + else if (res == -EALREADY) { + puts("no advertising in progress"); + } + return 0; + } /* make sure no advertising is in progress */ if (nimble_netif_conn_is_adv()) { puts("err: advertising already in progress"); - return; + return 1; } - /* build advertising data */ - res = bluetil_ad_init_with_flags(&ad, buf, BLE_HS_ADV_MAX_SZ, - BLUETIL_AD_FLAGS_DEFAULT); - assert(res == BLUETIL_AD_OK); - uint16_t ipss = BLE_GATT_SVC_IPSS; - res = bluetil_ad_add(&ad, BLE_GAP_AD_UUID16_INCOMP, &ipss, sizeof(ipss)); - assert(res == BLUETIL_AD_OK); - if (name == NULL) { - name = DEFAULT_NODE_NAME; + /* try if first parameter is a BLE address, if so, use directed + * advertisement */ + if (bluetil_addr_from_str(addrn, argv[2]) != NULL) { + /* NimBLE expects address in little endian, so swap */ + bluetil_addr_swapped_cp(addrn, addr.val); + puts("Found BLE address: sending directed advertisements"); } - res = bluetil_ad_add(&ad, BLE_GAP_AD_NAME, name, strlen(name)); - if (res != BLUETIL_AD_OK) { - puts("err: the given name is too long"); - return; + else { + name = argv[2]; + } + + uint32_t timeout = 0; + if (argc >= 4) { + timeout = (uint32_t)atoi(argv[3]); + } + + uint8_t phy_sec = BLE_GAP_LE_PHY_1M; + if (argc >= 5) { + phy_sec = _parsephy(argv[4]); + if (phy_sec == 0) { + puts("err: PHY mode not supported\n"); + return 1; + } + } + uint8_t phy_pri = (phy_sec == BLE_HCI_LE_PHY_2M) ? BLE_HCI_LE_PHY_1M + : phy_sec; + + nimble_netif_accept_cfg_t p = { + .flags = (legacy) ? NIMBLE_NETIF_FLAG_LEGACY : 0, + .adv_itvl_ms = DEFAULT_ADV_ITVL_MS, + .primary_phy = phy_pri, + .secondary_phy = phy_sec, + .tx_power = DEFAULT_TX_POWER, + .channel_map = 0, + .timeout_ms = timeout, + .own_addr_type = nimble_riot_own_addr_type, + }; + + if (name != NULL) { + uint8_t buf[BLE_HS_ADV_MAX_SZ]; + bluetil_ad_t ad; + /* build advertising data */ + res = bluetil_ad_init_with_flags(&ad, buf, BLE_HS_ADV_MAX_SZ, + BLUETIL_AD_FLAGS_DEFAULT); + assert(res == BLUETIL_AD_OK); + uint16_t ipss = BLE_GATT_SVC_IPSS; + res = bluetil_ad_add(&ad, BLE_GAP_AD_UUID16_INCOMP, &ipss, sizeof(ipss)); + assert(res == BLUETIL_AD_OK); + res = bluetil_ad_add(&ad, BLE_GAP_AD_NAME, name, strlen(name)); + if (res != BLUETIL_AD_OK) { + puts("err: the given name is too long"); + return 1; + } + + res = nimble_netif_accept(ad.buf, ad.pos, &p); + } + else { + res = nimble_netif_accept_direct(&addr, &p); } - /* start listening for incoming connections */ - res = nimble_netif_accept(ad.buf, ad.pos, &_adv_params); if (res != 0) { printf("err: unable to start advertising (%i)\n", res); } else { - printf("success: advertising this node as '%s'\n", name); - } -} - -static void _cmd_adv_direct(const char *addr_str) -{ - int res; - (void)res; - uint8_t addrn[BLE_ADDR_LEN]; - ble_addr_t addr; - const struct ble_gap_adv_params _adv_params = { - .conn_mode = BLE_GAP_CONN_MODE_DIR, - .disc_mode = BLE_GAP_DISC_MODE_GEN, - .itvl_min = BLE_GAP_ADV_FAST_INTERVAL2_MIN, - .itvl_max = BLE_GAP_ADV_FAST_INTERVAL2_MAX, - }; - - /* make sure no advertising is in progress */ - if (nimble_netif_conn_is_adv()) { - puts("err: advertising already in progress"); - return; - } - - /* parse and convert address -> RIOT uses big endian notation, NimBLE - * expects little endian... */ - if (bluetil_addr_from_str(addrn, addr_str) == NULL) { - puts("err: unable to parse BLE address"); - return; - } - addr.type = nimble_riot_own_addr_type; - bluetil_addr_swapped_cp(addrn, addr.val); - - /* start advertising directed advertising with the given BLE address */ - res = nimble_netif_accept_direct(&addr, BLE_HS_FOREVER, &_adv_params); - if (res != 0) { - printf("err: unable to start directed advertising (%i)\n", res); - } - else { - puts("success: started to send directed advertisements"); - } -} - -static void _cmd_adv_stop(void) -{ - int res = nimble_netif_accept_stop(); - if (res == 0) { - puts("canceled advertising"); - } - else { - puts("no advertising in progress"); + if (name != NULL) { + printf("success: advertising this node as '%s'\n", name); + } + else { + printf("success: sending direct advertisements to "); + bluetil_addr_print(addrn); + puts(""); + } } + return 0; } static void _do_scan(nimble_scanner_cb cb, unsigned duration) @@ -301,7 +373,19 @@ static void _do_scan(nimble_scanner_cb cb, unsigned duration) printf("err: scanner already active\n"); return; } - nimble_scanner_init(NULL, cb); + + nimble_scanner_cfg_t p = { + .itvl_ms = DEFAULT_SCAN_ITVL_MS, + .win_ms = DEFAULT_SCAN_ITVL_MS, +#if IS_USED(MODULE_NIMBLE_PHY_CODED) + .flags = (NIMBLE_SCANNER_PASSIVE | NIMBLE_SCANNER_PHY_1M | + NIMBLE_SCANNER_PHY_CODED), +#else + .flags = (NIMBLE_SCANNER_PASSIVE | NIMBLE_SCANNER_PHY_1M), +#endif + }; + + nimble_scanner_init(&p, cb); nimble_scanlist_clear(); nimble_scanner_start(); ztimer_sleep(ZTIMER_MSEC, duration); @@ -316,58 +400,73 @@ static void _cmd_scan(unsigned duration) nimble_scanlist_print(); } -static void _cmd_connect_addr(ble_addr_t *addr) +static void _cmd_connect(int argc, char **argv) { - /* simply use NimBLEs default connection parameters */ - int res = nimble_netif_connect(addr, NULL, DEFAULT_CONN_TIMEOUT); - if (res < 0) { - printf("err: unable to trigger connection sequence (%i)\n", res); - return; - } + ble_addr_t addr; + int proceed = 0; - printf("initiated connection procedure with "); + /* populate connection parameters */ + _connect_params.timeout_ms = DEFAULT_CONN_TIMEOUT_MS; + if (argc >= 4) { + _connect_params.timeout_ms = (uint32_t)atoi(argv[3]); + } + _connect_params.phy_mode = NIMBLE_PHY_1M; + if (argc >= 5) { + _connect_params.phy_mode = _parsephy(argv[4]); + if (_connect_params.phy_mode == 0) { + puts("err: PHY mode not supported\n"); + return; + } + } + _connect_params.own_addr_type = nimble_riot_own_addr_type; + + /* try to parse address directly */ uint8_t addrn[BLE_ADDR_LEN]; - bluetil_addr_swapped_cp(addr->val, addrn); - bluetil_addr_print(addrn); - puts(""); - -} - -static void _cmd_connect_addr_raw(const uint8_t *addr_in) -{ - /* RANDOM is the most common type, has no noticeable effect when connecting - anyhow... */ - ble_addr_t addr = { .type = BLE_ADDR_RANDOM }; - /* NimBLE expects address in little endian, so swap */ - bluetil_addr_swapped_cp(addr_in, addr.val); - _cmd_connect_addr(&addr); -} - -static void _cmd_connect_name(const char *name, unsigned duration) -{ - if (_name_to_connect != NULL) { - printf("err: already trying to connect to '%s'\n", _name_to_connect); + if (bluetil_addr_from_str(addrn, argv[2]) != NULL) { + addr.type = nimble_riot_own_addr_type; + /* NimBLE expects address in little endian, so swap */ + bluetil_addr_swapped_cp(addrn, addr.val); + proceed = 1; + } + /* try if param is a number, if so use it as scanlist entry number */ + else if (fmt_is_number(argv[2])) { + unsigned pos = atoi(argv[2]); + nimble_scanlist_entry_t *sle = nimble_scanlist_get_by_pos(pos); + if (sle == NULL) { + puts("err: unable to find given entry in scanlist"); + return; + } + _connect_params.phy_mode = sle->phy_sec; + memcpy(&addr, &sle->addr, sizeof(addr)); + proceed = 1; + } + /* else interpret value as name and search for that peer */ + else { + unsigned duration = DEFAULT_SCAN_DURATION_MS; + if (argc > 3) { + duration = atoi(argv[3]); + } + _name_to_connect = argv[2]; + printf("trying to find and connect to a node with name '%s'\n", argv[2]); + _do_scan(_scan_for_name, duration); + if (_name_to_connect != NULL) { + printf("fail: unable to connect to '%s'\n", _name_to_connect); + _name_to_connect = NULL; + } return; } - _name_to_connect = name; - printf("trying to find and connect to a node with name '%s'\n", name); - _do_scan(_scan_for_name, duration); - if (_name_to_connect != NULL) { - printf("fail: unable to connect to '%s'\n", _name_to_connect); - _name_to_connect = NULL; - } -} -static void _cmd_connect_scanlist(unsigned pos) -{ - nimble_scanlist_entry_t *sle = nimble_scanlist_get_by_pos(pos); - if (sle == NULL) { - puts("err: unable to find given entry in scanlist"); - return; + if (proceed == 1) { + int res = nimble_netif_connect(&addr, &_connect_params); + if (res == 0) { + puts("Successfully connected 123"); + } + else { + puts("err: unable to connect"); + } } - _cmd_connect_addr(&sle->addr); } -#endif /* MODULE_NIMBLE_AUTOCONN */ +#endif static void _cmd_close(int handle) { @@ -406,7 +505,7 @@ static int _ishelp(char *argv) void sc_nimble_netif_init(void) { -#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) +#if FULL_CONTROL /* setup the scanning environment */ nimble_scanlist_init(); @@ -418,8 +517,9 @@ void sc_nimble_netif_init(void) int _nimble_netif_handler(int argc, char **argv) { if ((argc == 1) || _ishelp(argv[1])) { -#if !IS_USED(MODULE_NIMBLE_AUTOCONN) && !IS_USED(MODULE_NIMBLE_STATCONN) - printf("usage: %s [help|info|adv|scan|connect|close|update|chanmap]\n", argv[0]); +#if FULL_CONTROL + printf("usage: %s [help|info|adv|adv_ext|adv_dir|" + "scan|connect|close|update|chanmap]\n", argv[0]); #else printf("usage: %s [help|info|close|update|chanmap]\n", argv[0]); #endif @@ -429,34 +529,26 @@ int _nimble_netif_handler(int argc, char **argv) _cmd_info(); } -#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) { - if (_ishelp(argv[2])) { - printf("usage: %s adv [help|stop|direct |]\n", - argv[0]); - return 0; - } - if (memcmp(argv[2], "stop", 4) == 0) { - _cmd_adv_stop(); - return 0; - } - if (memcmp(argv[2], "direct", 6) == 0) { - puts("DBG: direct adv"); - if (argc < 4) { - printf("error, no BLE address given\n"); - return 0; - } - _cmd_adv_direct(argv[3]); - return 0; - } - name = argv[2]; +#if FULL_CONTROL + else if (memcmp(argv[1], "adv_ext", 7) == 0) { + if (argc <= 2 || _ishelp(argv[2])) { + printf("usage: %s adv_ext [timeout] [phy mode]\n" + " timeout in ms, 0 for no timeout\n" + " phy mode: [1M|2M|CODED]\n", argv[0]); + return 0; } - _cmd_adv(name); + return _cmd_adv(argc, argv, false); + } + else if (memcmp(argv[1], "adv", 3) == 0) { + if (argc <= 2 || _ishelp(argv[2])) { + printf("usage: %s adv [timeout]\n" + " timeout in ms, 0 for no timeout\n", argv[0]); + return 0; + } + return _cmd_adv(argc, argv, true); } else if (memcmp(argv[1], "scan", 4) == 0) { - uint32_t duration = DEFAULT_SCAN_DURATION; + uint32_t duration = DEFAULT_SCAN_DURATION_MS; if (argc > 2) { if (_ishelp(argv[2])) { printf("usage: %s scan [help|list|[duration in ms]]\n", argv[0]); @@ -472,32 +564,16 @@ int _nimble_netif_handler(int argc, char **argv) } else if (memcmp(argv[1], "connect", 7) == 0) { if ((argc < 3) || _ishelp(argv[2])) { - printf("usage: %s connect [help|list|||]\n", - argv[0]); + printf("usage: %s %s [help|list|||] " + "[timeout ms] [phy mode]\n" + " phy mode: [1M|2M|CODED]\n", argv[0], argv[1]); return 0; } if (memcmp(argv[2], "list", 4) == 0) { _conn_list(); return 0; } - /* try if param is an BLE address */ - uint8_t addr[BLE_ADDR_LEN]; - if (bluetil_addr_from_str(addr, argv[2]) != NULL) { - _cmd_connect_addr_raw(addr); - return 0; - } - /* try if param is a name (contains non-number chars) */ - if (!fmt_is_number(argv[2])) { - unsigned duration = DEFAULT_SCAN_DURATION; - if (argc > 3) { - duration = atoi(argv[3]); - } - _cmd_connect_name(argv[2], duration); - return 0; - } - - unsigned pos = atoi(argv[2]); - _cmd_connect_scanlist(pos); + _cmd_connect(argc, argv); } #endif else if (memcmp(argv[1], "close", 5) == 0) { diff --git a/sys/shell/commands/sc_nimble_statconn.c b/sys/shell/commands/sc_nimble_statconn.c index 26726c42a9..99679649c5 100644 --- a/sys/shell/commands/sc_nimble_statconn.c +++ b/sys/shell/commands/sc_nimble_statconn.c @@ -24,13 +24,48 @@ #include "net/bluetil/addr.h" #include "nimble_statconn.h" + +static uint8_t _parsephy(const char *phy_str) +{ + if (memcmp(phy_str, "1M", 2) == 0) { + return NIMBLE_PHY_1M; + } +#if IS_ACTIVE(MODULE_NIMBLE_PHY_2MBIT) + else if (memcmp(phy_str, "2M", 2) == 0) { + return NIMBLE_PHY_2M; + } +#endif +#if IS_ACTIVE(MODULE_NIMBLE_PHY_CODED) + else if (memcmp(phy_str, "CODED", 5) == 0) { + return NIMBLE_PHY_CODED; + } +#endif + else { + return NIMBLE_PHY_INVALID; + } +} + int _nimble_statconn_handler(int argc, char **argv) { + nimble_statconn_cfg_t cfg; + if ((argc < 3)) { - printf("usage: %s \n", argv[0]); + printf("usage: %s [phy mode]\n" + " phy_mode := [1M, 2M, CODED]\n", argv[0]); return 0; } + if (argc >= 4) { + cfg.phy_mode = _parsephy(argv[3]); + if (cfg.phy_mode == NIMBLE_PHY_INVALID) { + puts("err: PHY mode not supported"); + return 1; + } + } + else { + cfg.phy_mode = NIMBLE_STATCONN_PHY_MODE; + } + /* parse address */ uint8_t addr[BLE_ADDR_LEN]; if (bluetil_addr_from_str(addr, argv[2]) == NULL) { @@ -39,7 +74,7 @@ int _nimble_statconn_handler(int argc, char **argv) } if (strncmp(argv[1], "addm", 4) == 0) { - if (nimble_statconn_add_master(addr) == 0) { + if (nimble_statconn_add_master(addr, &cfg) == 0) { puts("success: connecting to peer as slave"); } else { @@ -47,7 +82,7 @@ int _nimble_statconn_handler(int argc, char **argv) } } else if (strncmp(argv[1], "adds", 4) == 0) { - if (nimble_statconn_add_slave(addr) == 0) { + if (nimble_statconn_add_slave(addr, &cfg) == 0) { puts("success: connecting to peer as master"); } else { diff --git a/tests/nimble_autoconn_gnrc_ext/Makefile b/tests/nimble_autoconn_gnrc_ext/Makefile new file mode 100644 index 0000000000..d63fd5a7e7 --- /dev/null +++ b/tests/nimble_autoconn_gnrc_ext/Makefile @@ -0,0 +1,24 @@ +BOARD ?= nrf52dk +include ../Makefile.tests_common + +# include shell support +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# Include GNRC and RPL +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_router_default +USEMODULE += gnrc_icmpv6_echo +USEMODULE += auto_init_gnrc_rpl +USEMODULE += gnrc_rpl + +# Setup Nimble +USEMODULE += nimble_autoconn_ipsp +USEMODULE += nimble_autoconn_ext +FEATURES_OPTIONAL += ble_phy_2mbit +FEATURES_OPTIONAL += ble_phy_coded + +TEST_ON_CI_WHITELIST += nrf52dk nrf52840dk + +include $(RIOTBASE)/Makefile.include diff --git a/tests/nimble_autoconn_gnrc_ext/Makefile.ci b/tests/nimble_autoconn_gnrc_ext/Makefile.ci new file mode 100644 index 0000000000..4079597a0f --- /dev/null +++ b/tests/nimble_autoconn_gnrc_ext/Makefile.ci @@ -0,0 +1,4 @@ +BOARD_INSUFFICIENT_MEMORY := \ + e104-bt5010a-tb \ + e104-bt5011a-tb \ + # diff --git a/tests/nimble_autoconn_gnrc_ext/main.c b/tests/nimble_autoconn_gnrc_ext/main.c new file mode 100644 index 0000000000..cbbac5bfca --- /dev/null +++ b/tests/nimble_autoconn_gnrc_ext/main.c @@ -0,0 +1,43 @@ +/* + * 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 examples + * @{ + * + * @file + * @brief Run IP-over-BLE using the 'autoconn' BLE connection manager + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "shell.h" +#include "msg.h" + +#define MAIN_QUEUE_SIZE (8) +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +int main(void) +{ + /* 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); + puts("IPv6-over-BLE with autoconn BLE connection manager"); + + /* 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; +} diff --git a/tests/nimble_ext_adv/Makefile b/tests/nimble_ext_adv/Makefile index 42cd1b0c77..8d6e5107e2 100644 --- a/tests/nimble_ext_adv/Makefile +++ b/tests/nimble_ext_adv/Makefile @@ -8,11 +8,8 @@ USEMODULE += shell_commands USEMODULE += nimble_scanner USEMODULE += nimble_scanlist USEMODULE += nimble_adv_ext -USEMODULE += nimble_phy_2mbit - -ifneq (,$(filter nrf52840dk nrf52840dongle,$(BOARD))) - USEMODULE += nimble_phy_coded -endif +FEATURES_OPTIONAL += ble_phy_2mbit +FEATURES_OPTIONAL += ble_phy_coded # Comment this out to disable code in RIOT that does safety checking # which is not needed in a production environment but helps in the diff --git a/tests/nimble_netif_ext/Makefile b/tests/nimble_netif_ext/Makefile new file mode 100644 index 0000000000..744dc8e258 --- /dev/null +++ b/tests/nimble_netif_ext/Makefile @@ -0,0 +1,24 @@ +BOARD ?= nrf52dk +include ../Makefile.tests_common + +# include shell support +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# Include GNRC and RPL +USEMODULE += 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_netif_ext +FEATURES_OPTIONAL += ble_phy_2mbit +FEATURES_OPTIONAL += ble_phy_coded + +TEST_ON_CI_WHITELIST += nrf52dk nrf52840dk + +include $(RIOTBASE)/Makefile.include diff --git a/tests/nimble_netif_ext/Makefile.ci b/tests/nimble_netif_ext/Makefile.ci new file mode 100644 index 0000000000..4079597a0f --- /dev/null +++ b/tests/nimble_netif_ext/Makefile.ci @@ -0,0 +1,4 @@ +BOARD_INSUFFICIENT_MEMORY := \ + e104-bt5010a-tb \ + e104-bt5011a-tb \ + # diff --git a/tests/nimble_netif_ext/main.c b/tests/nimble_netif_ext/main.c new file mode 100644 index 0000000000..84b13d0531 --- /dev/null +++ b/tests/nimble_netif_ext/main.c @@ -0,0 +1,45 @@ +/* + * 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 examples + * @{ + * + * @file + * @brief Test extended advertising and additional PHY modes with + * nimble_netif + * + * @author Hauke Petersen + * + * @} + */ + +#include + +#include "shell.h" +#include "msg.h" + +#define MAIN_QUEUE_SIZE (8) +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +int main(void) +{ + puts("Extended PHY mode test for IP over BLE"); + + /* 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); + + /* 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; +} diff --git a/tests/nimble_rpble_gnrc/Makefile b/tests/nimble_rpble_gnrc/Makefile index bb23b35c4c..b57fffb558 100644 --- a/tests/nimble_rpble_gnrc/Makefile +++ b/tests/nimble_rpble_gnrc/Makefile @@ -17,7 +17,7 @@ USEMODULE += auto_init_gnrc_rpl # configure and use Nimble USEMODULE += bluetil_addr USEMODULE += nimble_rpble -NIMBLE_MAX_CONN := 3 +NIMBLE_MAX_CONN = 3 DEVELHELP = 0 diff --git a/tests/nimble_rpble_gnrc_ext/Makefile b/tests/nimble_rpble_gnrc_ext/Makefile new file mode 100644 index 0000000000..d7b7d01194 --- /dev/null +++ b/tests/nimble_rpble_gnrc_ext/Makefile @@ -0,0 +1,30 @@ +BOARD ?= nrf52dk +include ../Makefile.tests_common + +# include shell support +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# Enable single interface optimization. +# Remove this if more than one interface is present +USEMODULE += gnrc_netif_single +# Include GNRC and RPL +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_ext +FEATURES_OPTIONAL += ble_phy_2mbit +FEATURES_OPTIONAL += ble_phy_coded +NIMBLE_MAX_CONN = 3 + +DEVELHELP = 0 + +TEST_ON_CI_WHITELIST += nrf52dk nrf52840dk + +include $(RIOTBASE)/Makefile.include diff --git a/tests/nimble_rpble_gnrc_ext/Makefile.ci b/tests/nimble_rpble_gnrc_ext/Makefile.ci new file mode 100644 index 0000000000..4079597a0f --- /dev/null +++ b/tests/nimble_rpble_gnrc_ext/Makefile.ci @@ -0,0 +1,4 @@ +BOARD_INSUFFICIENT_MEMORY := \ + e104-bt5010a-tb \ + e104-bt5011a-tb \ + # diff --git a/tests/nimble_rpble_gnrc_ext/main.c b/tests/nimble_rpble_gnrc_ext/main.c new file mode 100644 index 0000000000..7428966008 --- /dev/null +++ b/tests/nimble_rpble_gnrc_ext/main.c @@ -0,0 +1,80 @@ +/* + * 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 examples + * @{ + * + * @file + * @brief Test for using rpble in its extended mode + * + * @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 (ping) */ + 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; +} diff --git a/tests/nimble_statconn_gnrc_ext/Makefile b/tests/nimble_statconn_gnrc_ext/Makefile new file mode 100644 index 0000000000..c0a373c6dc --- /dev/null +++ b/tests/nimble_statconn_gnrc_ext/Makefile @@ -0,0 +1,23 @@ +BOARD ?= nrf52dk +include ../Makefile.tests_common + +# include shell support +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +# Include GNRC and RPL +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_ext +FEATURES_OPTIONAL += ble_phy_2mbit +FEATURES_OPTIONAL += ble_phy_coded + +TEST_ON_CI_WHITELIST += nrf52dk nrf52840dk + +include $(RIOTBASE)/Makefile.include diff --git a/tests/nimble_statconn_gnrc_ext/Makefile.ci b/tests/nimble_statconn_gnrc_ext/Makefile.ci new file mode 100644 index 0000000000..4079597a0f --- /dev/null +++ b/tests/nimble_statconn_gnrc_ext/Makefile.ci @@ -0,0 +1,4 @@ +BOARD_INSUFFICIENT_MEMORY := \ + e104-bt5010a-tb \ + e104-bt5011a-tb \ + # diff --git a/tests/nimble_statconn_gnrc_ext/main.c b/tests/nimble_statconn_gnrc_ext/main.c new file mode 100644 index 0000000000..ffc45a567f --- /dev/null +++ b/tests/nimble_statconn_gnrc_ext/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; +}