1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 14:33:52 +01:00

shell/sc_nimble_netif: extend to support PHY modes

This commit is contained in:
Hauke Petersen 2021-12-06 13:35:24 +01:00
parent 0ea7bf33d0
commit f9386b4af7

View File

@ -21,6 +21,7 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#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 <addr>|<name>]\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 <help|stop|addr|name> [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 <help|stop|addr|name> [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|<scanlist entry #>|<BLE addr>|<name>]\n",
argv[0]);
printf("usage: %s %s [help|list|<scanlist #>|<BLE addr>|<name>] "
"[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) {