| /* |
| * 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 <string.h> |
| #include "os/os.h" |
| #include "testutil/testutil.h" |
| #include "nimble/hci_common.h" |
| #include "nimble/ble_hci_trans.h" |
| #include "host/ble_hs_test.h" |
| #include "host/ble_gap.h" |
| #include "ble_hs_test_util.h" |
| |
| #define BLE_OS_TEST_STACK_SIZE 256 |
| #define BLE_OS_TEST_APP_STACK_SIZE 256 |
| |
| #define BLE_OS_TEST_APP_PRIO 9 |
| #define BLE_OS_TEST_TASK_PRIO 10 |
| |
| static struct os_task ble_os_test_task; |
| static struct os_task ble_os_test_app_task; |
| static os_stack_t ble_os_test_stack[OS_STACK_ALIGN(BLE_OS_TEST_STACK_SIZE)]; |
| |
| static os_stack_t |
| ble_os_test_app_stack[OS_STACK_ALIGN(BLE_OS_TEST_APP_STACK_SIZE)]; |
| |
| static uint8_t ble_os_test_peer_addr[6] = { 1, 2, 3, 4, 5, 6 }; |
| |
| static void ble_os_test_app_task_handler(void *arg); |
| |
| static int ble_os_test_gap_event_type; |
| |
| static void |
| ble_os_test_init_app_task(void) |
| { |
| int rc; |
| |
| rc = os_task_init(&ble_os_test_app_task, |
| "ble_gap_terminate_test_task", |
| ble_os_test_app_task_handler, NULL, |
| BLE_OS_TEST_APP_PRIO, OS_WAIT_FOREVER, |
| ble_os_test_app_stack, |
| OS_STACK_ALIGN(BLE_OS_TEST_APP_STACK_SIZE)); |
| TEST_ASSERT_FATAL(rc == 0); |
| } |
| |
| static void |
| ble_os_test_misc_init(void) |
| { |
| extern os_time_t g_os_time; |
| |
| ble_hs_test_util_init_no_start(); |
| |
| /* Allow the OS to approach tick rollover. This will help ensure host |
| * timers don't break when the tick counter resets. |
| */ |
| g_os_time = UINT32_MAX - 10 * OS_TICKS_PER_SEC; |
| |
| /* Receive acknowledgements for the startup sequence. We sent the |
| * corresponding requests when the host task was started. |
| */ |
| ble_hs_test_util_hci_ack_set_startup(); |
| |
| ble_os_test_init_app_task(); |
| } |
| |
| static int |
| ble_os_test_misc_conn_exists(uint16_t conn_handle) |
| { |
| struct ble_hs_conn *conn; |
| |
| ble_hs_lock(); |
| |
| if (conn_handle == BLE_HS_CONN_HANDLE_NONE) { |
| conn = ble_hs_conn_first(); |
| } else { |
| conn = ble_hs_conn_find(conn_handle); |
| } |
| |
| ble_hs_unlock(); |
| |
| return conn != NULL; |
| } |
| |
| static int |
| ble_gap_direct_connect_test_connect_cb(struct ble_gap_event *event, void *arg) |
| { |
| struct ble_gap_conn_desc desc; |
| int *cb_called; |
| int rc; |
| |
| cb_called = arg; |
| *cb_called = 1; |
| |
| TEST_ASSERT(event->type == BLE_GAP_EVENT_CONNECT); |
| TEST_ASSERT(event->connect.status == 0); |
| TEST_ASSERT(event->connect.conn_handle == 2); |
| |
| rc = ble_gap_conn_find(event->connect.conn_handle, &desc); |
| TEST_ASSERT_FATAL(rc == 0); |
| TEST_ASSERT(desc.peer_id_addr.type == BLE_ADDR_PUBLIC); |
| TEST_ASSERT(memcmp(desc.peer_id_addr.val, ble_os_test_peer_addr, 6) == 0); |
| |
| return 0; |
| } |
| |
| static void |
| ble_gap_direct_connect_test_task_handler(void *arg) |
| { |
| struct hci_le_conn_complete evt; |
| ble_addr_t addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; |
| int cb_called; |
| int rc; |
| |
| /* Set the connect callback so we can verify that it gets called with the |
| * proper arguments. |
| */ |
| cb_called = 0; |
| |
| /* Make sure there are no created connections and no connections in |
| * progress. |
| */ |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| |
| /* Initiate a direct connection. */ |
| ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, &addr, 0, NULL, |
| ble_gap_direct_connect_test_connect_cb, |
| &cb_called, 0); |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| TEST_ASSERT(!cb_called); |
| |
| /* ble_gap_rx_conn_complete() will send extra HCI command, need phony ack */ |
| ble_hs_test_util_hci_ack_set(ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_RD_REM_FEAT), 0); |
| |
| /* Receive an HCI connection-complete event. */ |
| memset(&evt, 0, sizeof evt); |
| evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE; |
| evt.status = BLE_ERR_SUCCESS; |
| evt.connection_handle = 2; |
| memcpy(evt.peer_addr, addr.val, 6); |
| rc = ble_gap_rx_conn_complete(&evt, 0); |
| TEST_ASSERT(rc == 0); |
| |
| /* The connection should now be created. */ |
| TEST_ASSERT(ble_os_test_misc_conn_exists(2)); |
| TEST_ASSERT(cb_called); |
| |
| tu_restart(); |
| } |
| |
| TEST_CASE(ble_gap_direct_connect_test_case) |
| { |
| ble_os_test_misc_init(); |
| |
| os_task_init(&ble_os_test_task, |
| "ble_gap_direct_connect_test_task", |
| ble_gap_direct_connect_test_task_handler, NULL, |
| BLE_OS_TEST_TASK_PRIO, OS_WAIT_FOREVER, ble_os_test_stack, |
| OS_STACK_ALIGN(BLE_OS_TEST_STACK_SIZE)); |
| |
| os_start(); |
| } |
| |
| static int |
| ble_os_disc_test_cb(struct ble_gap_event *event, void *arg) |
| { |
| int *cb_called; |
| |
| cb_called = arg; |
| *cb_called = 1; |
| |
| TEST_ASSERT(event->type == BLE_GAP_EVENT_DISC_COMPLETE); |
| |
| return 0; |
| } |
| |
| static void |
| ble_os_disc_test_task_handler(void *arg) |
| { |
| struct ble_gap_disc_params disc_params; |
| int cb_called; |
| int rc; |
| |
| /* Receive acknowledgements for the startup sequence. We sent the |
| * corresponding requests when the host task was started. |
| */ |
| ble_hs_test_util_hci_ack_set_startup(); |
| |
| /* Set the connect callback so we can verify that it gets called with the |
| * proper arguments. |
| */ |
| cb_called = 0; |
| |
| os_time_delay(10); |
| |
| /* Make sure there are no created connections and no connections in |
| * progress. |
| */ |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| TEST_ASSERT(!ble_gap_master_in_progress()); |
| |
| /* Initiate the general discovery procedure with a 300 ms timeout. */ |
| memset(&disc_params, 0, sizeof disc_params); |
| rc = ble_hs_test_util_disc(BLE_OWN_ADDR_PUBLIC, 300, &disc_params, |
| ble_os_disc_test_cb, |
| &cb_called, 0, 0); |
| TEST_ASSERT(rc == 0); |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| TEST_ASSERT(ble_gap_master_in_progress()); |
| TEST_ASSERT(!cb_called); |
| |
| /* Receive acks from the controller. */ |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| TEST_ASSERT(ble_gap_master_in_progress()); |
| TEST_ASSERT(!cb_called); |
| |
| /* Wait 100 ms; verify scan still in progress. */ |
| os_time_delay(100 * OS_TICKS_PER_SEC / 1000); |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| TEST_ASSERT(ble_gap_master_in_progress()); |
| TEST_ASSERT(!cb_called); |
| |
| ble_hs_test_util_hci_ack_set( |
| ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_SET_SCAN_ENABLE), |
| 0); |
| |
| /* Wait 250 more ms; verify scan completed. */ |
| os_time_delay(250 * OS_TICKS_PER_SEC / 1000); |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| TEST_ASSERT(!ble_gap_master_in_progress()); |
| TEST_ASSERT(cb_called); |
| |
| tu_restart(); |
| } |
| |
| TEST_CASE(ble_os_disc_test_case) |
| { |
| ble_os_test_misc_init(); |
| |
| os_task_init(&ble_os_test_task, |
| "ble_os_disc_test_task", |
| ble_os_disc_test_task_handler, NULL, |
| BLE_OS_TEST_TASK_PRIO, OS_WAIT_FOREVER, ble_os_test_stack, |
| OS_STACK_ALIGN(BLE_OS_TEST_STACK_SIZE)); |
| |
| os_start(); |
| } |
| |
| static int |
| ble_gap_terminate_cb(struct ble_gap_event *event, void *arg) |
| { |
| int *disconn_handle; |
| |
| ble_os_test_gap_event_type = event->type; |
| |
| if (event->type == BLE_GAP_EVENT_DISCONNECT) { |
| disconn_handle = arg; |
| *disconn_handle = event->disconnect.conn.conn_handle; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| ble_gap_terminate_test_task_handler(void *arg) |
| { |
| struct hci_disconn_complete disconn_evt; |
| struct hci_le_conn_complete conn_evt; |
| ble_addr_t addr1 = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 }}; |
| ble_addr_t addr2 = { BLE_ADDR_PUBLIC, { 2, 3, 4, 5, 6, 7 }}; |
| int disconn_handle; |
| int rc; |
| |
| /* Receive acknowledgements for the startup sequence. We sent the |
| * corresponding requests when the host task was started. |
| */ |
| ble_hs_test_util_hci_ack_set_startup(); |
| |
| /* Set the connect callback so we can verify that it gets called with the |
| * proper arguments. |
| */ |
| disconn_handle = 0; |
| |
| /* Make sure there are no created connections and no connections in |
| * progress. |
| */ |
| TEST_ASSERT(!ble_os_test_misc_conn_exists(BLE_HS_CONN_HANDLE_NONE)); |
| TEST_ASSERT(!ble_gap_master_in_progress()); |
| |
| /* Create two direct connections. */ |
| ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, |
| &addr1, 0, NULL, ble_gap_terminate_cb, |
| &disconn_handle, 0); |
| /* ble_gap_rx_conn_complete() will send extra HCI command, need phony ack */ |
| ble_hs_test_util_hci_ack_set(ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_RD_REM_FEAT), 0); |
| memset(&conn_evt, 0, sizeof conn_evt); |
| conn_evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE; |
| conn_evt.status = BLE_ERR_SUCCESS; |
| conn_evt.connection_handle = 1; |
| memcpy(conn_evt.peer_addr, addr1.val, 6); |
| rc = ble_gap_rx_conn_complete(&conn_evt, 0); |
| TEST_ASSERT(rc == 0); |
| |
| ble_hs_test_util_connect(BLE_OWN_ADDR_PUBLIC, |
| &addr2, 0, NULL, ble_gap_terminate_cb, |
| &disconn_handle, 0); |
| /* ble_gap_rx_conn_complete() will send extra HCI command, need phony ack */ |
| ble_hs_test_util_hci_ack_set(ble_hs_hci_util_opcode_join(BLE_HCI_OGF_LE, |
| BLE_HCI_OCF_LE_RD_REM_FEAT), 0); |
| memset(&conn_evt, 0, sizeof conn_evt); |
| conn_evt.subevent_code = BLE_HCI_LE_SUBEV_CONN_COMPLETE; |
| conn_evt.status = BLE_ERR_SUCCESS; |
| conn_evt.connection_handle = 2; |
| memcpy(conn_evt.peer_addr, addr2.val, 6); |
| rc = ble_gap_rx_conn_complete(&conn_evt, 0); |
| TEST_ASSERT(rc == 0); |
| |
| TEST_ASSERT_FATAL(ble_os_test_misc_conn_exists(1)); |
| TEST_ASSERT_FATAL(ble_os_test_misc_conn_exists(2)); |
| |
| /* Terminate the first one. */ |
| rc = ble_hs_test_util_conn_terminate(1, 0); |
| TEST_ASSERT(rc == 0); |
| disconn_evt.connection_handle = 1; |
| disconn_evt.status = 0; |
| disconn_evt.reason = BLE_ERR_REM_USER_CONN_TERM; |
| ble_hs_test_util_hci_rx_disconn_complete_event(&disconn_evt); |
| TEST_ASSERT(ble_os_test_gap_event_type == BLE_GAP_EVENT_DISCONNECT); |
| TEST_ASSERT(disconn_handle == 1); |
| TEST_ASSERT_FATAL(!ble_os_test_misc_conn_exists(1)); |
| TEST_ASSERT_FATAL(ble_os_test_misc_conn_exists(2)); |
| |
| /* Terminate the second one. */ |
| rc = ble_hs_test_util_conn_terminate(2, 0); |
| TEST_ASSERT(rc == 0); |
| disconn_evt.connection_handle = 2; |
| disconn_evt.status = 0; |
| disconn_evt.reason = BLE_ERR_REM_USER_CONN_TERM; |
| ble_hs_test_util_hci_rx_disconn_complete_event(&disconn_evt); |
| TEST_ASSERT(ble_os_test_gap_event_type == BLE_GAP_EVENT_DISCONNECT); |
| TEST_ASSERT(disconn_handle == 2); |
| TEST_ASSERT_FATAL(!ble_os_test_misc_conn_exists(1)); |
| TEST_ASSERT_FATAL(!ble_os_test_misc_conn_exists(2)); |
| |
| tu_restart(); |
| } |
| |
| static void |
| ble_os_test_app_task_handler(void *arg) |
| { |
| while (1) { |
| os_eventq_run(os_eventq_dflt_get()); |
| } |
| } |
| |
| TEST_CASE(ble_gap_terminate_test_case) |
| { |
| ble_os_test_misc_init(); |
| |
| os_task_init(&ble_os_test_task, |
| "ble_gap_terminate_test_task", |
| ble_gap_terminate_test_task_handler, NULL, |
| BLE_OS_TEST_TASK_PRIO, OS_WAIT_FOREVER, ble_os_test_stack, |
| OS_STACK_ALIGN(BLE_OS_TEST_STACK_SIZE)); |
| |
| os_start(); |
| } |
| |
| TEST_SUITE(ble_os_test_suite) |
| { |
| tu_suite_set_post_test_cb(ble_hs_test_util_post_test, NULL); |
| |
| ble_os_disc_test_case(); |
| ble_gap_direct_connect_test_case(); |
| ble_gap_terminate_test_case(); |
| } |
| |
| int |
| ble_os_test_all(void) |
| { |
| ble_os_test_suite(); |
| return tu_any_failed; |
| } |