blob: 7d79a10d5b06dae82f578307cc1f55cbb09393a3 [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 <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;
}