| /* |
| * 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 <console/console.h> |
| #include <host/util/util.h> |
| #include <host/ble_l2cap.h> |
| #include "tx_stress.h" |
| |
| /* Main test task priority. Set a high value so that the task does not |
| * interfere with event handling */ |
| #define TX_STRESS_MAIN_TASK_PRIO 0xf0 |
| #define BASE_UUID_LEN 13 |
| |
| /* Contexts for stress tests. */ |
| static struct com_stress_test_ctx tx_stress_ctxD = { |
| .conn_handle = 0xffff, |
| .cur_test_id = 0, |
| }; |
| |
| static struct com_stress_test_ctx *tx_stress_ctx; |
| |
| /* Define stack, object and semaphore for test main task. */ |
| #define TX_STRESS_MAIN_TASK_STACK_SIZE (500) |
| static struct os_task tx_stress_main_task; |
| static os_stack_t tx_stress_main_task_stack[TX_STRESS_MAIN_TASK_STACK_SIZE]; |
| static struct os_sem tx_stress_main_sem; |
| /* Test use case and address of test advertiser. */ |
| static int tx_stress_use_case; |
| static int completed_tests = 0; |
| |
| static void |
| tx_stress_on_test_finish(int test_num) |
| { |
| console_printf("\033[0;32m\nStress test %d completed\033[0m\n", test_num); |
| ++completed_tests; |
| tx_stress_ctx->completed[test_num] = true; |
| os_sem_release(&tx_stress_main_sem); |
| } |
| |
| static void |
| tx_stress_simple_scan(ble_gap_event_fn *cb, uint16_t duration) |
| { |
| uint8_t own_addr_type; |
| struct ble_gap_ext_disc_params params = {0}; |
| int rc; |
| |
| /* Figure out address to use while scanning. */ |
| rc = ble_hs_id_infer_auto(0, &own_addr_type); |
| if (rc != 0) { |
| console_printf("\033[0;31mError determining own address type; " |
| "rc=%d\033[0m\n", rc); |
| assert(0); |
| } |
| |
| params.itvl = BLE_GAP_SCAN_FAST_INTERVAL_MAX; |
| params.passive = 1; |
| params.window = BLE_GAP_SCAN_FAST_WINDOW; |
| |
| rc = ble_gap_ext_disc(own_addr_type, duration, 0, 0, 0, 0, ¶ms, NULL, |
| cb, NULL); |
| |
| if (rc != 0) { |
| console_printf("\033[0;31mError initiating GAP discovery procedure" |
| "; rc=%d\033[0m\n", rc); |
| } |
| } |
| |
| static int |
| tx_stress_simple_connect(ble_gap_event_fn *cb, int test_num, struct ble_gap_conn_params *params) |
| { |
| uint8_t own_addr_type; |
| int rc; |
| |
| /* Set so any PHY mask allowed. */ |
| ble_gap_set_prefered_default_le_phy(TX_PHY_MASK, RX_PHY_MASK); |
| |
| /* Figure out address to use while connecting. */ |
| rc = ble_hs_id_infer_auto(0, &own_addr_type); |
| if (rc != 0) { |
| MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; " |
| "rc=%d\033[0m\n", rc); |
| return rc; |
| } |
| |
| MODLOG_DFLT(INFO, "Connection attempt: %d\n", |
| ++tx_stress_ctx->con_stat[test_num].attempts_num); |
| |
| rc = ble_gap_ext_connect(own_addr_type, &tx_stress_ctx->dev_addr, |
| 10000, |
| BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK, |
| params, params, NULL, cb, NULL); |
| |
| if (rc != 0) { |
| MODLOG_DFLT(INFO, "\033[0;31mError during connection; rc=%d\033[0m\n", |
| rc); |
| } |
| |
| return rc; |
| } |
| |
| static int |
| tx_stress_find_test(struct ble_gap_ext_disc_desc *ext_disc) |
| { |
| struct ble_hs_adv_fields fields; |
| int data_len; |
| int rc; |
| |
| /* Parser refuses greater length data than 31. But known UUID128 will be |
| * in first 31 bytes of adv data first packet. */ |
| if (ext_disc->length_data > 31) { |
| data_len = 31; |
| } else { |
| data_len = ext_disc->length_data; |
| } |
| |
| /* Parse part of adv data. */ |
| ble_hs_adv_parse_fields(&fields, ext_disc->data, data_len); |
| print_adv_fields(&fields); |
| |
| /* UUID128 service data of stress test advert includes only UUID128. */ |
| if (fields.svc_data_uuid128_len != 16) { |
| return -1; |
| } |
| |
| /* Check if service data include known UUID128. */ |
| rc = memcmp(fields.svc_data_uuid128, (uint8_t[]) {0xC0, 0xDE}, 2); |
| if (rc) { |
| return -1; |
| } |
| |
| rc = memcmp(fields.svc_data_uuid128 + 3, MYNEWT_VAL(BLE_STRESS_UUID_BASE), |
| BASE_UUID_LEN); |
| |
| if (rc != 0) { |
| return -1; |
| } |
| |
| /* This UUID 128 byte indicates the stress test ID to be executed. */ |
| return fields.svc_data_uuid128[2]; |
| } |
| |
| static int |
| tx_stress_switcher_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| 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, "Success to connect to device\n"); |
| return 0; |
| } else if (event->connect.status == BLE_HS_ETIMEOUT_HCI) { |
| MODLOG_DFLT(INFO, "Connection timeout\n"); |
| } else { |
| MODLOG_DFLT(INFO, "Error: connection attempt failed; status=%d\n", |
| event->connect.status); |
| } |
| /* Connect to rx device just to give it a signal to switch test. */ |
| tx_stress_simple_connect(tx_stress_switcher_gap_event, |
| tx_stress_ctx->cur_test_id, NULL); |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| os_sem_release(&tx_stress_main_sem); |
| return 0; |
| |
| case BLE_GAP_EVENT_EXT_DISC: |
| /* Check if caught advert contains known UUID128. The UUID128 |
| * contains the ID of test use case to be executed. */ |
| rc = tx_stress_find_test(&event->ext_disc); |
| if (rc == 0) { |
| tx_stress_ctx->dev_addr = event->ext_disc.addr; |
| /* Stop scanning. */ |
| ble_gap_disc_cancel(); |
| /* Add token to semaphore. Main task will start the test. */ |
| os_sem_release(&tx_stress_main_sem); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISC_COMPLETE: |
| MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n", |
| event->disc_complete.reason); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static void |
| tx_stress_switch_test() |
| { |
| tx_stress_simple_scan(tx_stress_switcher_gap_event, 0); |
| os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); |
| |
| tx_stress_simple_connect(tx_stress_switcher_gap_event, 0, NULL); |
| } |
| |
| static int |
| tx_stress_1_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. */ |
| MODLOG_DFLT(INFO, "Connection %s; status=%d ", |
| event->connect.status == 0 ? "established" : "failed", |
| event->connect.status); |
| |
| if (event->connect.status == 0) { |
| /* Connection successfully established. In this use case |
| * it is error of 'Connect cancel'. Stress test failed. */ |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| ++tx_stress_ctx->con_stat[1].num; |
| |
| ble_gap_terminate(event->connect.conn_handle, BLE_ERR_NO_PAIRING); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| MODLOG_DFLT(INFO, "Disconnect; reason=%d ", event->disconnect.reason); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static void |
| tx_stress_1_test() |
| { |
| int rc; |
| uint8_t own_addr_type; |
| ble_addr_t rnd_rx_addr; |
| int delay_time; |
| |
| rc = ble_gap_disc_active(); |
| assert(rc == 0); |
| |
| /* Figure out address to use while advertising. */ |
| rc = ble_hs_id_infer_auto(0, &own_addr_type); |
| if (rc != 0) { |
| MODLOG_DFLT(INFO, "\033[0;31mError determining own address type; " |
| "rc=%d\033[0m\n", rc); |
| os_sem_release(&tx_stress_main_sem); |
| return; |
| } |
| |
| while (tx_stress_ctx->con_stat[1].attempts_num < |
| MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| /* Rand ble address to connect*/ |
| rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr); |
| assert (rc == 0); |
| |
| MODLOG_DFLT(INFO, "Connection attempt; num=%d\n", |
| ++tx_stress_ctx->con_stat[1].attempts_num); |
| |
| rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL, |
| tx_stress_1_gap_event, NULL); |
| |
| if (rc != 0) { |
| MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n", |
| rc); |
| os_sem_release(&tx_stress_main_sem); |
| return; |
| } |
| |
| MODLOG_DFLT(INFO, "Connect cancel\n"); |
| ble_gap_conn_cancel(); |
| console_printf("\033[0;32m>\033[0m"); |
| } |
| |
| console_printf( |
| "\033[0;32m\nFirst part of test completed\nStart second part: " |
| "Connect->random delay->cancel\n\033[0m"); |
| |
| while (tx_stress_ctx->con_stat[1].attempts_num < |
| 2 * MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| /* Rand ble address to connect*/ |
| rc = ble_hs_id_gen_rnd(1, &rnd_rx_addr); |
| assert (rc == 0); |
| |
| MODLOG_DFLT(INFO, "Connection attempt; num=%d\n", |
| ++tx_stress_ctx->con_stat[1].attempts_num); |
| |
| delay_time = rand() % 1000; |
| |
| MODLOG_DFLT(INFO, "Time to delay=%d\n", delay_time); |
| |
| rc = ble_gap_connect(own_addr_type, &rnd_rx_addr, 10000, NULL, |
| tx_stress_1_gap_event, NULL); |
| |
| if (rc != 0) { |
| MODLOG_DFLT(INFO, "\033[0;31mConnection error; rc=%d\033[0m\n", |
| rc); |
| os_sem_release(&tx_stress_main_sem); |
| return; |
| } |
| |
| os_time_delay(os_time_ms_to_ticks32(delay_time)); |
| |
| MODLOG_DFLT(INFO, "Connect cancel\n"); |
| ble_gap_conn_cancel(); |
| console_printf("\033[0;32m>\033[0m"); |
| } |
| |
| tx_stress_on_test_finish(1); |
| } |
| |
| static int |
| tx_stress_2_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: |
| /* A new connection was established or a connection attempt failed. */ |
| if (event->connect.status == 0) { |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| |
| ++tx_stress_ctx->con_stat[2].num; |
| rc = ble_gap_conn_find(event->connect.conn_handle, &desc); |
| assert(rc == 0); |
| |
| tx_stress_ctx->conn_handle = desc.conn_handle; |
| |
| ble_gap_terminate(event->connect.conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| 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 (tx_stress_ctx->con_stat[2].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| tx_stress_on_test_finish(2); |
| return 0; |
| } |
| tx_stress_simple_connect(tx_stress_2_gap_event, |
| tx_stress_ctx->cur_test_id, NULL); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_3_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| 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) { |
| ++tx_stress_ctx->con_stat[3].num; |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| |
| rc = ble_gap_terminate(event->connect.conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| |
| MODLOG_DFLT(INFO, "rc=%d\n", rc); |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| 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 (tx_stress_ctx->con_stat[3].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| tx_stress_on_test_finish(3); |
| return 0; |
| } |
| tx_stress_simple_connect(tx_stress_3_gap_event, |
| tx_stress_ctx->cur_test_id, NULL); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_4_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 + |
| (tx_stress_ctx->con_stat[4].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(tx_stress_ctx->conn_handle, ¶ms); |
| |
| 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 |
| tx_stress_4_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| int rc; |
| |
| switch (event->type) { |
| case BLE_GAP_EVENT_CONNECT: |
| ++tx_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, "Success to connect to device\n"); |
| |
| ++tx_stress_ctx->con_stat[4].num; |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| |
| tx_stress_4_con_update(); |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", |
| event->disconnect.reason); |
| tx_stress_on_test_finish(4); |
| 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", |
| ++tx_stress_ctx->con_stat[4].prms_upd_num); |
| console_printf("\033[0;32m>\033[0m"); |
| } |
| |
| if (tx_stress_ctx->con_stat[4].prms_upd_num >= |
| MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| ble_gap_terminate(tx_stress_ctx->conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| } else { |
| /* Update connection. */ |
| rc = tx_stress_4_con_update(); |
| |
| if (rc != 0) { |
| MODLOG_DFLT(INFO, "\033[0;31mError: update fail; " |
| "rc=%d\033[0m\n", rc); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_CONN_UPDATE_REQ: |
| MODLOG_DFLT(INFO, "Connection update request\n"); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_5_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, "Success to connect to device\n"); |
| |
| ++tx_stress_ctx->con_stat[5].num; |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| } else { |
| console_printf("\033[0;31mError: Update fail; " |
| "status=%d\033[0m\n", event->connect.status); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", |
| event->disconnect.reason); |
| tx_stress_on_test_finish(5); |
| 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", |
| ++tx_stress_ctx->con_stat[5].prms_upd_num); |
| console_printf("\033[0;32m>\033[0m"); |
| } |
| |
| if (tx_stress_ctx->con_stat[5].prms_upd_num >= |
| MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| ble_gap_terminate(tx_stress_ctx->conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_CONN_UPDATE_REQ: |
| MODLOG_DFLT(INFO, "Connection update request\n"); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_6_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| static int start_id = 0; |
| int use_case = 0; |
| int adv_pattern_len; |
| const uint8_t *adv_pattern; |
| |
| switch (event->type) { |
| case BLE_GAP_EVENT_EXT_DISC: |
| |
| /* Instance 0 reserved for SWITCH advert. */ |
| if (event->ext_disc.sid == 0) { |
| return 0; |
| } |
| |
| /* Check if advertiser is known rx device. */ |
| if (memcmp(tx_stress_ctx->dev_addr.val, |
| event->ext_disc.addr.val, 6) != 0) { |
| return 0; |
| } |
| |
| /* Return -1 if not first package of advert. */ |
| use_case = tx_stress_find_test(&event->ext_disc); |
| |
| if (use_case > 0) { |
| /* If first package of advert */ |
| ++tx_stress_ctx->s6_rcv_adv_first; |
| start_id = 0; |
| adv_pattern = &event->ext_disc.data[29]; |
| adv_pattern_len = event->ext_disc.length_data - 29; |
| } else { |
| if (start_id == 0) { |
| return 0; |
| } |
| /* If not first package of advert */ |
| adv_pattern = event->ext_disc.data; |
| adv_pattern_len = event->ext_disc.length_data; |
| } |
| |
| /* Check data pattern */ |
| if (memcmp(adv_pattern, test_6_pattern + start_id, |
| adv_pattern_len) != 0) { |
| /* Pattern does not match. May lost some data or package. |
| * Reset data pattern index. */ |
| start_id = 0; |
| return 0; |
| } |
| |
| /* At the next adv data package, start comparing from this index.*/ |
| start_id += adv_pattern_len; |
| |
| /* Check if last packet of advert. */ |
| if (event->ext_disc.data_status == |
| BLE_GAP_EXT_ADV_DATA_STATUS_COMPLETE) { |
| /* Got all packets of advert. */ |
| ++tx_stress_ctx->s6_rcv_adv_suc; |
| MODLOG_DFLT(INFO, "Got all packets of advert. num=%d\n", |
| tx_stress_ctx->s6_rcv_adv_suc); |
| console_printf("\033[0;32m>\033[0m"); |
| start_id = 0; |
| |
| if (tx_stress_ctx->s6_rcv_adv_suc >= |
| MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| /* Stop scanning. */ |
| ble_gap_disc_cancel(); |
| tx_stress_on_test_finish(6); |
| } |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISC_COMPLETE: |
| MODLOG_DFLT(INFO, "Discovery complete; reason=%d\n", |
| event->disc_complete.reason); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static void |
| tx_stress_6_perform(void) |
| { |
| tx_stress_simple_scan(tx_stress_6_gap_event, 0); |
| os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); |
| tx_stress_switch_test(); |
| } |
| |
| static int |
| tx_stress_7_phy_update(void) |
| { |
| int rc; |
| uint8_t tx_phys_mask; |
| uint8_t rx_phys_mask; |
| |
| ble_gap_read_le_phy(tx_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(tx_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 |
| tx_stress_7_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| 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, "Success to connect to device\n"); |
| |
| ++tx_stress_ctx->con_stat[7].num; |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| |
| tx_stress_7_phy_update(); |
| } else { |
| console_printf("\033[0;31mError: Update fail; " |
| "status=%d\033[0m\n", event->connect.status); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", |
| event->disconnect.reason); |
| tx_stress_on_test_finish(7); |
| return 0; |
| |
| case BLE_GAP_EVENT_CONN_UPDATE: |
| MODLOG_DFLT(INFO, "Connection updated\n"); |
| return 0; |
| |
| case BLE_GAP_EVENT_CONN_UPDATE_REQ: |
| MODLOG_DFLT(INFO, "Connection update request\n"); |
| 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", |
| ++tx_stress_ctx->con_stat[7].phy_upd_num, |
| event->phy_updated.rx_phy, event->phy_updated.tx_phy); |
| console_printf("\033[0;32m>\033[0m"); |
| } |
| |
| if (tx_stress_ctx->con_stat[7].phy_upd_num >= |
| MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| ble_gap_terminate(tx_stress_ctx->conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| } else { |
| /* Update connection. */ |
| rc = tx_stress_7_phy_update(); |
| if (rc != 0) { |
| console_printf("\033[0;31mError: PHPY update fail; " |
| "rc=%d\033[0m\n", event->phy_updated.status); |
| assert(0); |
| } |
| } |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_8_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, "Success to connect to device\n"); |
| |
| ++tx_stress_ctx->con_stat[8].num; |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", |
| event->disconnect.reason); |
| tx_stress_on_test_finish(8); |
| 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", |
| ++tx_stress_ctx->con_stat[8].prms_upd_num); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_CONN_UPDATE_REQ: |
| MODLOG_DFLT(INFO, "Connection update request\n"); |
| 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", |
| ++tx_stress_ctx->con_stat[8].phy_upd_num); |
| console_printf("\033[0;32m>\033[0m"); |
| } |
| |
| if (tx_stress_ctx->con_stat[8].phy_upd_num >= |
| MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| ble_gap_terminate(tx_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 |
| tx_stress_9_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| ble_addr_t addr; |
| int test; |
| struct ble_gap_conn_params conn_params = { |
| .scan_itvl = 0x0010, |
| .scan_window = 0x0010, |
| .itvl_min = BLE_GAP_INITIAL_CONN_ITVL_MIN, |
| .itvl_max = BLE_GAP_INITIAL_CONN_ITVL_MAX, |
| .latency = 0, |
| .supervision_timeout = 0x0C80, |
| .min_ce_len = 0x0010, |
| .max_ce_len = 0x0300, |
| }; |
| |
| switch (event->type) { |
| case BLE_GAP_EVENT_EXT_DISC: |
| /* Looking for next instance of test 9 advert. */ |
| test = tx_stress_find_test(&event->ext_disc); |
| /* To avoid messing by rest of test 9 events in queue, check if handle |
| * filled */ |
| if (test == 9 && tx_stress_ctx->conn_handle == 0xffff) { |
| tx_stress_ctx->conn_handle = 0; |
| ble_gap_disc_cancel(); |
| tx_stress_ctx->dev_addr = event->ext_disc.addr; |
| tx_stress_simple_connect(tx_stress_9_gap_event, |
| tx_stress_ctx->cur_test_id, &conn_params); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISC_COMPLETE: |
| if (event->disc_complete.reason == 0 && !tx_stress_ctx->completed[9]) { |
| console_printf("\033[0;31mScanning timeout\033[0m"); |
| tx_stress_ctx->completed[9] = true; |
| os_sem_release(&tx_stress_main_sem); |
| return 0; |
| } |
| break; |
| |
| case BLE_GAP_EVENT_CONNECT: |
| /* A new connection was established or a connection attempt failed. */ |
| if (event->connect.status == 0) { |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| MODLOG_DFLT(INFO, "Connections num: %d\n", |
| ++tx_stress_ctx->con_stat[9].num); |
| console_printf("\033[0;32m>\033[0m"); |
| /* Remember max number of handled connections */ |
| if (tx_stress_ctx->con_stat[9].num > |
| tx_stress_ctx->con_stat[9].max_num) { |
| tx_stress_ctx->con_stat[9].max_num = tx_stress_ctx->con_stat[9].num; |
| } |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| } |
| break; |
| |
| 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", |
| --tx_stress_ctx->con_stat[9].num); |
| |
| if (tx_stress_ctx->con_stat[9].num == 0) { |
| os_sem_release(&tx_stress_main_sem); |
| return 0; |
| } |
| break; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| |
| if (tx_stress_ctx->completed[9]) { |
| return 0; |
| } |
| |
| /* End use case after specified number of scan times or max config number |
| * of connections */ |
| if (tx_stress_ctx->con_stat[9].attempts_num < |
| MYNEWT_VAL(BLE_STRESS_REPEAT) && |
| tx_stress_ctx->con_stat[9].max_num < MYNEWT_VAL(BLE_MAX_CONNECTIONS)) { |
| if (ble_gap_disc_active() == 0) { |
| /* Generate and set new random address */ |
| ble_hs_id_gen_rnd(0, &addr); |
| ble_hs_id_set_rnd(addr.val); |
| tx_stress_ctx->conn_handle = 0xffff; |
| /* Scan for next instance of the test. */ |
| tx_stress_simple_scan(tx_stress_9_gap_event, 2000); |
| } |
| } else { |
| tx_stress_ctx->completed[9] = true; |
| os_sem_release(&tx_stress_main_sem); |
| } |
| return 0; |
| } |
| |
| static void |
| tx_stress_9_perform() |
| { |
| int i, rc; |
| |
| /* Scan for next instance of the test. */ |
| tx_stress_simple_scan(tx_stress_9_gap_event, 2000); |
| |
| os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); |
| |
| /* On use case finishing terminate all handled connections */ |
| for (i = 0; i <= MYNEWT_VAL(BLE_MAX_CONNECTIONS); ++i) { |
| rc = ble_gap_conn_find(i, NULL); |
| if (rc == 0) { |
| MODLOG_DFLT(INFO, "Terminating...\n"); |
| ble_gap_terminate(i, BLE_ERR_REM_USER_CONN_TERM); |
| } |
| } |
| |
| os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); |
| tx_stress_on_test_finish(9); |
| } |
| |
| static int |
| tx_stress_10_l2cap_send(struct ble_l2cap_chan *chan, uint8_t *data, |
| int data_len) |
| { |
| struct os_mbuf *data_buf; |
| int rc; |
| |
| /* Get mbuf for adv data */ |
| data_buf = os_msys_get_pkthdr(data_len, 0); |
| assert(data != NULL); |
| |
| /* Fill mbuf with pattern data */ |
| rc = os_mbuf_append(data_buf, data, data_len); |
| |
| if (rc) { |
| os_mbuf_free_chain(data_buf); |
| assert(0); |
| } |
| |
| /* Send data with L2CAP */ |
| rc = ble_l2cap_send(chan, data_buf); |
| |
| return rc; |
| } |
| |
| void tx_stress_10_timer_ev_cb(struct os_event *ev) |
| { |
| assert(ev != NULL); |
| |
| if (tx_stress_ctx->rcv_data_flag) { |
| return; |
| } |
| |
| tx_stress_ctx->timeout_flag = true; |
| MODLOG_DFLT(INFO, "L2CAP receiving timeout\n"); |
| ble_gap_terminate(tx_stress_ctx->conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| } |
| |
| static void |
| tx_stress_10_l2cap_send_req() |
| { |
| int rc; |
| /* Send a request to the RX device */ |
| |
| MODLOG_DFLT(INFO, "L2CAP sending request\n"); |
| tx_stress_ctx->timeout_flag = false; |
| tx_stress_ctx->rcv_data_flag = false; |
| stress_start_timer(7000, tx_stress_10_timer_ev_cb); |
| |
| /* Get the sending begin time */ |
| tx_stress_ctx->begin_us = os_get_uptime_usec(); |
| |
| /* Send anything just to give a signal to start sending data |
| * by RX device */ |
| rc = tx_stress_10_l2cap_send(tx_stress_ctx->chan, (uint8_t *) "S", |
| sizeof("S")); |
| assert(rc == 0); |
| } |
| |
| static int |
| tx_stress_10_l2cap_event(struct ble_l2cap_event *event, void *arg) |
| { |
| static int i = 0; |
| int64_t us = 0; |
| struct ble_l2cap_chan_info chan_info; |
| |
| switch (event->type) { |
| case BLE_L2CAP_EVENT_COC_CONNECTED: |
| /* A new L2CAP connection was established. */ |
| if (event->connect.status == 0) { |
| MODLOG_DFLT(INFO, "Established L2CAP connection\n"); |
| tx_stress_ctx->chan = event->connect.chan; |
| |
| 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); |
| |
| tx_stress_10_l2cap_send_req(); |
| } |
| return 0; |
| |
| case BLE_L2CAP_EVENT_COC_DISCONNECTED: |
| MODLOG_DFLT(INFO, "Remote device disconnected from L2CAP server\n"); |
| 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: |
| /* Get the time of data receive */ |
| tx_stress_ctx->end_us = os_get_uptime_usec(); |
| |
| /* And test after timeout */ |
| if (tx_stress_ctx->timeout_flag) { |
| ble_gap_terminate(tx_stress_ctx->conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| return 0; |
| } |
| |
| tx_stress_ctx->rcv_data_flag = true; |
| |
| stress_l2cap_coc_recv(event->receive.chan, event->receive.sdu_rx); |
| |
| /* Time of data sending */ |
| us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us; |
| MODLOG_DFLT(INFO, "Time of receiving L2CAP data: %ld \n", |
| tx_stress_ctx->end_us); |
| |
| /* Remember size of entire mbuf chain */ |
| tx_stress_ctx->rcv_data_bytes = OS_MBUF_PKTLEN( |
| event->receive.sdu_rx); |
| MODLOG_DFLT(INFO, "Num of received bytes: %lld\n", |
| tx_stress_ctx->rcv_data_bytes); |
| |
| /* Calculate the bit rate of this send */ |
| tx_stress_ctx->s10_bit_rate = |
| stress_calc_bit_rate(us, tx_stress_ctx->rcv_data_bytes); |
| MODLOG_DFLT(INFO, "Bit rate: %d B/s\n", tx_stress_ctx->s10_bit_rate); |
| |
| /* Remember the sum of bytes and the time to calculate the average |
| * bit rate. */ |
| tx_stress_ctx->bytes_sum += tx_stress_ctx->rcv_data_bytes; |
| tx_stress_ctx->time_sum += us; |
| |
| /* Remember max received MTU */ |
| if (tx_stress_ctx->s10_max_mtu < tx_stress_ctx->rcv_data_bytes) { |
| tx_stress_ctx->s10_max_mtu = tx_stress_ctx->rcv_data_bytes; |
| } |
| console_printf("\033[0;32m>\033[0m"); |
| MODLOG_DFLT(INFO, "Loop nr: %d\n", ++i); |
| |
| tx_stress_10_l2cap_send_req(); |
| return 0; |
| |
| case BLE_L2CAP_EVENT_COC_TX_UNSTALLED: |
| MODLOG_DFLT(INFO, "L2CAP event unstalled\n"); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other L2CAP event occurs; rc=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_10_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| int rc; |
| struct os_mbuf *sdu_rx; |
| |
| 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, "Success to connect to device; num: %d\n", |
| ++tx_stress_ctx->con_stat[10].num); |
| |
| sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0); |
| assert(sdu_rx != NULL); |
| |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| rc = ble_l2cap_connect(event->connect.conn_handle, TEST_PSM, |
| STRESS_COC_MTU, sdu_rx, |
| tx_stress_10_l2cap_event, NULL); |
| assert(rc == 0); |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| tx_stress_ctx->s10_bit_rate = 1000000 * tx_stress_ctx->bytes_sum / |
| tx_stress_ctx->time_sum; |
| |
| MODLOG_DFLT(INFO, "Average bit rate: %d B/s\n", |
| tx_stress_ctx->s10_bit_rate); |
| tx_stress_on_test_finish(10); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_11_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| int test; |
| |
| switch (event->type) { |
| case BLE_GAP_EVENT_EXT_DISC: |
| /* Looking for next instance of test 9 advert. */ |
| test = tx_stress_find_test(&event->ext_disc); |
| |
| /* To avoid messing by rest of test 9 events in queue, check if handle |
| * filled */ |
| if (test == 11 && tx_stress_ctx->conn_handle == 0xffff) { |
| tx_stress_ctx->conn_handle = 0; |
| ble_gap_disc_cancel(); |
| tx_stress_ctx->dev_addr = event->ext_disc.addr; |
| tx_stress_simple_connect(tx_stress_11_gap_event, |
| tx_stress_ctx->cur_test_id, NULL); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_CONNECT: |
| /* A new connection was established or a connection attempt failed. */ |
| if (event->connect.status == 0) { |
| ++tx_stress_ctx->con_stat[11].num; |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| break; |
| } |
| 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 (tx_stress_ctx->con_stat[11].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| tx_stress_on_test_finish(11); |
| return 0; |
| } |
| break; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| |
| tx_stress_ctx->conn_handle = 0xffff; |
| |
| /* Scan for next instance of the test. */ |
| tx_stress_simple_scan(tx_stress_11_gap_event, 750); |
| |
| return 0; |
| } |
| |
| static int |
| tx_stress_12_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) { |
| ++tx_stress_ctx->con_stat[12].num; |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| } else { |
| MODLOG_DFLT(INFO, "\033[0;31mError: connection attempt failed; " |
| "status=%d\033[0m\n", event->connect.status); |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISCONNECT: |
| MODLOG_DFLT(INFO, "Disconnect; reason=%d \n", |
| event->disconnect.reason); |
| /* Finish test after first disconnection */ |
| tx_stress_on_test_finish(12); |
| return 0; |
| |
| case BLE_GAP_EVENT_NOTIFY_RX: |
| /* Received indication */ |
| MODLOG_DFLT(INFO, "Notify RX event\n"); |
| console_printf("\033[0;32m>\033[0m"); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_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) { |
| ++tx_stress_ctx->con_stat[13].num; |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| } 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); |
| /* Finish test after disconnection */ |
| tx_stress_on_test_finish(13); |
| return 0; |
| |
| case BLE_GAP_EVENT_NOTIFY_RX: |
| MODLOG_DFLT(INFO, "Notify RX event\n"); |
| console_printf("\033[0;32m>\033[0m"); |
| ++tx_stress_ctx->rcv_num; |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_14_subs_cb(uint16_t conn_handle, const struct ble_gatt_error *error, |
| struct ble_gatt_attr *attr, void *arg) |
| { |
| struct os_mbuf *om; |
| bool *sub; |
| int rc; |
| |
| assert(error->status == 0); |
| |
| /* If the first subscription after finding cccd */ |
| if (arg == NULL) { |
| return 0; |
| } |
| |
| sub = (bool *)arg; |
| |
| /* Enable notifications */ |
| if (*sub == 0) { |
| *sub = true; |
| om = ble_hs_mbuf_from_flat((uint8_t[]) {0x01, 0x00}, 2); |
| |
| tx_stress_ctx->begin_us = tx_stress_ctx->end_us; |
| |
| rc = ble_gattc_write(tx_stress_ctx->conn_handle, |
| tx_stress_ctx->dsc_handle, om, |
| tx_stress_14_subs_cb, arg); |
| assert(rc == 0); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| tx_stress_14_disc_cccd_fn(struct stress_gatt_search_ctx *search_ctx) |
| { |
| int rc; |
| struct os_mbuf *om; |
| MODLOG_DFLT(INFO, "CCCD found\n"); |
| |
| /* Enable notifications */ |
| om = ble_hs_mbuf_from_flat((uint8_t[]) {0x01, 0x00}, 2); |
| tx_stress_ctx->begin_us = os_get_uptime_usec(); |
| tx_stress_ctx->dsc_handle = search_ctx->dsc_handle; |
| |
| rc = ble_gattc_write(tx_stress_ctx->conn_handle, |
| tx_stress_ctx->dsc_handle, om, |
| tx_stress_14_subs_cb, NULL); |
| assert(rc == 0); |
| } |
| |
| static int |
| tx_stress_14_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| int64_t us = 0; |
| struct os_mbuf *om; |
| int rc; |
| static bool subscribed = true; |
| |
| switch (event->type) { |
| case BLE_GAP_EVENT_CONNECT: |
| /* A new connection was established or a connection attempt failed. */ |
| if (event->connect.status == 0) { |
| ++tx_stress_ctx->con_stat[14].num; |
| MODLOG_DFLT(INFO, "Success to connect to device\n"); |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| |
| /* Find CCCD handle (with default UUID16 = 0x2902) */ |
| stress_find_dsc_handle(event->connect.conn_handle, |
| BLE_UUID16_DECLARE(STRESS_GATT_UUID), |
| BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID), |
| BLE_UUID16_DECLARE(0x2902), |
| &tx_stress_14_disc_cccd_fn); |
| } 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); |
| /* Calc average notifying time */ |
| if (tx_stress_ctx->rcv_num > 0) { |
| tx_stress_ctx->s14_notif_time = tx_stress_ctx->time_sum / |
| tx_stress_ctx->rcv_num; |
| } |
| MODLOG_DFLT(INFO, "Average notification time: %d\n", |
| tx_stress_ctx->s14_notif_time); |
| /* Finish test after first disconnection */ |
| tx_stress_on_test_finish(14); |
| return 0; |
| |
| case BLE_GAP_EVENT_NOTIFY_RX: |
| tx_stress_ctx->end_us = os_get_uptime_usec(); |
| MODLOG_DFLT(INFO, "Notify RX event\n"); |
| |
| /* Time of data sending */ |
| us = tx_stress_ctx->end_us - tx_stress_ctx->begin_us; |
| MODLOG_DFLT(INFO, "Notification time: %lld\n us", us); |
| |
| tx_stress_ctx->time_sum += us; |
| console_printf("\033[0;32m>\033[0m"); |
| |
| /* Perform use case specified number of times */ |
| if (++tx_stress_ctx->rcv_num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| rc = ble_gap_terminate(event->notify_rx.conn_handle, |
| BLE_ERR_REM_USER_CONN_TERM); |
| MODLOG_DFLT(INFO, "rc=%d\n", rc); |
| assert(rc == 0); |
| return 0; |
| } |
| |
| /* Disable notifications */ |
| subscribed = false; |
| om = ble_hs_mbuf_from_flat( |
| (uint8_t[]) {0x00, 0x00}, 2); |
| |
| rc = ble_gattc_write(tx_stress_ctx->conn_handle, |
| tx_stress_ctx->dsc_handle, om, |
| tx_stress_14_subs_cb, &subscribed); |
| assert(rc == 0); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| tx_stress_15_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, |
| struct ble_gatt_attr *attr, void *arg) |
| { |
| /* Disconnect */ |
| ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); |
| console_printf("\033[0;32m>\033[0m"); |
| return 0; |
| } |
| |
| static void |
| tx_stress_15_disc_chr_fn(struct stress_gatt_search_ctx *search_ctx) |
| { |
| int rc; |
| struct os_mbuf *om; |
| |
| /* Send some data */ |
| MODLOG_DFLT(INFO, "Write to chr\n"); |
| om = ble_hs_mbuf_from_flat(test_6_pattern, 20); |
| |
| rc = ble_gattc_write(tx_stress_ctx->conn_handle, |
| search_ctx->chr_start_handle, om, |
| tx_stress_15_write_cb, NULL); |
| assert(rc == 0); |
| } |
| |
| static int |
| tx_stress_15_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, "Success to connect to device; num: %d\n", |
| ++tx_stress_ctx->con_stat[15].num); |
| tx_stress_ctx->conn_handle = event->connect.conn_handle; |
| |
| /* Find characteristic handle */ |
| stress_find_chr_handle(event->connect.conn_handle, |
| BLE_UUID16_DECLARE(STRESS_GATT_UUID), |
| BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID), |
| &tx_stress_15_disc_chr_fn); |
| } 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: |
| /* Perform use case specified number of times */ |
| if (tx_stress_ctx->con_stat[15].num >= MYNEWT_VAL(BLE_STRESS_REPEAT)) { |
| tx_stress_on_test_finish(15); |
| return 0; |
| } |
| /* Reconnect */ |
| tx_stress_simple_connect(tx_stress_15_gap_event, 15, NULL); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static int |
| scan_for_test_gap_event(struct ble_gap_event *event, void *arg) |
| { |
| int use_case; |
| int rc; |
| |
| switch (event->type) { |
| case BLE_GAP_EVENT_EXT_DISC: |
| /* Check if caught advert contains known UUID128. The UUID128 |
| * contains the ID of test use case to be executed. */ |
| use_case = tx_stress_find_test(&event->ext_disc); |
| if (use_case > 0) { |
| rc = ble_gap_disc_cancel(); |
| tx_stress_ctx->dev_addr = event->ext_disc.addr; |
| |
| /* After discovery cancel there are still some events in queue. */ |
| if (rc == 0) { |
| tx_stress_use_case = use_case; |
| /* Add token to semaphore. Main task will start the test. */ |
| os_sem_release(&tx_stress_main_sem); |
| } |
| } |
| return 0; |
| |
| case BLE_GAP_EVENT_DISC_COMPLETE: |
| /* On timeout */ |
| tx_stress_ctx->scan_timeout = true; |
| console_printf("\033[1;36mDiscover complete\033[0m\n"); |
| os_sem_release(&tx_stress_main_sem); |
| return 0; |
| |
| default: |
| MODLOG_DFLT(INFO, "Other event occurs=%d\n", event->type); |
| return 0; |
| } |
| } |
| |
| static void |
| tx_stress_test_perform(int test_num) |
| { |
| /* Perform every test only once */ |
| // if (test_num <= 0 || tx_stress_ctx->completed[test_num] == true) { |
| // return; |
| // } |
| |
| tx_stress_ctx->cur_test_id = test_num; |
| tx_stress_ctx->completed[test_num] = false; |
| tx_stress_ctx->conn_handle = 0xffff; |
| |
| console_printf("\033[1;36mStart test num %d - ", test_num); |
| |
| /* Start test */ |
| switch (test_num) { |
| case 0: |
| return; |
| case 1: |
| console_printf("Stress Connect -> Connect Cancel\033[0m\n"); |
| tx_stress_1_test(); |
| break; |
| case 2: |
| console_printf("Stress Connect/Disconnect legacy\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_2_gap_event, 2, NULL); |
| break; |
| case 3: |
| console_printf("Stress Connect/Disconnect ext adv\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_3_gap_event, 3, NULL); |
| break; |
| case 4: |
| console_printf("Stress connection params update (TX)\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_4_gap_event, 4, NULL); |
| break; |
| case 5: |
| console_printf("Stress connection params update (RX)\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_5_gap_event, 5, NULL); |
| break; |
| case 6: |
| console_printf("Stress Scan\033[0m\n"); |
| tx_stress_6_perform(); |
| break; |
| case 7: |
| console_printf("Stress PHY Update (TX)\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_7_gap_event, 7, NULL); |
| break; |
| case 8: |
| console_printf("Stress PHY Update (RX)\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_8_gap_event, 8, NULL); |
| break; |
| case 9: |
| console_printf("Stress multi connection\033[0m\n"); |
| tx_stress_9_perform(); |
| break; |
| case 10: |
| console_printf("Stress L2CAP send\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_10_gap_event, 10, NULL); |
| break; |
| case 11: |
| console_printf("Stress Advertise/Connect/Disconnect\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_11_gap_event, 11, NULL); |
| break; |
| case 12: |
| console_printf("Stress GATT indication\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_12_gap_event, 12, NULL); |
| break; |
| case 13: |
| console_printf("Stress GATT notification\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_13_gap_event, 13, NULL); |
| break; |
| case 14: |
| console_printf("Stress GATT Subscribe/Notify/Unsubscribe\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_14_gap_event, 14, NULL); |
| break; |
| case 15: |
| console_printf("Stress Connect/Send/Disconnect\033[0m\n"); |
| tx_stress_simple_connect(&tx_stress_15_gap_event, 15, NULL); |
| 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(&tx_stress_main_sem, OS_TIMEOUT_NEVER); |
| |
| stress_clear_ctx_reusable_var(tx_stress_ctx); |
| } |
| |
| static void |
| tx_stress_read_command_cb(void) |
| { |
| console_printf("Start testing\n"); |
| os_sem_release(&tx_stress_main_sem); |
| } |
| |
| static void |
| tx_stress_main_task_fn(void *arg) |
| { |
| int rc; |
| |
| tx_stress_ctx = &tx_stress_ctxD; |
| |
| console_printf("\033[1;36mTX device\033[0m\n"); |
| console_printf("Press ENTER to start: \n"); |
| console_init(&tx_stress_read_command_cb); |
| |
| /* Waite for pressing ENTER in console */ |
| os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); |
| |
| /* Init semaphore with 0 tokens. */ |
| rc = os_sem_init(&tx_stress_main_sem, 0); |
| assert(rc == 0); |
| |
| /* Start test 1 - Connect/Connect cancel */ |
| //tx_stress_test_perform(1); |
| |
| while (1) { |
| console_printf("\033[0;36mStart scan for test\033[0m\n"); |
| |
| /* Scan for known UUID128 of one of the stress tests. */ |
| tx_stress_simple_scan(scan_for_test_gap_event, 2000); |
| |
| /* Wait for the scan to find the test. Then 1 token will be |
| * released allowing to pass through semaphore. */ |
| os_sem_pend(&tx_stress_main_sem, OS_TIMEOUT_NEVER); |
| if (tx_stress_ctx->scan_timeout) { |
| break; |
| } |
| |
| /* Start test. */ |
| tx_stress_test_perform(tx_stress_use_case); |
| tx_stress_use_case = -1; |
| } |
| |
| /* Print tests results */ |
| com_stress_print_report(tx_stress_ctx); |
| |
| /* Task should never return */ |
| while (1) { |
| /* Delay used only to prevent watchdog to reset the device. */ |
| os_time_delay(os_time_ms_to_ticks32(2000)); |
| } |
| } |
| |
| void tx_stress_start_auto() |
| { |
| /* Start task that will run all stress tests one by one. */ |
| os_task_init(&tx_stress_main_task, "tx_stress_main_task", |
| tx_stress_main_task_fn, NULL, TX_STRESS_MAIN_TASK_PRIO, |
| OS_WAIT_FOREVER, tx_stress_main_task_stack, |
| TX_STRESS_MAIN_TASK_STACK_SIZE); |
| } |