blob: 95b70821850ea8d1696804dab925208f3982bd29 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <host/ble_gap.h>
#include "rx_stress.h"
/* UUID128 of stress test use cases*/
static uint8_t rx_stress_uuid128[STRESS_UUIDS_NUM][16];
static struct com_stress_test_ctx rx_stress_ctxD = {
.conn_handle = 0xffff,
.cur_test_id = 0,
.s6_rcv_adv_first = 0,
.s6_rcv_adv_suc = 0,
};
static struct com_stress_test_ctx *rx_stress_ctx = &rx_stress_ctxD;
#define EXTENDED_ADVERT 0
#define LEGACY_ADVERT 1
/* Advertising instances ids */
#define SWITCHER_INSTANCE 0
#define TEST_INSTANCE 1
/* Main test task priority. Set a high value so that the task does not
* interfere with event handling */
#define RX_STRESS_MAIN_TASK_PRIO 0xf0
/* Advertising settings */
struct rx_stress_adv_set {
uint8_t instance;
uint8_t *instance_uuid128;
uint8_t legacy_pdu;
ble_gap_event_fn *cb;
const uint8_t *pattern_data;
int pattern_len;
};
/* Define task stack and task object */
#define RX_STRESS_MAIN_TASK_STACK_SIZE (2000)
static struct os_task rx_stress_main_task;
static os_stack_t rx_stress_main_task_stack[RX_STRESS_MAIN_TASK_STACK_SIZE];
static struct os_sem rx_stress_main_sem;
static void
rx_stress_on_test_finish(int test_num)
{
console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num);
os_sem_release(&rx_stress_main_sem);
}
static int
rx_stress_adv_start(uint8_t instance)
{
int rc;
/* Resume advertising earlier configured instance */
rc = ble_gap_ext_adv_start(instance, 0, 0);
assert (rc == 0 || rc == 2);
MODLOG_DFLT(INFO, "Instance %d started; rc: %d\n", instance, rc);
return rc;
}
static int
rx_stress_adv_start_with_rand_addr(uint8_t instance)
{
int rc;
ble_addr_t addr;
ble_gap_ext_adv_stop(instance);
rc = ble_hs_id_gen_rnd(1, &addr);
assert (rc == 0);
/* Set random address for advertising instance */
rc = ble_gap_ext_adv_set_addr(instance, &addr);
assert (rc == 0);
return rx_stress_adv_start(instance);
}
static void
rx_stress_simple_adv(struct rx_stress_adv_set *adv_set)
{
uint8_t own_addr_type;
struct ble_gap_ext_adv_params params;
struct ble_hs_adv_fields fields;
struct os_mbuf *adv_data;
ble_addr_t addr;
const char *name;
int rc;
int pattern_len;
/* Determine own address type */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "\033[0;31mError determining own address type; "
"rc=%d\033[0m\n", rc);
return;
}
/* Use defaults for non-set fields */
memset(&fields, 0, sizeof fields);
/* General Discoverable and BrEdrNotSupported */
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
/* Set device name as instance name */
name = ble_svc_gap_device_name();
fields.name = (uint8_t *) name;
fields.name_len = strlen(name);
fields.name_is_complete = 1;
/* Set UUID 128 data service */
fields.svc_data_uuid128 = adv_set->instance_uuid128;
fields.svc_data_uuid128_len = 16;
/* Use defaults for non-set params */
memset(&params, 0, sizeof(params));
/* Set adv mode */
if (adv_set->legacy_pdu == 1) {
params.connectable = 1;
params.scannable = 1;
} else if (adv_set->pattern_len < 255) {
params.connectable = 1;
}
params.own_addr_type = own_addr_type;
params.primary_phy = BLE_HCI_LE_PHY_1M;
/* If legacy, this param will be lowered by API */
params.secondary_phy = BLE_HCI_LE_PHY_2M;
params.sid = adv_set->instance;
params.legacy_pdu = adv_set->legacy_pdu;
ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK);
rc = ble_gap_ext_adv_remove(adv_set->instance);
assert(rc == 0 || rc == BLE_HS_EALREADY);
/* Configure instance with the params set */
rc = ble_gap_ext_adv_configure(adv_set->instance, &params,
NULL, adv_set->cb, NULL);
assert (rc == 0);
if (own_addr_type == 0) {
rc = ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr.val, NULL);
assert (rc == 0);
} else {
rc = ble_hs_id_gen_rnd(1, &addr);
assert (rc == 0);
/* Set random address for advertising instance */
rc = ble_gap_ext_adv_set_addr(adv_set->instance, &addr);
assert (rc == 0);
}
/* Get mbuf for adv data */
adv_data = os_msys_get_pkthdr(16, 0);
assert(adv_data != NULL);
/* Fill mbuf with adv fields - structured data */
rc = ble_hs_adv_set_fields_mbuf(&fields, adv_data);
if (rc) {
os_mbuf_free_chain(adv_data);
assert(0);
}
pattern_len = min(adv_set->pattern_len,
MYNEWT_VAL(BLE_EXT_ADV_MAX_SIZE) - adv_data->om_len);
/* Append to mbuf pattern data - raw data */
rc = os_mbuf_append(adv_data, adv_set->pattern_data, pattern_len);
if (rc) {
os_mbuf_free_chain(adv_data);
assert(0);
}
/* Include mbuf data in advertisement */
rc = ble_gap_ext_adv_set_data(adv_set->instance, adv_data);
assert (rc == 0);
/* Start advertising */
rc = ble_gap_ext_adv_start(adv_set->instance, 0, 0);
assert (rc == 0);
MODLOG_DFLT(INFO, "instance %u started\n", adv_set->instance);
}
static int
rx_stress_0_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[0].num);
/* Stop test advert */
ble_gap_ext_adv_stop(TEST_INSTANCE);
ble_gap_terminate(event->connect.conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
} else {
/* Connection failed; resume advertising */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
console_printf("\033[0;32mReceived signal to switch test\033[0m\n");
/* Add token to semaphore. Main task will start next test. */
os_sem_release(&rx_stress_main_sem);
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_2_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[2].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[2].num);
} else {
/* Connection failed; resume advertising. */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
console_printf("\033[0;32m>\033[0m");
if (rx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(2);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_3_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[3].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[3].num);
} else {
/* Connection failed; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
console_printf("\033[0;32m>\033[0m");
if (rx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(3);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_4_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[4].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
event->connect.status,
++rx_stress_ctx->con_stat[4].num);
/* Remember connection handler */
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
rx_stress_ctx->conn_handle = event->connect.conn_handle;
} else {
/* Connection failed; resume advertising. */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
if (rx_stress_ctx->con_stat[4].prms_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(4);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_CONN_UPDATE:
if (event->conn_update.status != 0) {
MODLOG_DFLT(INFO, "Connection update failed\n");
} else {
MODLOG_DFLT(INFO, "Connection updated; num=%d\n",
++rx_stress_ctx->con_stat[4].prms_upd_num);
console_printf("\033[0;32m>\033[0m");
}
if (rx_stress_ctx->con_stat[4].prms_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
/* Test completed. */
ble_gap_terminate(rx_stress_ctx->conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_5_con_update(void)
{
int rc;
/* With every next update at least one param must change. Otherwise no
* event occurs and test will not be continued */
struct ble_gap_upd_params params = {
.itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN,
.itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX,
.latency = BLE_GAP_INITIAL_CONN_LATENCY,
/* So let's change e.g. timeout value. Put ...% 2 ? 1 : 2 to make sure
* that value won't grow significantly and will be different with every
* iteration. */
.supervision_timeout = BLE_GAP_INITIAL_SUPERVISION_TIMEOUT +
(rx_stress_ctx->con_stat[5].prms_upd_num % 2 ?
1 : 2),
.min_ce_len = BLE_GAP_INITIAL_CONN_MIN_CE_LEN,
.max_ce_len = BLE_GAP_INITIAL_CONN_MAX_CE_LEN,
};
rc = ble_gap_update_params(rx_stress_ctx->conn_handle, &params);
if (rc == BLE_HS_ENOTCONN) {
MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n");
assert(0);
}
if (rc != 0) {
MODLOG_DFLT(ERROR, "\033[0;31mError during connection update; "
"rc=%d\033[0m\n", rc);
assert(0);
}
return rc;
}
static int
rx_stress_5_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[5].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
event->connect.status,
++rx_stress_ctx->con_stat[5].num);
/* Remember connection handler */
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
rx_stress_ctx->conn_handle = event->connect.conn_handle;
/* Update connection. */
rc = rx_stress_5_con_update();
assert(rc == 0);
} else {
/* Connection failed; resume advertising. */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
if (rx_stress_ctx->con_stat[5].prms_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(5);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_CONN_UPDATE:
if (event->conn_update.status != 0) {
MODLOG_DFLT(INFO, "Connection update failed\n");
} else {
MODLOG_DFLT(INFO, "Connection updated; num=%d\n",
++rx_stress_ctx->con_stat[5].prms_upd_num);
console_printf("\033[0;32m>\033[0m");
}
if (rx_stress_ctx->con_stat[5].prms_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
/* Test completed. */
ble_gap_terminate(rx_stress_ctx->conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
} else {
/* Update connection. */
rc = rx_stress_5_con_update();
assert(rc == 0);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_6_gap_event(struct ble_gap_event *event, void *arg)
{
MODLOG_DFLT(INFO, "Event occurs=%d\n", event->type);
return 0;
}
static int
rx_stress_7_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[7].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
event->connect.status,
++rx_stress_ctx->con_stat[7].num);
/* Remember connection handler */
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
rx_stress_ctx->conn_handle = event->connect.conn_handle;
} else {
/* Connection failed; resume advertising. */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
if (rx_stress_ctx->con_stat[7].phy_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(7);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
if (event->phy_updated.status != 0) {
MODLOG_DFLT(INFO, "PHY update failed\n");
} else {
MODLOG_DFLT(INFO, "PHY updated; num=%d\n",
++rx_stress_ctx->con_stat[7].phy_upd_num);
console_printf("\033[0;32m>\033[0m");
}
if (rx_stress_ctx->con_stat[7].phy_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
/* Test completed. */
ble_gap_terminate(rx_stress_ctx->conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_8_con_update(void)
{
int rc;
uint8_t tx_phys_mask;
uint8_t rx_phys_mask;
ble_gap_read_le_phy(rx_stress_ctx->conn_handle, &tx_phys_mask,
&rx_phys_mask);
/* With every next update at least one param must change */
switch (rx_phys_mask) {
case BLE_GAP_LE_PHY_1M_MASK:
rx_phys_mask = BLE_GAP_LE_PHY_2M_MASK;
break;
case BLE_GAP_LE_PHY_2M_MASK:
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
rx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK;
break;
case BLE_GAP_LE_PHY_CODED_MASK:
#endif
rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
break;
default:
rx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
break;
}
switch (tx_phys_mask) {
case BLE_GAP_LE_PHY_1M_MASK:
tx_phys_mask = BLE_GAP_LE_PHY_2M_MASK;
break;
case BLE_GAP_LE_PHY_2M_MASK:
#if MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_CODED_PHY)
tx_phys_mask = BLE_GAP_LE_PHY_CODED_MASK;
break;
case BLE_GAP_LE_PHY_CODED_MASK:
#endif
tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
break;
default:
tx_phys_mask = BLE_GAP_LE_PHY_1M_MASK;
break;
}
rc = ble_gap_set_prefered_le_phy(rx_stress_ctx->conn_handle,
tx_phys_mask, rx_phys_mask, 0);
if (rc == BLE_HS_ENOTCONN) {
MODLOG_DFLT(INFO, "Device disconnected. Connection update failed\n");
return rc;
}
if (rc != 0) {
MODLOG_DFLT(ERROR, "\033[0;31mError during PHY update; "
"rc=%d\033[0m\n", rc);
}
return rc;
}
static int
rx_stress_8_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[8].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
event->connect.status,
++rx_stress_ctx->con_stat[8].num);
/* Remember connection handler */
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
rx_stress_ctx->conn_handle = event->connect.conn_handle;
/* Update connection. */
rc = rx_stress_8_con_update();
assert(rc == 0);
} else {
/* Connection failed; resume advertising. */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
if (rx_stress_ctx->con_stat[8].phy_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(8);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
if (event->phy_updated.status != 0) {
MODLOG_DFLT(INFO, "PHY update failed\n");
} else {
MODLOG_DFLT(INFO, "PHY updated; num=%d; rx:%d, tx:%d\n",
++rx_stress_ctx->con_stat[8].phy_upd_num,
event->phy_updated.rx_phy, event->phy_updated.tx_phy);
console_printf("\033[0;32m>\033[0m");
}
if (rx_stress_ctx->con_stat[8].phy_upd_num >=
MYNEWT_VAL(BLE_STRESS_REPEAT)) {
/* Test completed. */
ble_gap_terminate(rx_stress_ctx->conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
} else {
/* Update connection. */
rc = rx_stress_8_con_update();
assert(rc == 0);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_9_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[9].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
event->connect.status,
++rx_stress_ctx->con_stat[9].num);
console_printf("\033[0;32m>\033[0m");
/* Remember max number of established connections */
if (rx_stress_ctx->con_stat[9].num >
rx_stress_ctx->con_stat[9].max_num) {
rx_stress_ctx->con_stat[9].max_num = rx_stress_ctx->con_stat[9].num;
}
} else {
/* Connection failed; resume advertising. */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
console_printf("\033[0;31mX\033[0m");
MODLOG_DFLT(INFO, "Connections num: %d\n",
--rx_stress_ctx->con_stat[9].num);
if (rx_stress_ctx->con_stat[9].num != 0 &&
rx_stress_ctx->con_stat[9].num <
MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) {
rx_stress_adv_start_with_rand_addr(TEST_INSTANCE);
} else {
/* When TX device has terminated all connections, stop advertising. */
ble_gap_ext_adv_stop(TEST_INSTANCE);
rx_stress_on_test_finish(9);
}
return 0;
case BLE_GAP_EVENT_ADV_COMPLETE:
/* Stop test when TX device has terminated all connections or
* number of connections has reached the max possible value. */
if (rx_stress_ctx->con_stat[9].num != 0 &&
rx_stress_ctx->con_stat[9].num <
MYNEWT_VAL(BLE_MAX_CONNECTIONS) + 1) {
rx_stress_adv_start_with_rand_addr(TEST_INSTANCE);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
}
return 0;
}
static void
tx_stress_10_l2cap_update_event(uint16_t conn_handle, int status, void *arg)
{
if (status == 0) {
MODLOG_DFLT(INFO, "L2CAP params updated\n");
} else {
MODLOG_DFLT(INFO, "L2CAP params update failed; rc=%d\n", status);
assert(0);
}
}
static int
rx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg)
{
int rc;
struct os_mbuf *data_buf;
static int data_len = 1000;
static int send_cnt = 0;
static bool stalled = false;
struct ble_l2cap_chan_info chan_info;
switch (event->type) {
case BLE_L2CAP_EVENT_COC_CONNECTED:
if (event->connect.status) {
MODLOG_DFLT(INFO, "LE COC error: %d\n", event->connect.status);
return 0;
}
ble_l2cap_get_chan_info(event->connect.chan, &chan_info);
MODLOG_DFLT(INFO,
"LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, "
"dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n",
event->connect.conn_handle,
(uint32_t) event->connect.chan,
chan_info.scid,
chan_info.dcid,
chan_info.our_l2cap_mtu,
chan_info.peer_l2cap_mtu);
struct ble_l2cap_sig_update_params params = {
.itvl_min = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN
.itvl_max = 0x0006,//BLE_GAP_INITIAL_CONN_ITVL_MIN
.slave_latency = 0x0000,
.timeout_multiplier = 0x0100,
};
rc = ble_l2cap_sig_update(event->connect.conn_handle, &params,
&tx_stress_10_l2cap_update_event, NULL);
assert(rc == 0);
return 0;
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
MODLOG_DFLT(INFO, "LE CoC disconnected, chan: 0x%08lx\n",
(uint32_t) event->disconnect.chan);
return 0;
case BLE_L2CAP_EVENT_COC_ACCEPT:
stress_l2cap_coc_accept(event->accept.peer_sdu_size,
event->accept.chan);
return 0;
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx);
MODLOG_DFLT(INFO, "L2CAP server received data; num=%d\n",
++rx_stress_ctx->rcv_num);
rx_stress_ctx->chan = event->receive.chan;
/* In this use case, receiving any data by RX device L2CAP server means
* request from TX device to send data. */
/* Do not send if stalled on the last sending. */
if (stalled) {
return 0;
}
break;
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
MODLOG_DFLT(INFO, "L2CAP unstalled event\n");
stalled = false;
/* Send if was stalled on the last request to send. */
if (rx_stress_ctx->rcv_num > send_cnt) {
break;
}
return 0;
default:
MODLOG_DFLT(INFO, "Other L2CAP event occurs: %d\n", event->type);
return 0;
}
/* Send pattern data */
/* Get mbuf for adv data */
data_buf = os_msys_get_pkthdr(data_len, 0);
MODLOG_DFLT(INFO, "Data buf %s\n", data_buf ? "OK" : "NOK");
assert(data_buf != NULL);
/* Fill mbuf with the pattern */
stress_fill_mbuf_with_pattern(data_buf, data_len);
/* Send data */
rc = ble_l2cap_send(rx_stress_ctx->chan, data_buf);
MODLOG_DFLT(INFO, "Return code=%d\n", rc);
if (rc) {
if (rc == BLE_HS_ESTALLED) {
MODLOG_DFLT(INFO, "L2CAP stalled - waiting\n");
stalled = true;
} else {
MODLOG_DFLT(INFO, "Sending data via L2CAP failed with error "
"code %d\n", rc);
}
}
MODLOG_DFLT(INFO, " %d, %d\n", ++send_cnt, data_len);
data_len += 500;
return 0;
}
static int
rx_stress_10_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc out_desc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[10].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "Connection established; status=%d; num=%d",
event->connect.status,
++rx_stress_ctx->con_stat[10].num);
ble_gap_conn_find(event->connect.conn_handle, &out_desc);
MODLOG_DFLT(INFO, "Address %s",
addr_str(out_desc.peer_id_addr.val));
} else {
/* Connection failed; resume advertising. */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason);
rx_stress_ctx->completed[10] = true;
rx_stress_on_test_finish(10);
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_11_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[11].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[11].num);
} else {
MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
"status=%d\033[0m\n", event->connect.status);
}
ble_gap_terminate(event->connect.conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
console_printf("\033[0;32m>\033[0m");
if (rx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(11);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_12_gap_event(struct ble_gap_event *event, void *arg)
{
int om_len = 10000;
struct os_mbuf *om;
int64_t us = 0;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[12].num);
rx_stress_ctx->conn_handle = event->connect.conn_handle;
break;
} else {
/* Connection failed; resume advertising */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
rx_stress_ctx->s12_notif_time = rx_stress_ctx->time_sum /
rx_stress_ctx->send_num;
MODLOG_DFLT(INFO, "Average time: %d us\n",
rx_stress_ctx->s12_notif_time);
rx_stress_on_test_finish(12);
return 0;
case BLE_GAP_EVENT_NOTIFY_TX:
rx_stress_ctx->end_us = os_get_uptime_usec();
MODLOG_DFLT(INFO, "Notify TX event\n");
if (!event->notify_tx.status) {
/* Send next only after previous indication is done */
return 0;
}
assert(event->notify_tx.status == BLE_HS_EDONE);
if (rx_stress_ctx->send_num++ >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
ble_gap_terminate(event->notify_tx.conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
return 0;
}
/* Time of data sending */
us = rx_stress_ctx->end_us - rx_stress_ctx->begin_us;
console_printf("Indication time: %lld\n", us);
rx_stress_ctx->time_sum += us;
console_printf("\033[0;32m>\033[0m");
break;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
/* Indicate data pattern */
rx_stress_ctx->begin_us = os_get_uptime_usec();
om = os_msys_get_pkthdr(om_len, 0);
stress_fill_mbuf_with_pattern(om, om_len);
rc = ble_gatts_indicate_custom(rx_stress_ctx->conn_handle, hrs_hrm_handle,
om);
assert(rc == 0);
return 0;
}
static struct ble_npl_event rx_stress_13_notify_ev;
static void
rx_stress_13_notify_ev_func(struct ble_npl_event *ev)
{
struct os_mbuf *om;
int rc;
om = ble_hs_mbuf_from_flat(test_6_pattern, 10);
rc = ble_gatts_notify_custom(rx_stress_ctx->conn_handle,
hrs_hrm_handle, om);
assert(rc == 0);
}
static int
rx_stress_13_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[13].num);
rx_stress_ctx->conn_handle = event->connect.conn_handle;
rx_stress_ctx->begin_us = os_get_uptime_usec();
ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(),
&rx_stress_13_notify_ev);
break;
} else {
/* Connection failed; resume advertising */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
assert(0);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
rx_stress_ctx->time_sum = rx_stress_ctx->end_us -
rx_stress_ctx->begin_us;
rx_stress_ctx->s13_notif_time = rx_stress_ctx->time_sum /
rx_stress_ctx->send_num;
MODLOG_DFLT(INFO, "Average time: %d us\n",
rx_stress_ctx->s13_notif_time);
rx_stress_on_test_finish(13);
return 0;
case BLE_GAP_EVENT_NOTIFY_TX:
MODLOG_DFLT(INFO, "Notify TX event; num=%d\n",
++rx_stress_ctx->send_num);
assert(event->notify_tx.status == 0);
if (rx_stress_ctx->send_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_ctx->end_us = os_get_uptime_usec();
ble_gap_terminate(event->connect.conn_handle,
BLE_ERR_REM_USER_CONN_TERM);
return 0;
}
ble_npl_eventq_put((struct ble_npl_eventq *)os_eventq_dflt_get(),
&rx_stress_13_notify_ev);
break;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
return 0;
}
static int
rx_stress_14_gap_event(struct ble_gap_event *event, void *arg)
{
int bytes_num = 10000;
static struct os_mbuf *om = NULL;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[14].num);
rx_stress_ctx->conn_handle = event->connect.conn_handle;
} else {
/* Connection failed; resume advertising */
MODLOG_DFLT(INFO, "Connection failed; status=%d ",
event->connect.status);
assert(0);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
rx_stress_ctx->s14_notif_time = rx_stress_ctx->time_sum /
rx_stress_ctx->send_num;
MODLOG_DFLT(INFO, "Average time: %d us\n",
rx_stress_ctx->s14_notif_time);
rx_stress_on_test_finish(14);
return 0;
case BLE_GAP_EVENT_NOTIFY_TX:
MODLOG_DFLT(INFO, "Notify TX event\n");
assert(event->notify_tx.status == 0);
return 0;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "Subscribe event\n");
if (event->subscribe.cur_notify) {
MODLOG_DFLT(INFO, "Notification subscribed\n");
++rx_stress_ctx->send_num;
/* Notify data pattern */
om = ble_hs_mbuf_from_flat(test_6_pattern, bytes_num);
rc = ble_gatts_notify_custom(rx_stress_ctx->conn_handle,
hrs_hrm_handle, om);
assert(rc == 0);
console_printf("\033[0;32m>\033[0m");
} else if (event->subscribe.prev_notify) {
MODLOG_DFLT(INFO, "Notification unsubscribed\n");
} else {
MODLOG_DFLT(INFO, "Other subscription\n");
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
static int
rx_stress_15_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
++rx_stress_ctx->con_stat[15].attempts_num;
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
MODLOG_DFLT(INFO, "\nConnection established; status=%d; num=%d\n",
event->connect.status,
++rx_stress_ctx->con_stat[15].num);
} else {
MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; "
"status=%d\033[0m\n", event->connect.status);
assert(0);
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "Disconnect; reason=%d \n",
event->disconnect.reason);
console_printf("\033[0;32m>\033[0m");
if (rx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) {
rx_stress_on_test_finish(15);
} else {
/* Connection terminated; resume advertising. */
rx_stress_adv_start(TEST_INSTANCE);
}
return 0;
default:
MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type);
return 0;
}
}
/* Advert settings for each test. */
static struct rx_stress_adv_set rx_stress_adv_sets[] = {
{
.instance = SWITCHER_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[0],
.legacy_pdu = LEGACY_ADVERT,
.cb = rx_stress_0_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = SWITCHER_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[0],
.legacy_pdu = LEGACY_ADVERT,
.cb = rx_stress_0_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[2],
.legacy_pdu = LEGACY_ADVERT,
.cb = rx_stress_2_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[3],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_3_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[4],
.legacy_pdu = LEGACY_ADVERT,
.cb = rx_stress_4_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[5],
.legacy_pdu = LEGACY_ADVERT,
.cb = rx_stress_5_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[6],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_6_gap_event,
.pattern_data = test_6_pattern,
.pattern_len = 1640,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[7],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_7_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[8],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_8_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[9],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_9_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[10],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_10_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[11],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_11_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[12],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_12_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[13],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_13_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[14],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_14_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
{
.instance = TEST_INSTANCE,
.instance_uuid128 = rx_stress_uuid128[15],
.legacy_pdu = EXTENDED_ADVERT,
.cb = rx_stress_15_gap_event,
.pattern_data = NULL,
.pattern_len = 0,
},
};
static void
rx_stress_start(int test_num)
{
int rc;
/* Init semaphore with 0 tokens. */
os_sem_init(&rx_stress_main_sem, 0);
console_printf("\033[1;36mStart test num %d - ", test_num);
/* Start test. */
switch (test_num) {
case 2:
console_printf("Stress Connect/Disconnect legacy\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[2]);
break;
case 3:
console_printf("Stress Connect/Disconnect ext adv\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[3]);
break;
case 4:
console_printf("Stress connection params update (TX)\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[4]);
break;
case 5:
console_printf("Stress connection params update (RX)\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[5]);
break;
case 6:
/* Start SWITCHER advert that gives possibility to remotely start
* next test advert */
console_printf("Stress Scan\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[0]);
rx_stress_simple_adv(&rx_stress_adv_sets[6]);
break;
case 7:
console_printf("Stress PHY Update (TX)\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[7]);
break;
case 8:
console_printf("Stress PHY Update (RX)\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[8]);
break;
case 9:
console_printf("Stress multi connection\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[9]);
break;
case 10:
console_printf("Stress L2CAP send\033[0m\n");
rc = ble_l2cap_create_server(TEST_PSM, STRESS_COC_MTU,
rx_stress_10_l2cap_event, NULL);
assert(rc == 0);
rx_stress_simple_adv(&rx_stress_adv_sets[10]);
break;
case 11:
console_printf("Stress Advertise/Connect/Disconnect\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[11]);
break;
case 12:
console_printf("Stress GATT indication\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[12]);
break;
case 13:
console_printf("Stress GATT notification\033[0m\n");
ble_npl_event_init(&rx_stress_13_notify_ev,
rx_stress_13_notify_ev_func, NULL);
rx_stress_simple_adv(&rx_stress_adv_sets[13]);
break;
case 14:
console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[14]);
break;
case 15:
console_printf("Stress Connect/Send/Disconnect\033[0m\n");
rx_stress_simple_adv(&rx_stress_adv_sets[15]);
break;
default:
console_printf("\033[0;31mFound test, but do not know how to perform."
"\033[0m\n");
assert(0);
}
/* Wait for the test to finish. Then 1 token will be released
* allowing to pass through semaphore. */
os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER);
ble_gap_ext_adv_stop(SWITCHER_INSTANCE);
stress_clear_ctx_reusable_var(rx_stress_ctx);
}
static void
stress_uuid_init()
{
uint8_t i;
for (i = 0; i < STRESS_UUIDS_NUM; ++i) {
/* Fill all 16 bytes of UUID128 */
rx_stress_uuid128[i][0] = 0xC0;
rx_stress_uuid128[i][1] = 0xDE;
rx_stress_uuid128[i][2] = i;
memcpy(&rx_stress_uuid128[i][3], MYNEWT_VAL(BLE_STRESS_UUID_BASE), 13);
}
}
static void
rx_stress_read_command_cb(void)
{
console_printf("Start testing\n");
os_sem_release(&rx_stress_main_sem);
}
static void
rx_stress_main_task_fn(void *arg)
{
int i;
stress_uuid_init();
console_printf("\033[1;36mRX device\033[0m\n");
console_printf("Press ENTER to start: \n");
console_init(&rx_stress_read_command_cb);
/* Waite for pressing ENTER in console */
os_sem_pend(&rx_stress_main_sem, OS_TIMEOUT_NEVER);
/* Standard tests perform */
for (i = 11; i < STRESS_UUIDS_NUM; ++i) {
/* Start test. */
rx_stress_start(i);
}
/* Print tests results */
com_stress_print_report(rx_stress_ctx);
/* Task should never return */
while (1) {
}
}
void
rx_stress_start_auto()
{
/* Start task that will run all stress tests one by one. */
os_task_init(&rx_stress_main_task, "rx_stress_main_task",
rx_stress_main_task_fn, NULL, RX_STRESS_MAIN_TASK_PRIO,
OS_WAIT_FOREVER, rx_stress_main_task_stack,
RX_STRESS_MAIN_TASK_STACK_SIZE);
}