Bluetooth: CAP: Commander discovery support
Implement the CAP Commander discovery function. Adds support for it in the shell. This includes initial babblesim and unit testing as well. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
2876dcabbf
commit
cda5e58aa5
25 changed files with 917 additions and 12 deletions
|
@ -137,3 +137,40 @@ used.
|
|||
|
||||
uart:~$ cap_initiator unicast-stop
|
||||
Unicast stopped for group 0x81e41c0 completed
|
||||
|
||||
CAP Commander
|
||||
*************
|
||||
|
||||
The Commander will typically be a either co-located with a CAP Initiator or be on a separate
|
||||
resource-rich mobile device, such as a phone or smartwatch. The Commander can
|
||||
discover CAP Acceptors's CAS and optional CSIS services. The CSIS service can be read to provide
|
||||
information about other CAP Acceptors in the same Coordinated Set. The Commander can provide
|
||||
information about broadcast sources to CAP Acceptors or coordinate capture and rendering information
|
||||
such as mute or volume states.
|
||||
|
||||
Using the CAP Commander
|
||||
=======================
|
||||
|
||||
When the Bluetooth stack has been initialized (:code:`bt init`), the Commander can discover CAS and
|
||||
the optionally included CSIS instance by calling (:code:`cap_commander discover`).
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
cap_commander --help
|
||||
cap_commander - Bluetooth CAP commander shell commands
|
||||
Subcommands:
|
||||
discover :Discover CAS
|
||||
|
||||
Before being able to perform any stream operation, the device must also perform the
|
||||
:code:`bap discover` operation to discover the ASEs and PAC records. The :code:`bap init`
|
||||
command also needs to be called.
|
||||
|
||||
When connected
|
||||
--------------
|
||||
|
||||
Discovering CAS and CSIS on a device:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
uart:~$ cap_commander discover
|
||||
discovery completed with CSIS
|
||||
|
|
|
@ -131,7 +131,10 @@ struct bt_cap_initiator_cb {
|
|||
*
|
||||
* @param conn Connection to a remote server.
|
||||
*
|
||||
* @return 0 on success or negative error value on failure.
|
||||
* @retval 0 Success
|
||||
* @retval -EINVAL @p conn is NULL
|
||||
* @retval -ENOTCONN @p conn is not connected
|
||||
* @retval -ENOMEM Could not allocated memory for the request
|
||||
*/
|
||||
int bt_cap_initiator_unicast_discover(struct bt_conn *conn);
|
||||
|
||||
|
@ -250,7 +253,7 @@ struct bt_cap_unicast_audio_update_param {
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief Register Common Audio Profile callbacks
|
||||
* @brief Register Common Audio Profile Initiator callbacks
|
||||
*
|
||||
* @param cb The callback structure. Shall remain static.
|
||||
*
|
||||
|
@ -636,6 +639,45 @@ struct bt_cap_broadcast_to_unicast_param {
|
|||
int bt_cap_initiator_broadcast_to_unicast(const struct bt_cap_broadcast_to_unicast_param *param,
|
||||
struct bt_bap_unicast_group **unicast_group);
|
||||
|
||||
/** Callback structure for CAP procedures */
|
||||
struct bt_cap_commander_cb {
|
||||
/**
|
||||
* @brief Callback for bt_cap_initiator_unicast_discover().
|
||||
*
|
||||
* @param conn The connection pointer supplied to
|
||||
* bt_cap_initiator_unicast_discover().
|
||||
* @param err 0 if Common Audio Service was found else -ENODATA.
|
||||
* @param csis_inst The Coordinated Set Identification Service if
|
||||
* Common Audio Service was found and includes a
|
||||
* Coordinated Set Identification Service.
|
||||
* NULL on error or if remote device does not include
|
||||
* Coordinated Set Identification Service.
|
||||
*/
|
||||
void (*discovery_complete)(struct bt_conn *conn, int err,
|
||||
const struct bt_csip_set_coordinator_csis_inst *csis_inst);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Register Common Audio Profile Commander callbacks
|
||||
*
|
||||
* @param cb The callback structure. Shall remain static.
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -EINVAL @p cb is NULL
|
||||
* @retval -EALREADY Callbacks are already registered
|
||||
*/
|
||||
int bt_cap_commander_register_cb(const struct bt_cap_commander_cb *cb);
|
||||
|
||||
/**
|
||||
* @brief Unregister Common Audio Profile Commander callbacks
|
||||
*
|
||||
* @param cb The callback structure that was previously registered.
|
||||
*
|
||||
* @retval 0 Success
|
||||
* @retval -EINVAL @p cb is NULL or @p cb was not registered
|
||||
*/
|
||||
int bt_cap_commander_unregister_cb(const struct bt_cap_commander_cb *cb);
|
||||
|
||||
/**
|
||||
* @brief Discovers audio support on a remote device.
|
||||
*
|
||||
|
@ -644,13 +686,17 @@ int bt_cap_initiator_broadcast_to_unicast(const struct bt_cap_broadcast_to_unica
|
|||
*
|
||||
* @note @kconfig{CONFIG_BT_CAP_COMMANDER} must be enabled for this function. If
|
||||
* @kconfig{CONFIG_BT_CAP_INITIATOR} is also enabled, it does not matter if
|
||||
* bt_cap_commander_unicast_discover() or bt_cap_initiator_unicast_discover() is used.
|
||||
* bt_cap_commander_discover() or bt_cap_initiator_unicast_discover() is used.
|
||||
*
|
||||
* @param conn Connection to a remote server.
|
||||
*
|
||||
* @return 0 on success or negative error value on failure.
|
||||
* @retval 0 Success
|
||||
* @retval -EINVAL @p conn is NULL
|
||||
* @retval -ENOTCONN @p conn is not connected
|
||||
* @retval -ENOMEM Could not allocated memory for the request
|
||||
* @retval -EBUSY Already doing discovery for @p conn
|
||||
*/
|
||||
int bt_cap_commander_unicast_discover(struct bt_conn *conn);
|
||||
int bt_cap_commander_discover(struct bt_conn *conn);
|
||||
|
||||
struct bt_cap_commander_broadcast_reception_start_member_param {
|
||||
/** Coordinated or ad-hoc set member. */
|
||||
|
|
|
@ -20,9 +20,59 @@ LOG_MODULE_REGISTER(bt_cap_commander, CONFIG_BT_CAP_COMMANDER_LOG_LEVEL);
|
|||
|
||||
#include "common/bt_str.h"
|
||||
|
||||
int bt_cap_commander_unicast_discover(struct bt_conn *conn)
|
||||
static const struct bt_cap_commander_cb *cap_cb;
|
||||
|
||||
int bt_cap_commander_register_cb(const struct bt_cap_commander_cb *cb)
|
||||
{
|
||||
return -ENOSYS;
|
||||
CHECKIF(cb == NULL) {
|
||||
LOG_DBG("cb is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
CHECKIF(cap_cb != NULL) {
|
||||
LOG_DBG("callbacks already registered");
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
cap_cb = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_cap_commander_unregister_cb(const struct bt_cap_commander_cb *cb)
|
||||
{
|
||||
CHECKIF(cb == NULL) {
|
||||
LOG_DBG("cb is NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
CHECKIF(cap_cb != cb) {
|
||||
LOG_DBG("cb is not registered");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cap_cb = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cap_commander_discover_complete(struct bt_conn *conn, int err,
|
||||
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
|
||||
{
|
||||
if (cap_cb && cap_cb->discovery_complete) {
|
||||
cap_cb->discovery_complete(conn, err, csis_inst);
|
||||
}
|
||||
}
|
||||
|
||||
int bt_cap_commander_discover(struct bt_conn *conn)
|
||||
{
|
||||
CHECKIF(conn == NULL) {
|
||||
LOG_DBG("NULL conn");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return bt_cap_common_discover(conn, cap_commander_discover_complete);
|
||||
}
|
||||
|
||||
int bt_cap_commander_broadcast_reception_start(
|
||||
|
|
|
@ -240,7 +240,6 @@ static uint8_t bt_cap_common_discover_included_cb(struct bt_conn *conn,
|
|||
client->csis_start_handle = included_service->start_handle;
|
||||
client->csis_inst = bt_csip_set_coordinator_csis_inst_by_handle(
|
||||
conn, client->csis_start_handle);
|
||||
|
||||
if (client->csis_inst == NULL) {
|
||||
static struct bt_csip_set_coordinator_cb csis_client_cb = {
|
||||
.discover = csis_client_discover_cb,
|
||||
|
@ -325,10 +324,20 @@ int bt_cap_common_discover(struct bt_conn *conn, bt_cap_common_discover_func_t f
|
|||
param->start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE;
|
||||
param->end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
|
||||
discover_cb_func = func;
|
||||
|
||||
err = bt_gatt_discover(conn, param);
|
||||
if (err == 0) {
|
||||
discover_cb_func = func;
|
||||
if (err != 0) {
|
||||
discover_cb_func = NULL;
|
||||
|
||||
/* Report expected possible errors */
|
||||
if (err == -ENOTCONN || err == -ENOMEM) {
|
||||
return err;
|
||||
}
|
||||
|
||||
LOG_DBG("Unexpected err %d from bt_gatt_discover", err);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_CCID_H_
|
||||
#define ZEPHYR_INCLUDE_BLUETOOTH_CCID_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,6 +59,10 @@ zephyr_library_sources_ifdef(
|
|||
CONFIG_BT_CAP_INITIATOR
|
||||
cap_initiator.c
|
||||
)
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_BT_CAP_COMMANDER
|
||||
cap_commander.c
|
||||
)
|
||||
zephyr_library_sources_ifdef(
|
||||
CONFIG_BT_HAS_CLIENT
|
||||
has_client.c
|
||||
|
|
79
subsys/bluetooth/audio/shell/cap_commander.c
Normal file
79
subsys/bluetooth/audio/shell/cap_commander.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* @file
|
||||
* @brief Shell APIs for Bluetooth CAP commander
|
||||
*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
#include "shell/bt.h"
|
||||
#include "audio.h"
|
||||
|
||||
static void cap_discover_cb(struct bt_conn *conn, int err,
|
||||
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
|
||||
{
|
||||
if (err != 0) {
|
||||
shell_error(ctx_shell, "discover failed (%d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
shell_print(ctx_shell, "discovery completed%s", csis_inst == NULL ? "" : " with CSIS");
|
||||
}
|
||||
|
||||
static struct bt_cap_commander_cb cbs = {
|
||||
.discovery_complete = cap_discover_cb,
|
||||
};
|
||||
|
||||
static int cmd_cap_commander_discover(const struct shell *sh, size_t argc, char *argv[])
|
||||
{
|
||||
static bool cbs_registered;
|
||||
int err;
|
||||
|
||||
if (default_conn == NULL) {
|
||||
shell_error(sh, "Not connected");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
if (ctx_shell == NULL) {
|
||||
ctx_shell = sh;
|
||||
}
|
||||
|
||||
if (!cbs_registered) {
|
||||
bt_cap_commander_register_cb(&cbs);
|
||||
cbs_registered = true;
|
||||
}
|
||||
|
||||
err = bt_cap_commander_discover(default_conn);
|
||||
if (err != 0) {
|
||||
shell_error(sh, "Fail: %d", err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cmd_cap_commander(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
if (argc > 1) {
|
||||
shell_error(sh, "%s unknown parameter: %s", argv[0], argv[1]);
|
||||
} else {
|
||||
shell_error(sh, "%s Missing subcommand", argv[0]);
|
||||
}
|
||||
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(
|
||||
cap_commander_cmds,
|
||||
SHELL_CMD_ARG(discover, NULL, "Discover CAS", cmd_cap_commander_discover, 1, 0),
|
||||
SHELL_SUBCMD_SET_END
|
||||
);
|
||||
|
||||
SHELL_CMD_ARG_REGISTER(cap_commander, &cap_commander_cmds, "Bluetooth CAP commander shell commands",
|
||||
cmd_cap_commander, 1, 1);
|
18
tests/bluetooth/audio/cap_commander/CMakeLists.txt
Normal file
18
tests/bluetooth/audio/cap_commander/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
cmake_minimum_required(VERSION 3.20.0)
|
||||
|
||||
project(bluetooth_ascs)
|
||||
find_package(Zephyr COMPONENTS unittest HINTS $ENV{ZEPHYR_BASE})
|
||||
|
||||
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/cap_commander/uut uut)
|
||||
|
||||
target_link_libraries(testbinary PRIVATE uut)
|
||||
|
||||
target_include_directories(testbinary PRIVATE include)
|
||||
|
||||
target_sources(testbinary
|
||||
PRIVATE
|
||||
${ZEPHYR_BASE}/subsys/bluetooth/host/uuid.c
|
||||
src/main.c
|
||||
)
|
18
tests/bluetooth/audio/cap_commander/prj.conf
Normal file
18
tests/bluetooth/audio/cap_commander/prj.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
CONFIG_ZTEST=y
|
||||
|
||||
CONFIG_BT=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_AUDIO=y
|
||||
|
||||
# Requirements for CAP commander
|
||||
CONFIG_BT_VCP_VOL_CTLR=y
|
||||
CONFIG_BT_CSIP_SET_COORDINATOR=y
|
||||
|
||||
CONFIG_BT_CAP_COMMANDER=y
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_BT_CAP_COMMANDER_LOG_LEVEL_DBG=y
|
||||
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_ASSERT_LEVEL=2
|
||||
CONFIG_ASSERT_VERBOSE=y
|
166
tests/bluetooth/audio/cap_commander/src/main.c
Normal file
166
tests/bluetooth/audio/cap_commander/src/main.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/* main.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
#include <zephyr/fff.h>
|
||||
|
||||
#include "bluetooth.h"
|
||||
#include "cap_commander.h"
|
||||
#include "conn.h"
|
||||
#include "expects_util.h"
|
||||
|
||||
DEFINE_FFF_GLOBALS;
|
||||
|
||||
static void mock_init_rule_before(const struct ztest_unit_test *test, void *fixture)
|
||||
{
|
||||
mock_cap_commander_init();
|
||||
}
|
||||
|
||||
static void mock_destroy_rule_after(const struct ztest_unit_test *test, void *fixture)
|
||||
{
|
||||
mock_cap_commander_cleanup();
|
||||
}
|
||||
|
||||
ZTEST_RULE(mock_rule, mock_init_rule_before, mock_destroy_rule_after);
|
||||
|
||||
struct cap_commander_test_suite_fixture {
|
||||
struct bt_conn conn;
|
||||
};
|
||||
|
||||
static void test_conn_init(struct bt_conn *conn)
|
||||
{
|
||||
conn->index = 0;
|
||||
conn->info.type = BT_CONN_TYPE_LE;
|
||||
conn->info.role = BT_CONN_ROLE_PERIPHERAL;
|
||||
conn->info.state = BT_CONN_STATE_CONNECTED;
|
||||
conn->info.security.level = BT_SECURITY_L2;
|
||||
conn->info.security.enc_key_size = BT_ENC_KEY_SIZE_MAX;
|
||||
conn->info.security.flags = BT_SECURITY_FLAG_OOB | BT_SECURITY_FLAG_SC;
|
||||
}
|
||||
|
||||
static void cap_commander_test_suite_fixture_init(struct cap_commander_test_suite_fixture *fixture)
|
||||
{
|
||||
test_conn_init(&fixture->conn);
|
||||
}
|
||||
|
||||
static void *cap_commander_test_suite_setup(void)
|
||||
{
|
||||
struct cap_commander_test_suite_fixture *fixture;
|
||||
|
||||
fixture = malloc(sizeof(*fixture));
|
||||
zassert_not_null(fixture);
|
||||
|
||||
return fixture;
|
||||
}
|
||||
|
||||
static void cap_commander_test_suite_before(void *f)
|
||||
{
|
||||
memset(f, 0, sizeof(struct cap_commander_test_suite_fixture));
|
||||
cap_commander_test_suite_fixture_init(f);
|
||||
}
|
||||
|
||||
static void cap_commander_test_suite_after(void *f)
|
||||
{
|
||||
bt_cap_commander_unregister_cb(&mock_cap_commander_cb);
|
||||
}
|
||||
|
||||
static void cap_commander_test_suite_teardown(void *f)
|
||||
{
|
||||
free(f);
|
||||
}
|
||||
|
||||
ZTEST_SUITE(cap_commander_test_suite, NULL, cap_commander_test_suite_setup,
|
||||
cap_commander_test_suite_before, cap_commander_test_suite_after,
|
||||
cap_commander_test_suite_teardown);
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_register_cb)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
}
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_register_cb_inval_param_null)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_register_cb(NULL);
|
||||
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
|
||||
}
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_register_cb_inval_double_register)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
|
||||
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(-EALREADY, err, "Unexpected return value %d", err);
|
||||
}
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_unregister_cb)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
|
||||
err = bt_cap_commander_unregister_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
}
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_unregister_cb_inval_param_null)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_unregister_cb(NULL);
|
||||
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
|
||||
}
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_unregister_cb_inval_double_unregister)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
|
||||
err = bt_cap_commander_unregister_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
|
||||
err = bt_cap_commander_unregister_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
|
||||
}
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_discover)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
|
||||
err = bt_cap_commander_discover(&fixture->conn);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
|
||||
zexpect_call_count("bt_cap_commander_cb.discovery_complete", 1,
|
||||
mock_cap_commander_discovery_complete_cb_fake.call_count);
|
||||
}
|
||||
|
||||
ZTEST_F(cap_commander_test_suite, test_commander_discover_inval_param_null)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_cap_commander_register_cb(&mock_cap_commander_cb);
|
||||
zassert_equal(0, err, "Unexpected return value %d", err);
|
||||
|
||||
err = bt_cap_commander_discover(NULL);
|
||||
zassert_equal(-EINVAL, err, "Unexpected return value %d", err);
|
||||
}
|
7
tests/bluetooth/audio/cap_commander/testcase.yaml
Normal file
7
tests/bluetooth/audio/cap_commander/testcase.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
common:
|
||||
tags:
|
||||
- bluetooth
|
||||
- bluetooth_audio
|
||||
tests:
|
||||
bluetooth.audio.cap_commander.test_default:
|
||||
type: unit
|
21
tests/bluetooth/audio/cap_commander/uut/CMakeLists.txt
Normal file
21
tests/bluetooth/audio/cap_commander/uut/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# CMakeLists.txt file for creating of uut library.
|
||||
#
|
||||
|
||||
add_library(uut STATIC
|
||||
${ZEPHYR_BASE}/subsys/bluetooth/audio/cap_commander.c
|
||||
${ZEPHYR_BASE}/subsys/bluetooth/audio/cap_common.c
|
||||
${ZEPHYR_BASE}/subsys/logging/log_minimal.c
|
||||
${ZEPHYR_BASE}/subsys/net/buf_simple.c
|
||||
csip.c
|
||||
)
|
||||
|
||||
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/audio/mocks mocks)
|
||||
|
||||
target_link_libraries(uut PUBLIC test_interface mocks)
|
||||
|
||||
target_compile_options(uut PRIVATE -std=c11 -include ztest.h)
|
62
tests/bluetooth/audio/cap_commander/uut/csip.c
Normal file
62
tests/bluetooth/audio/cap_commander/uut/csip.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
/* csip.c - CAP Commander specific CSIP mocks */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/audio/csip.h>
|
||||
|
||||
static struct bt_csip_set_coordinator_cb *csip_cb;
|
||||
|
||||
struct bt_csip_set_coordinator_svc_inst {
|
||||
struct bt_conn *conn;
|
||||
struct bt_csip_set_coordinator_set_info *set_info;
|
||||
} svc_inst;
|
||||
|
||||
static struct bt_csip_set_coordinator_set_member member = {
|
||||
.insts = {
|
||||
{
|
||||
.info = {
|
||||
.set_size = 2,
|
||||
.rank = 1,
|
||||
.lockable = false,
|
||||
},
|
||||
.svc_inst = &svc_inst,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
struct bt_csip_set_coordinator_csis_inst *
|
||||
bt_csip_set_coordinator_csis_inst_by_handle(struct bt_conn *conn, uint16_t start_handle)
|
||||
{
|
||||
return &member.insts[0];
|
||||
}
|
||||
|
||||
int bt_csip_set_coordinator_register_cb(struct bt_csip_set_coordinator_cb *cb)
|
||||
{
|
||||
csip_cb = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bt_csip_set_coordinator_discover(struct bt_conn *conn)
|
||||
{
|
||||
if (csip_cb != NULL) {
|
||||
svc_inst.conn = conn;
|
||||
svc_inst.set_info = &member.insts[0].info;
|
||||
csip_cb->discover(conn, &member, 0, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mock_bt_csip_init(void)
|
||||
{
|
||||
csip_cb = NULL;
|
||||
}
|
||||
|
||||
void mock_bt_csip_cleanup(void)
|
||||
{
|
||||
}
|
|
@ -10,6 +10,7 @@ add_library(mocks STATIC
|
|||
src/bap_stream.c
|
||||
src/bap_unicast_client.c
|
||||
src/bap_unicast_server.c
|
||||
src/cap_commander.c
|
||||
src/conn.c
|
||||
src/crypto.c
|
||||
src/fatal.c
|
||||
|
|
21
tests/bluetooth/audio/mocks/include/cap_commander.h
Normal file
21
tests/bluetooth/audio/mocks/include/cap_commander.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef MOCKS_CAP_COMMANDER_H_
|
||||
#define MOCKS_CAP_COMMANDER_H_
|
||||
|
||||
#include <zephyr/fff.h>
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
extern struct bt_cap_commander_cb mock_cap_commander_cb;
|
||||
|
||||
void mock_cap_commander_init(void);
|
||||
void mock_cap_commander_cleanup(void);
|
||||
|
||||
DECLARE_FAKE_VOID_FUNC(mock_cap_commander_discovery_complete_cb, struct bt_conn *, int,
|
||||
const struct bt_csip_set_coordinator_csis_inst *);
|
||||
|
||||
#endif /* MOCKS_CAP_COMMANDER_H_ */
|
28
tests/bluetooth/audio/mocks/src/cap_commander.c
Normal file
28
tests/bluetooth/audio/mocks/src/cap_commander.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
|
||||
#include "cap_commander.h"
|
||||
|
||||
/* List of fakes used by this unit tester */
|
||||
#define FFF_FAKES_LIST(FAKE) FAKE(mock_cap_commander_discovery_complete_cb)
|
||||
|
||||
struct bt_cap_commander_cb mock_cap_commander_cb;
|
||||
|
||||
DEFINE_FAKE_VOID_FUNC(mock_cap_commander_discovery_complete_cb, struct bt_conn *, int,
|
||||
const struct bt_csip_set_coordinator_csis_inst *);
|
||||
|
||||
void mock_cap_commander_init(void)
|
||||
{
|
||||
FFF_FAKES_LIST(RESET_FAKE);
|
||||
|
||||
mock_cap_commander_cb.discovery_complete = mock_cap_commander_discovery_complete_cb;
|
||||
}
|
||||
|
||||
void mock_cap_commander_cleanup(void)
|
||||
{
|
||||
}
|
|
@ -7,9 +7,12 @@
|
|||
#include <stdlib.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/bluetooth/att.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/sys/iterable_sections.h>
|
||||
|
||||
#include "gatt.h"
|
||||
#include "conn.h"
|
||||
|
||||
#define LOG_LEVEL CONFIG_BT_GATT_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
|
@ -283,6 +286,63 @@ ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
|||
return len;
|
||||
}
|
||||
|
||||
int bt_gatt_discover(struct bt_conn *conn, struct bt_gatt_discover_params *params)
|
||||
{
|
||||
zassert_not_null(conn, "'%s()' was called with incorrect '%s' value", __func__, "conn");
|
||||
zassert_not_null(params, "'%s()' was called with incorrect '%s' value", __func__, "params");
|
||||
zassert_not_null(params->func, "'%s()' was called with incorrect '%s' value", __func__,
|
||||
"params->func");
|
||||
zassert_between_inclusive(
|
||||
params->start_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE,
|
||||
"'%s()' was called with incorrect '%s' value", __func__, "params->start_handle");
|
||||
zassert_between_inclusive(
|
||||
params->end_handle, BT_ATT_FIRST_ATTRIBUTE_HANDLE, BT_ATT_LAST_ATTRIBUTE_HANDLE,
|
||||
"'%s()' was called with incorrect '%s' value", __func__, "params->end_handle");
|
||||
zassert_true(params->start_handle <= params->end_handle,
|
||||
"'%s()' was called with incorrect '%s' value", __func__, "params->end_handle");
|
||||
|
||||
struct bt_gatt_service_val value;
|
||||
struct bt_uuid_16 uuid;
|
||||
struct bt_gatt_attr attr;
|
||||
uint16_t start_handle;
|
||||
uint16_t end_handle;
|
||||
|
||||
if (conn->info.state != BT_CONN_STATE_CONNECTED) {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
switch (params->type) {
|
||||
case BT_GATT_DISCOVER_PRIMARY:
|
||||
case BT_GATT_DISCOVER_SECONDARY:
|
||||
case BT_GATT_DISCOVER_STD_CHAR_DESC:
|
||||
case BT_GATT_DISCOVER_INCLUDE:
|
||||
case BT_GATT_DISCOVER_CHARACTERISTIC:
|
||||
case BT_GATT_DISCOVER_DESCRIPTOR:
|
||||
case BT_GATT_DISCOVER_ATTRIBUTE:
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("Invalid discovery type: %u", params->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uuid.uuid.type = BT_UUID_TYPE_16;
|
||||
uuid.val = params->type;
|
||||
start_handle = params->start_handle;
|
||||
end_handle = params->end_handle;
|
||||
value.end_handle = end_handle;
|
||||
value.uuid = params->uuid;
|
||||
|
||||
attr = (struct bt_gatt_attr){
|
||||
.uuid = &uuid.uuid,
|
||||
.user_data = &value,
|
||||
.handle = start_handle,
|
||||
};
|
||||
|
||||
params->func(conn, &attr, params);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t bt_gatt_get_mtu(struct bt_conn *conn)
|
||||
{
|
||||
return 64;
|
||||
|
|
|
@ -309,3 +309,9 @@ tests:
|
|||
platform_allow: native_posix
|
||||
extra_configs:
|
||||
- CONFIG_BT_GMAP=n
|
||||
bluetooth.audio_shell.no_cap_commander:
|
||||
extra_args: CONF_FILE="audio.conf"
|
||||
build_only: true
|
||||
platform_allow: native_posix
|
||||
extra_configs:
|
||||
- CONFIG_BT_CAP_COMMANDER=n
|
||||
|
|
|
@ -124,6 +124,7 @@ CONFIG_BT_HAS_FEATURES_NOTIFIABLE=y
|
|||
# Common Audio Profile
|
||||
CONFIG_BT_CAP_ACCEPTOR=y
|
||||
CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER=y
|
||||
CONFIG_BT_CAP_COMMANDER=y
|
||||
CONFIG_BT_CAP_INITIATOR=y
|
||||
|
||||
# Telephony and Media Audio Profile
|
||||
|
|
|
@ -758,6 +758,15 @@ static void test_cap_acceptor_broadcast(void)
|
|||
PASS("CAP acceptor broadcast passed\n");
|
||||
}
|
||||
|
||||
static void test_cap_acceptor_capture_and_render(void)
|
||||
{
|
||||
init();
|
||||
|
||||
WAIT_FOR_FLAG(flag_connected);
|
||||
|
||||
PASS("CAP acceptor unicast passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_cap_acceptor[] = {
|
||||
{
|
||||
.test_id = "cap_acceptor_unicast",
|
||||
|
@ -777,6 +786,12 @@ static const struct bst_test_instance test_cap_acceptor[] = {
|
|||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_cap_acceptor_broadcast,
|
||||
},
|
||||
{
|
||||
.test_id = "cap_acceptor_capture_and_render",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_cap_acceptor_capture_and_render,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
|
|
216
tests/bsim/bluetooth/audio/src/cap_commander_test.c
Normal file
216
tests/bsim/bluetooth/audio/src/cap_commander_test.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_BT_CAP_COMMANDER)
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/byteorder.h>
|
||||
#include <zephyr/bluetooth/audio/bap_lc3_preset.h>
|
||||
#include <zephyr/bluetooth/audio/cap.h>
|
||||
#include <zephyr/bluetooth/audio/bap.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include "common.h"
|
||||
#include "bap_common.h"
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
static struct bt_conn *connected_conns[CONFIG_BT_MAX_CONN];
|
||||
static volatile size_t connected_conn_cnt;
|
||||
|
||||
CREATE_FLAG(flag_discovered);
|
||||
CREATE_FLAG(flag_mtu_exchanged);
|
||||
|
||||
static void cap_discovery_complete_cb(struct bt_conn *conn, int err,
|
||||
const struct bt_csip_set_coordinator_csis_inst *csis_inst)
|
||||
{
|
||||
if (err != 0) {
|
||||
FAIL("Failed to discover CAS: %d", err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR_SET_MEMBER)) {
|
||||
if (csis_inst == NULL) {
|
||||
FAIL("Failed to discover CAS CSIS");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Found CAS with CSIS %p\n", csis_inst);
|
||||
} else {
|
||||
printk("Found CAS\n");
|
||||
}
|
||||
|
||||
SET_FLAG(flag_discovered);
|
||||
}
|
||||
|
||||
static struct bt_cap_commander_cb cap_cb = {
|
||||
.discovery_complete = cap_discovery_complete_cb,
|
||||
};
|
||||
|
||||
static void att_mtu_updated(struct bt_conn *conn, uint16_t tx, uint16_t rx)
|
||||
{
|
||||
printk("MTU exchanged\n");
|
||||
SET_FLAG(flag_mtu_exchanged);
|
||||
}
|
||||
|
||||
static struct bt_gatt_cb gatt_callbacks = {
|
||||
.att_mtu_updated = att_mtu_updated,
|
||||
};
|
||||
|
||||
static void init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth enable failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_gatt_cb_register(&gatt_callbacks);
|
||||
|
||||
err = bt_cap_commander_register_cb(&cap_cb);
|
||||
if (err != 0) {
|
||||
FAIL("Failed to register CAP callbacks (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void cap_device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
struct bt_conn *conn;
|
||||
int err;
|
||||
|
||||
/* We're only interested in connectable events */
|
||||
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
|
||||
return;
|
||||
}
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, addr);
|
||||
if (conn != NULL) {
|
||||
/* Already connected to this device */
|
||||
bt_conn_unref(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
|
||||
printk("Device found: %s (RSSI %d)\n", addr_str, rssi);
|
||||
|
||||
/* connect only to devices in close proximity */
|
||||
if (rssi < -70) {
|
||||
FAIL("RSSI too low");
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Stopping scan\n");
|
||||
if (bt_le_scan_stop()) {
|
||||
FAIL("Could not stop scan");
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_conn_le_create(
|
||||
addr, BT_CONN_LE_CREATE_CONN,
|
||||
BT_LE_CONN_PARAM(BT_GAP_INIT_CONN_INT_MIN, BT_GAP_INIT_CONN_INT_MIN, 0, 400),
|
||||
&connected_conns[connected_conn_cnt]);
|
||||
if (err) {
|
||||
FAIL("Could not connect to peer: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_and_connect(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
UNSET_FLAG(flag_connected);
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, cap_device_found);
|
||||
if (err != 0) {
|
||||
FAIL("Scanning failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Scanning successfully started\n");
|
||||
WAIT_FOR_FLAG(flag_connected);
|
||||
connected_conn_cnt++;
|
||||
}
|
||||
|
||||
static void disconnect_acl(struct bt_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
||||
if (err != 0) {
|
||||
FAIL("Failed to disconnect (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void discover_cas(struct bt_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
UNSET_FLAG(flag_discovered);
|
||||
|
||||
err = bt_cap_commander_discover(conn);
|
||||
if (err != 0) {
|
||||
printk("Failed to discover CAS: %d\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_discovered);
|
||||
}
|
||||
|
||||
static void test_main_cap_commander_capture_and_render(void)
|
||||
{
|
||||
init();
|
||||
|
||||
/* Connect to and do discovery on all CAP acceptors */
|
||||
for (size_t i = 0U; i < get_dev_cnt() - 1; i++) {
|
||||
scan_and_connect();
|
||||
|
||||
WAIT_FOR_FLAG(flag_mtu_exchanged);
|
||||
|
||||
/* TODO: We should use CSIP to find set members */
|
||||
discover_cas(default_conn);
|
||||
}
|
||||
|
||||
/* TODO: Do CAP Commander stuff */
|
||||
|
||||
/* Disconnect all CAP acceptors */
|
||||
for (size_t i = 0U; i < connected_conn_cnt; i++) {
|
||||
disconnect_acl(connected_conns[i]);
|
||||
}
|
||||
connected_conn_cnt = 0U;
|
||||
|
||||
PASS("CAP commander capture and rendering passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_cap_commander[] = {
|
||||
{
|
||||
.test_id = "cap_commander_capture_and_render",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_cap_commander_capture_and_render,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_cap_commander_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_cap_commander);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_BT_CAP_COMMANDER */
|
||||
|
||||
struct bst_test_list *test_cap_commander_install(struct bst_test_list *tests)
|
||||
{
|
||||
return tests;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_CAP_COMMANDER */
|
|
@ -167,6 +167,11 @@ static void register_more_cmd_args(void)
|
|||
}
|
||||
NATIVE_TASK(register_more_cmd_args, PRE_BOOT_1, 100);
|
||||
|
||||
uint16_t get_dev_cnt(void)
|
||||
{
|
||||
return (uint16_t)dev_cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the channel id based on remote device ID
|
||||
*
|
||||
|
|
|
@ -98,6 +98,7 @@ extern volatile bt_security_t security_level;
|
|||
void disconnected(struct bt_conn *conn, uint8_t reason);
|
||||
void test_tick(bs_time_t HW_device_time);
|
||||
void test_init(void);
|
||||
uint16_t get_dev_cnt(void);
|
||||
void backchannel_sync_send(uint dev);
|
||||
void backchannel_sync_send_all(void);
|
||||
void backchannel_sync_wait(uint dev);
|
||||
|
|
|
@ -25,6 +25,7 @@ extern struct bst_test_list *test_scan_delegator_install(struct bst_test_list *t
|
|||
extern struct bst_test_list *test_bap_broadcast_assistant_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_bass_broadcaster_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_cap_acceptor_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_cap_commander_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_cap_initiator_broadcast_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_cap_initiator_unicast_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_has_install(struct bst_test_list *tests);
|
||||
|
@ -59,6 +60,7 @@ bst_test_install_t test_installers[] = {
|
|||
test_scan_delegator_install,
|
||||
test_bap_broadcast_assistant_install,
|
||||
test_bass_broadcaster_install,
|
||||
test_cap_commander_install,
|
||||
test_cap_acceptor_install,
|
||||
test_cap_initiator_broadcast_install,
|
||||
test_cap_initiator_unicast_install,
|
||||
|
|
33
tests/bsim/bluetooth/audio/test_scripts/cap_capture_and_render.sh
Executable file
33
tests/bsim/bluetooth/audio/test_scripts/cap_capture_and_render.sh
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
SIMULATION_ID="cap_capture_and_render"
|
||||
VERBOSITY_LEVEL=2
|
||||
EXECUTE_TIMEOUT=20
|
||||
|
||||
source ${ZEPHYR_BASE}/tests/bsim/sh_common.source
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
printf "\n\n======== Running CAP commander capture and rendering test =========\n\n"
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
|
||||
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=0 -testid=cap_commander_capture_and_render \
|
||||
-rs=46 -D=3
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
|
||||
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=1 -testid=cap_acceptor_capture_and_render \
|
||||
-rs=23 -D=3
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_audio_prj_conf \
|
||||
-v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} -d=2 -testid=cap_acceptor_capture_and_render \
|
||||
-rs=69 -D=3
|
||||
|
||||
# Simulation time should be larger than the WAIT_TIME in common.h
|
||||
Execute ./bs_2G4_phy_v1 -v=${VERBOSITY_LEVEL} -s=${SIMULATION_ID} \
|
||||
-D=3 -sim_length=60e6 $@
|
||||
|
||||
wait_for_background_jobs
|
Loading…
Add table
Add a link
Reference in a new issue