/* * 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 #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; }