blob: 41493f1d7cce7961e11e1d760b37c9c3e193e409 [file] [log] [blame]
/*
* Copyright (c) 2019 - 2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_BT_MCC
#include <zephyr/bluetooth/audio/mcc.h>
#include <zephyr/bluetooth/audio/media_proxy.h>
#include <zephyr/bluetooth/services/ots.h>
#include "common.h"
extern enum bst_result_t bst_result;
static struct bt_mcc_cb mcc_cb;
static uint64_t g_icon_object_id;
static uint64_t g_track_segments_object_id;
static uint64_t g_current_track_object_id;
static uint64_t g_next_track_object_id;
static uint64_t g_parent_group_object_id;
static uint64_t g_current_group_object_id;
static uint64_t g_search_results_object_id;
static int32_t g_pos;
static int8_t g_pb_speed;
static uint8_t g_playing_order;
static uint8_t g_state;
static uint8_t g_command_result;
static uint8_t g_search_result;
CREATE_FLAG(ble_is_initialized);
CREATE_FLAG(ble_link_is_ready);
CREATE_FLAG(discovery_done);
CREATE_FLAG(player_name_read);
CREATE_FLAG(icon_object_id_read);
CREATE_FLAG(icon_url_read);
CREATE_FLAG(track_change_notified);
CREATE_FLAG(track_title_read);
CREATE_FLAG(track_duration_read);
CREATE_FLAG(track_position_read);
CREATE_FLAG(track_position_set);
CREATE_FLAG(playback_speed_read);
CREATE_FLAG(playback_speed_set);
CREATE_FLAG(seeking_speed_read);
CREATE_FLAG(track_segments_object_id_read);
CREATE_FLAG(current_track_object_id_read);
CREATE_FLAG(current_track_object_id_set);
CREATE_FLAG(next_track_object_id_read);
CREATE_FLAG(next_track_object_id_set);
CREATE_FLAG(parent_group_object_id_read);
CREATE_FLAG(current_group_object_id_read);
CREATE_FLAG(current_group_object_id_set);
CREATE_FLAG(search_results_object_id_read);
CREATE_FLAG(playing_order_read);
CREATE_FLAG(playing_order_set);
CREATE_FLAG(playing_orders_supported_read);
CREATE_FLAG(ccid_read);
CREATE_FLAG(media_state_read);
CREATE_FLAG(command_sent);
CREATE_FLAG(command_notified);
CREATE_FLAG(search_sent);
CREATE_FLAG(search_notified);
CREATE_FLAG(object_selected);
CREATE_FLAG(metadata_read);
CREATE_FLAG(object_read);
static void mcc_discover_mcs_cb(struct bt_conn *conn, int err)
{
if (err) {
FAIL("Discovery of MCS failed (%d)\n", err);
return;
}
SET_FLAG(discovery_done);
}
static void mcc_read_player_name_cb(struct bt_conn *conn, int err, const char *name)
{
if (err) {
FAIL("Player Name read failed (%d)\n", err);
return;
}
SET_FLAG(player_name_read);
}
static void mcc_read_icon_obj_id_cb(struct bt_conn *conn, int err, uint64_t id)
{
if (err) {
FAIL("Icon Object ID read failed (%d)", err);
return;
}
g_icon_object_id = id;
SET_FLAG(icon_object_id_read);
}
static void mcc_read_icon_url_cb(struct bt_conn *conn, int err, const char *url)
{
if (err) {
FAIL("Icon URL read failed (%d)", err);
return;
}
SET_FLAG(icon_url_read);
}
static void mcc_track_changed_ntf_cb(struct bt_conn *conn, int err)
{
if (err) {
FAIL("Track change notification failed (%d)", err);
return;
}
SET_FLAG(track_change_notified);
}
static void mcc_read_track_title_cb(struct bt_conn *conn, int err, const char *title)
{
if (err) {
FAIL("Track title read failed (%d)", err);
return;
}
SET_FLAG(track_title_read);
}
static void mcc_read_track_duration_cb(struct bt_conn *conn, int err, int32_t dur)
{
if (err) {
FAIL("Track duration read failed (%d)", err);
return;
}
SET_FLAG(track_duration_read);
}
static void mcc_read_track_position_cb(struct bt_conn *conn, int err, int32_t pos)
{
if (err) {
FAIL("Track position read failed (%d)", err);
return;
}
g_pos = pos;
SET_FLAG(track_position_read);
}
static void mcc_set_track_position_cb(struct bt_conn *conn, int err, int32_t pos)
{
if (err) {
FAIL("Track Position set failed (%d)", err);
return;
}
g_pos = pos;
SET_FLAG(track_position_set);
}
static void mcc_read_playback_speed_cb(struct bt_conn *conn, int err,
int8_t speed)
{
if (err) {
FAIL("Playback speed read failed (%d)", err);
return;
}
g_pb_speed = speed;
SET_FLAG(playback_speed_read);
}
static void mcc_set_playback_speed_cb(struct bt_conn *conn, int err, int8_t speed)
{
if (err) {
FAIL("Playback speed set failed (%d)", err);
return;
}
g_pb_speed = speed;
SET_FLAG(playback_speed_set);
}
static void mcc_read_seeking_speed_cb(struct bt_conn *conn, int err,
int8_t speed)
{
if (err) {
FAIL("Seeking speed read failed (%d)", err);
return;
}
SET_FLAG(seeking_speed_read);
}
static void mcc_read_segments_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Track Segments ID read failed (%d)\n", err);
return;
}
g_track_segments_object_id = id;
SET_FLAG(track_segments_object_id_read);
}
static void mcc_read_current_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Current Track Object ID read failed (%d)\n", err);
return;
}
g_current_track_object_id = id;
SET_FLAG(current_track_object_id_read);
}
static void mcc_set_current_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Current Track Object ID set failed (%d)\n", err);
return;
}
g_current_track_object_id = id;
SET_FLAG(current_track_object_id_set);
}
static void mcc_read_next_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Next Track Object ID read failed (%d)\n", err);
return;
}
g_next_track_object_id = id;
SET_FLAG(next_track_object_id_read);
}
static void mcc_set_next_track_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Next Track Object ID set failed (%d)\n", err);
return;
}
g_next_track_object_id = id;
SET_FLAG(next_track_object_id_set);
}
static void mcc_read_parent_group_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Parent Group Object ID read failed (%d)\n", err);
return;
}
g_parent_group_object_id = id;
SET_FLAG(parent_group_object_id_read);
}
static void mcc_read_current_group_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Current Group Object ID read failed (%d)\n", err);
return;
}
g_current_group_object_id = id;
SET_FLAG(current_group_object_id_read);
}
static void mcc_set_current_group_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Current Group Object ID set failed (%d)\n", err);
return;
}
g_current_group_object_id = id;
SET_FLAG(current_group_object_id_set);
}
static void mcc_read_playing_order_cb(struct bt_conn *conn, int err, uint8_t order)
{
if (err) {
FAIL("Playing order read failed (%d)", err);
return;
}
g_playing_order = order;
SET_FLAG(playing_order_read);
}
static void mcc_set_playing_order_cb(struct bt_conn *conn, int err, uint8_t order)
{
if (err) {
FAIL("Playing order set failed (%d)", err);
return;
}
g_playing_order = order;
SET_FLAG(playing_order_set);
}
static void mcc_read_playing_orders_supported_cb(struct bt_conn *conn, int err,
uint16_t orders)
{
if (err) {
FAIL("Playing orders supported read failed (%d)", err);
return;
}
SET_FLAG(playing_orders_supported_read);
}
static void mcc_read_media_state_cb(struct bt_conn *conn, int err, uint8_t state)
{
if (err) {
FAIL("Media State read failed (%d)", err);
return;
}
g_state = state;
SET_FLAG(media_state_read);
}
static void mcc_send_command_cb(struct bt_conn *conn, int err, const struct mpl_cmd *cmd)
{
if (err) {
FAIL("Command send failed (%d) - opcode: %u, param: %d",
err, cmd->opcode, cmd->param);
return;
}
SET_FLAG(command_sent);
}
static void mcc_cmd_ntf_cb(struct bt_conn *conn, int err, const struct mpl_cmd_ntf *ntf)
{
if (err) {
FAIL("Command notification error (%d) - opcode: %u, result: %u",
err, ntf->requested_opcode, ntf->result_code);
return;
}
g_command_result = ntf->result_code;
SET_FLAG(command_notified);
}
static void mcc_send_search_cb(struct bt_conn *conn, int err,
const struct mpl_search *search)
{
if (err) {
FAIL("Search send failed (%d)", err);
return;
}
SET_FLAG(search_sent);
}
static void mcc_search_ntf_cb(struct bt_conn *conn, int err, uint8_t result_code)
{
if (err) {
FAIL("Search notification error (%d), result code: %u",
err, result_code);
return;
}
g_search_result = result_code;
SET_FLAG(search_notified);
}
static void mcc_read_search_results_obj_id_cb(struct bt_conn *conn, int err,
uint64_t id)
{
if (err) {
FAIL("Search Results Object ID read failed (%d)", err);
return;
}
g_search_results_object_id = id;
SET_FLAG(search_results_object_id_read);
}
static void mcc_read_content_control_id_cb(struct bt_conn *conn, int err, uint8_t ccid)
{
if (err) {
FAIL("Content control ID read failed (%d)", err);
return;
}
SET_FLAG(ccid_read);
}
static void mcc_otc_obj_selected_cb(struct bt_conn *conn, int err)
{
if (err) {
FAIL("Selecting object failed (%d)\n", err);
return;
}
SET_FLAG(object_selected);
}
static void mcc_otc_obj_metadata_cb(struct bt_conn *conn, int err)
{
if (err) {
FAIL("Reading object metadata failed (%d)\n", err);
return;
}
SET_FLAG(metadata_read);
}
static void mcc_icon_object_read_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
FAIL("Reading Icon Object failed (%d)", err);
return;
}
SET_FLAG(object_read);
}
static void mcc_track_segments_object_read_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
FAIL("Reading Track Segments Object failed (%d)", err);
return;
}
SET_FLAG(object_read);
}
static void mcc_otc_read_current_track_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
FAIL("Current Track Object read failed (%d)", err);
return;
}
SET_FLAG(object_read);
}
static void mcc_otc_read_next_track_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
FAIL("Next Track Object read failed (%d)", err);
return;
}
SET_FLAG(object_read);
}
static void mcc_otc_read_parent_group_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
FAIL("Parent Group Object read failed (%d)", err);
return;
}
SET_FLAG(object_read);
}
static void mcc_otc_read_current_group_object_cb(struct bt_conn *conn, int err,
struct net_buf_simple *buf)
{
if (err) {
FAIL("Current Group Object read failed (%d)", err);
return;
}
SET_FLAG(object_read);
}
int do_mcc_init(void)
{
/* Set up the callbacks */
mcc_cb.discover_mcs = mcc_discover_mcs_cb;
mcc_cb.read_player_name = mcc_read_player_name_cb;
mcc_cb.read_icon_obj_id = mcc_read_icon_obj_id_cb;
mcc_cb.read_icon_url = mcc_read_icon_url_cb;
mcc_cb.track_changed_ntf = mcc_track_changed_ntf_cb;
mcc_cb.read_track_title = mcc_read_track_title_cb;
mcc_cb.read_track_duration = mcc_read_track_duration_cb;
mcc_cb.read_track_position = mcc_read_track_position_cb;
mcc_cb.set_track_position = mcc_set_track_position_cb;
mcc_cb.read_playback_speed = mcc_read_playback_speed_cb;
mcc_cb.set_playback_speed = mcc_set_playback_speed_cb;
mcc_cb.read_seeking_speed = mcc_read_seeking_speed_cb;
mcc_cb.read_current_track_obj_id = mcc_read_current_track_obj_id_cb;
mcc_cb.set_current_track_obj_id = mcc_set_current_track_obj_id_cb;
mcc_cb.read_next_track_obj_id = mcc_read_next_track_obj_id_cb;
mcc_cb.set_next_track_obj_id = mcc_set_next_track_obj_id_cb;
mcc_cb.read_segments_obj_id = mcc_read_segments_obj_id_cb;
mcc_cb.read_parent_group_obj_id = mcc_read_parent_group_obj_id_cb;
mcc_cb.read_current_group_obj_id = mcc_read_current_group_obj_id_cb;
mcc_cb.set_current_group_obj_id = mcc_set_current_group_obj_id_cb;
mcc_cb.read_playing_order = mcc_read_playing_order_cb;
mcc_cb.set_playing_order = mcc_set_playing_order_cb;
mcc_cb.read_playing_orders_supported = mcc_read_playing_orders_supported_cb;
mcc_cb.read_media_state = mcc_read_media_state_cb;
mcc_cb.send_cmd = mcc_send_command_cb;
mcc_cb.cmd_ntf = mcc_cmd_ntf_cb;
mcc_cb.send_search = mcc_send_search_cb;
mcc_cb.search_ntf = mcc_search_ntf_cb;
mcc_cb.read_search_results_obj_id = mcc_read_search_results_obj_id_cb;
mcc_cb.read_content_control_id = mcc_read_content_control_id_cb;
mcc_cb.otc_obj_selected = mcc_otc_obj_selected_cb;
mcc_cb.otc_obj_metadata = mcc_otc_obj_metadata_cb;
mcc_cb.otc_icon_object = mcc_icon_object_read_cb;
mcc_cb.otc_track_segments_object = mcc_track_segments_object_read_cb;
mcc_cb.otc_current_track_object = mcc_otc_read_current_track_object_cb;
mcc_cb.otc_next_track_object = mcc_otc_read_next_track_object_cb;
mcc_cb.otc_parent_group_object = mcc_otc_read_parent_group_object_cb;
mcc_cb.otc_current_group_object = mcc_otc_read_current_group_object_cb;
/* Initialize the module */
return bt_mcc_init(&mcc_cb);
}
/* Callback after Bluetoot initialization attempt */
static void bt_ready(int err)
{
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
SET_FLAG(ble_is_initialized);
}
/* Callback on connection */
static void connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err) {
bt_conn_unref(default_conn);
default_conn = NULL;
FAIL("Failed to connect to %s (%u)\n", addr, err);
return;
}
SET_FLAG(ble_link_is_ready);
}
/* Helper function - select object and read the object metadata
*
* Will FAIL the test on errors calling select and read metadata.
* Will WAIT (hang) until callbacks are received.
* If callbacks are not received, the test fill FAIL due to timeout.
*
* @param object_id ID of the object to select and read metadata for
*/
static void select_read_meta(int64_t id)
{
int err;
/* TODO: Fix the instance pointer - it is neither valid nor used */
UNSET_FLAG(object_selected);
err = bt_ots_client_select_id(bt_mcc_otc_inst(default_conn),
default_conn, id);
if (err) {
FAIL("Failed to select object\n");
return;
}
WAIT_FOR_FLAG(object_selected);
printk("Selecting object succeeded\n");
/* TODO: Fix the instance pointer - it is neither valid nor used */
UNSET_FLAG(metadata_read);
err = bt_ots_client_read_object_metadata(bt_mcc_otc_inst(default_conn),
default_conn,
BT_OTS_METADATA_REQ_ALL);
if (err) {
FAIL("Failed to read object metadata\n");
return;
}
WAIT_FOR_FLAG(metadata_read);
printk("Reading object metadata succeeded\n");
}
/* Helper function to read the media state and verify that it is as expected
* Will FAIL on error reading the media state
* Will FAIL if the state is not as expected
*
* Returns true if the state is as expected
* Returns false in case of errors, or if the state is not as expected
*/
static bool test_verify_media_state_wait_flags(uint8_t expected_state)
{
int err;
UNSET_FLAG(media_state_read);
err = bt_mcc_read_media_state(default_conn);
if (err) {
FAIL("Failed to read media state: %d", err);
return false;
}
WAIT_FOR_FLAG(media_state_read);
if (g_state != expected_state) {
FAIL("Server is not in expected state: %d, expected: %d\n",
g_state, expected_state);
return false;
}
return true;
}
/* Helper function to write commands to to the control point, including the
* flag handling.
* Will FAIL on error to send the command.
* Will WAIT for the required flags before returning.
*/
static void test_send_cmd_wait_flags(struct mpl_cmd *cmd)
{
int err;
/* Need both flags, even if the notification result is what we care
* about. The notification may come before the write callback, and if
* the write callback has not yet arrived, we will get EBUSY at the
* next call.
*/
UNSET_FLAG(command_sent);
UNSET_FLAG(command_notified);
err = bt_mcc_send_cmd(default_conn, cmd);
if (err) {
FAIL("Failed to send command: %d, opcode: %u",
err, cmd->opcode);
return;
}
WAIT_FOR_FLAG(command_sent);
WAIT_FOR_FLAG(command_notified);
}
static void test_cp_play(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_PLAY;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("PLAY command failed\n");
return;
}
if (test_verify_media_state_wait_flags(BT_MCS_MEDIA_STATE_PLAYING)) {
printk("PLAY command succeeded\n");
}
}
static void test_cp_pause(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_PAUSE;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("PAUSE command failed\n");
return;
}
if (test_verify_media_state_wait_flags(BT_MCS_MEDIA_STATE_PAUSED)) {
printk("PAUSE command succeeded\n");
}
}
static void test_cp_fast_rewind(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_FAST_REWIND;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("FAST REWIND command failed\n");
return;
}
if (test_verify_media_state_wait_flags(BT_MCS_MEDIA_STATE_SEEKING)) {
printk("FAST REWIND command succeeded\n");
}
}
static void test_cp_fast_forward(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_FAST_FORWARD;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("FAST FORWARD command failed\n");
return;
}
if (test_verify_media_state_wait_flags(BT_MCS_MEDIA_STATE_SEEKING)) {
printk("FAST FORWARD command succeeded\n");
}
}
static void test_cp_stop(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_STOP;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("STOP command failed\n");
return;
}
/* There is no "STOPPED" state in the spec - STOP goes to PAUSED */
if (test_verify_media_state_wait_flags(BT_MCS_MEDIA_STATE_PAUSED)) {
printk("STOP command succeeded\n");
}
}
static void test_cp_move_relative(void)
{
int err;
struct mpl_cmd cmd;
/* Assumes that the server is in a state where it is able to change
* the current track position
* Also assumes position will not change by itself, which is wrong if
* if the player is playing.
*/
UNSET_FLAG(track_position_read);
err = bt_mcc_read_track_position(default_conn);
if (err) {
FAIL("Failed to read track position: %d\n", err);
return;
}
WAIT_FOR_FLAG(track_position_read);
uint32_t tmp_pos = g_pos;
cmd.opcode = BT_MCS_OPC_MOVE_RELATIVE;
cmd.use_param = true;
cmd.param = 1000; /* Position change, measured in 1/100 of a second */
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("MOVE RELATIVE command failed\n");
return;
}
UNSET_FLAG(track_position_read);
err = bt_mcc_read_track_position(default_conn);
if (err) {
FAIL("Failed to read track position: %d\n", err);
return;
}
WAIT_FOR_FLAG(track_position_read);
if (g_pos == tmp_pos) {
/* Position did not change */
FAIL("Server did not move track position\n");
return;
}
printk("MOVE RELATIVE command succeeded\n");
}
static void test_cp_prev_segment(void)
{
struct mpl_cmd cmd;
/* Assumes that the server is in a state where there is a current
* track that has segments, and where the server may switch between
* these
*/
/* To properly verify track segment changes, the track segments
* object must be downloaded and parsed. That is somewhat complex,
* and is getting close to what the qualification tests do.
* Alternatively, the track position may be checked, but the server
* implementation does not set that for segment changes yet.
* For now, we will settle for seeing that the opcodes are accepted.
*/
cmd.opcode = BT_MCS_OPC_PREV_SEGMENT;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("PREV SEGMENT command failed\n");
return;
}
printk("PREV SEGMENT command succeeded\n");
}
static void test_cp_next_segment(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_NEXT_SEGMENT;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("NEXT SEGMENT command failed\n");
return;
}
printk("NEXT SEGMENT command succeeded\n");
}
static void test_cp_first_segment(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_FIRST_SEGMENT;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("FIRST SEGMENT command failed\n");
return;
}
printk("FIRST SEGMENT command succeeded\n");
}
static void test_cp_last_segment(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_LAST_SEGMENT;
cmd.use_param = false;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("LAST SEGMENT command failed\n");
return;
}
printk("LAST SEGMENT command succeeded\n");
}
static void test_cp_goto_segment(void)
{
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_GOTO_SEGMENT;
cmd.use_param = true;
cmd.param = 2; /* Second segment - not the first, maybe not last */
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("GOTO SEGMENT command failed\n");
return;
}
printk("GOTO SEGMENT command succeeded\n");
}
/* Helper function to read the current track object ID, including flag handling
* Will FAIL on error reading object ID
* Will WAIT until the read is completed (object ID flag read flag is set)
*/
static void test_read_current_track_object_id_wait_flags(void)
{
int err;
UNSET_FLAG(current_track_object_id_read);
err = bt_mcc_read_current_track_obj_id(default_conn);
if (err) {
FAIL("Failed to read current track object ID: %d", err);
return;
}
WAIT_FOR_FLAG(current_track_object_id_read);
}
static void test_cp_prev_track(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
/* Assumes that the server is in a state where it has multiple tracks
* and can change between them.
*/
/* To verify that a track change has happeded, the test checks that the
* current track object ID has changed.
*/
cmd.opcode = BT_MCS_OPC_PREV_TRACK;
cmd.use_param = false;
test_read_current_track_object_id_wait_flags();
object_id = g_current_track_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("PREV TRACK command failed\n");
return;
}
test_read_current_track_object_id_wait_flags();
if (g_current_track_object_id == object_id) {
/* Track did not change */
FAIL("Server did not change track\n");
return;
}
printk("PREV TRACK command succeeded\n");
}
static void test_cp_next_track_and_track_changed(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
/* This test is also used to test the track changed notification */
UNSET_FLAG(track_change_notified);
cmd.opcode = BT_MCS_OPC_NEXT_TRACK;
cmd.use_param = false;
test_read_current_track_object_id_wait_flags();
object_id = g_current_track_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("NEXT TRACK command failed\n");
return;
}
WAIT_FOR_FLAG(track_change_notified);
printk("Track change notified\n");
test_read_current_track_object_id_wait_flags();
if (g_current_track_object_id == object_id) {
FAIL("Server did not change track\n");
return;
}
printk("NEXT TRACK command succeeded\n");
}
static void test_cp_first_track(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_FIRST_TRACK;
cmd.use_param = false;
test_read_current_track_object_id_wait_flags();
object_id = g_current_track_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("FIRST TRACK command failed\n");
return;
}
test_read_current_track_object_id_wait_flags();
if (g_current_track_object_id == object_id) {
FAIL("Server did not change track\n");
return;
}
printk("FIRST TRACK command succeeded\n");
}
static void test_cp_last_track(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_LAST_TRACK;
cmd.use_param = false;
test_read_current_track_object_id_wait_flags();
object_id = g_current_track_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("LAST TRACK command failed\n");
return;
}
test_read_current_track_object_id_wait_flags();
if (g_current_track_object_id == object_id) {
FAIL("Server did not change track\n");
return;
}
printk("LAST TRACK command succeeded\n");
}
static void test_cp_goto_track(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_GOTO_TRACK;
cmd.use_param = true;
cmd.param = 2; /* Second track, not the first, maybe not the last */
test_read_current_track_object_id_wait_flags();
object_id = g_current_track_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("GOTO TRACK command failed\n");
return;
}
test_read_current_track_object_id_wait_flags();
if (g_current_track_object_id == object_id) {
FAIL("Server did not change track\n");
return;
}
printk("GOTO TRACK command succeeded\n");
}
/* Helper function to read the current group object ID, including flag handling
* Will FAIL on error reading object ID
* Will WAIT until the read is completed (object ID flag read flag is set)
*/
static void test_read_current_group_object_id_wait_flags(void)
{
int err;
UNSET_FLAG(current_group_object_id_read);
err = bt_mcc_read_current_group_obj_id(default_conn);
if (err) {
FAIL("Failed to read current group object ID: %d", err);
return;
}
WAIT_FOR_FLAG(current_group_object_id_read);
}
static void test_cp_prev_group(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
/* Assumes that the server is in a state where it has multiple groups
* and can change between them.
*/
/* To verify that a group change has happeded, the test checks that the
* current group object ID has changed.
*/
cmd.opcode = BT_MCS_OPC_PREV_GROUP;
cmd.use_param = false;
test_read_current_group_object_id_wait_flags();
object_id = g_current_group_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("PREV GROUP command failed\n");
return;
}
test_read_current_group_object_id_wait_flags();
if (g_current_group_object_id == object_id) {
/* Group did not change */
FAIL("Server did not change group\n");
return;
}
printk("PREV GROUP command succeeded\n");
}
static void test_cp_next_group(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_NEXT_GROUP;
cmd.use_param = false;
test_read_current_group_object_id_wait_flags();
object_id = g_current_group_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("NEXT GROUP command failed\n");
return;
}
test_read_current_group_object_id_wait_flags();
if (g_current_group_object_id == object_id) {
FAIL("Server did not change group\n");
return;
}
printk("NEXT GROUP command succeeded\n");
}
static void test_cp_first_group(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_FIRST_GROUP;
cmd.use_param = false;
test_read_current_group_object_id_wait_flags();
object_id = g_current_group_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("FIRST GROUP command failed\n");
return;
}
test_read_current_group_object_id_wait_flags();
if (g_current_group_object_id == object_id) {
FAIL("Server did not change group\n");
return;
}
printk("FIRST GROUP command succeeded\n");
}
static void test_cp_last_group(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_LAST_GROUP;
cmd.use_param = false;
test_read_current_group_object_id_wait_flags();
object_id = g_current_group_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("LAST GROUP command failed\n");
return;
}
test_read_current_group_object_id_wait_flags();
if (g_current_group_object_id == object_id) {
FAIL("Server did not change group\n");
return;
}
printk("LAST GROUP command succeeded\n");
}
static void test_cp_goto_group(void)
{
uint64_t object_id;
struct mpl_cmd cmd;
cmd.opcode = BT_MCS_OPC_GOTO_GROUP;
cmd.use_param = true;
cmd.param = 2; /* Second group, not the first, maybe not the last */
test_read_current_group_object_id_wait_flags();
object_id = g_current_group_object_id;
test_send_cmd_wait_flags(&cmd);
if (g_command_result != BT_MCS_OPC_NTF_SUCCESS) {
FAIL("GOTO GROUP command failed\n");
return;
}
test_read_current_group_object_id_wait_flags();
if (g_current_group_object_id == object_id) {
FAIL("Server did not change group\n");
return;
}
printk("GOTO GROUP command succeeded\n");
}
static void test_search(void)
{
struct mpl_search search;
struct mpl_sci sci = {0};
int err;
/* Test outline:
* - verify that the search results object ID is zero before search
* - write a search (one search control item) to the search control point,
* get write callback and notification
* - verify that the search results object ID is non-zero
*/
UNSET_FLAG(search_results_object_id_read);
err = bt_mcc_read_search_results_obj_id(default_conn);
if (err) {
FAIL("Failed to read search results object ID: %d", err);
return;
}
WAIT_FOR_FLAG(search_results_object_id_read);
if (g_search_results_object_id != 0) {
FAIL("Search results object ID not zero before search\n");
return;
}
/* Set up the search control item, then the search
* Note: As of now, the server implementation only fakes the search,
* so it makes no difference what we search for. The result is the
* same anyway.
*/
sci.type = BT_MCS_SEARCH_TYPE_TRACK_NAME;
strcpy(sci.param, "Some track name");
/* Length is length of type, plus length of param w/o termination */
sci.len = sizeof(sci.type) + strlen(sci.param);
search.len = 0;
memcpy(&search.search[search.len], &sci.len, sizeof(sci.len));
search.len += sizeof(sci.len);
memcpy(&search.search[search.len], &sci.type, sizeof(sci.type));
search.len += sizeof(sci.type);
memcpy(&search.search[search.len], &sci.param, strlen(sci.param));
search.len += strlen(sci.param);
UNSET_FLAG(search_sent);
UNSET_FLAG(search_notified);
UNSET_FLAG(search_results_object_id_read);
err = bt_mcc_send_search(default_conn, &search);
if (err) {
FAIL("Failed to write to search control point\n");
return;
}
WAIT_FOR_FLAG(search_sent);
WAIT_FOR_FLAG(search_notified);
if (g_search_result != BT_MCS_SCP_NTF_SUCCESS) {
FAIL("SEARCH operation failed\n");
return;
}
/* A search results object will have been created and the search
* results object ID will have been notified if the search gave results
*/
WAIT_FOR_FLAG(search_results_object_id_read);
if (g_search_results_object_id == 0) {
FAIL("No search results\n");
return;
}
printk("SEARCH operation succeeded\n");
}
/* This function tests all commands in the API in sequence
* The order of the sequence follows the order of the characterstics in the
* Media Control Service specification
*/
void test_main(void)
{
int err;
uint64_t tmp_object_id;
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
printk("Media Control Client test application. Board: %s\n", CONFIG_BOARD);
UNSET_FLAG(ble_is_initialized);
err = bt_enable(bt_ready);
if (err) {
FAIL("Bluetooth init failed (err %d)\n", err);
return;
}
WAIT_FOR_FLAG(ble_is_initialized);
printk("Bluetooth initialized\n");
bt_conn_cb_register(&conn_callbacks);
/* Connect ******************************************/
UNSET_FLAG(ble_link_is_ready);
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
if (err) {
FAIL("Failed to start scanning (err %d\n)", err);
} else {
printk("Scanning started successfully\n");
}
WAIT_FOR_FLAG(ble_link_is_ready);
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(default_conn), addr, sizeof(addr));
printk("Connected: %s\n", addr);
/* Initialize MCC ********************************************/
err = do_mcc_init();
if (err) {
FAIL("Could not initialize MCC (err %d\n)", err);
} else {
printk("MCC init succeeded\n");
}
/* Discover MCS, subscribe to notifications *******************/
UNSET_FLAG(discovery_done);
err = bt_mcc_discover_mcs(default_conn, true);
if (err) {
FAIL("Failed to start discovery of MCS: %d\n", err);
}
WAIT_FOR_FLAG(discovery_done);
printk("Discovery of MCS succeeded\n");
/* Read media player name ******************************************/
UNSET_FLAG(player_name_read);
err = bt_mcc_read_player_name(default_conn);
if (err) {
FAIL("Failed to read media player name ID: %d", err);
return;
}
WAIT_FOR_FLAG(player_name_read);
printk("Player Name read succeeded\n");
/* Read icon object ******************************************/
UNSET_FLAG(icon_object_id_read);
err = bt_mcc_read_icon_obj_id(default_conn);
if (err) {
FAIL("Failed to read icon object ID: %d", err);
return;
}
WAIT_FOR_FLAG(icon_object_id_read);
printk("Icon Object ID read succeeded\n");
select_read_meta(g_icon_object_id);
UNSET_FLAG(object_read);
err = bt_mcc_otc_read_icon_object(default_conn);
if (err) {
FAIL("Failed to read icon object\n");
return;
}
WAIT_FOR_FLAG(object_read);
printk("Reading Icon Object succeeded\n");
/* Read icon url *************************************************/
UNSET_FLAG(icon_url_read);
err = bt_mcc_read_icon_url(default_conn);
if (err) {
FAIL("Failed to read icon url: %d", err);
return;
}
WAIT_FOR_FLAG(icon_url_read);
printk("Icon URL read succeeded\n");
/* Track changed ************************************************
*
* The track changed characteristic is tested as part of the control
* point next track test
*/
/* Read track_title ******************************************/
UNSET_FLAG(track_title_read);
err = bt_mcc_read_track_title(default_conn);
if (err) {
FAIL("Failed to read track_title: %d", err);
return;
}
WAIT_FOR_FLAG(track_title_read);
printk("Track title read succeeded\n");
/* Read track_duration ******************************************/
UNSET_FLAG(track_duration_read);
err = bt_mcc_read_track_duration(default_conn);
if (err) {
FAIL("Failed to read track_duration: %d", err);
return;
}
WAIT_FOR_FLAG(track_duration_read);
printk("Track duration read succeeded\n");
/* Read and set track_position *************************************/
UNSET_FLAG(track_position_read);
err = bt_mcc_read_track_position(default_conn);
if (err) {
FAIL("Failed to read track position: %d", err);
return;
}
WAIT_FOR_FLAG(track_position_read);
printk("Track position read succeeded\n");
int32_t pos = g_pos + 1200; /*12 seconds further into the track */
UNSET_FLAG(track_position_set);
err = bt_mcc_set_track_position(default_conn, pos);
if (err) {
FAIL("Failed to set track position: %d", err);
return;
}
WAIT_FOR_FLAG(track_position_set);
if (g_pos != pos) {
/* In this controlled case, we expect that the resulting */
/* position is the position given in the set command */
FAIL("Track position set failed: Incorrect position\n");
}
printk("Track position set succeeded\n");
/* Read and set playback speed *************************************/
UNSET_FLAG(playback_speed_read);
err = bt_mcc_read_playback_speed(default_conn);
if (err) {
FAIL("Failed to read playback speed: %d", err);
return;
}
WAIT_FOR_FLAG(playback_speed_read);
printk("Playback speed read succeeded\n");
int8_t pb_speed = g_pb_speed + 8; /* 2^(8/64) faster than current speed */
UNSET_FLAG(playback_speed_set);
err = bt_mcc_set_playback_speed(default_conn, pb_speed);
if (err) {
FAIL("Failed to set playback speed: %d", err);
return;
}
WAIT_FOR_FLAG(playback_speed_set);
if (g_pb_speed != pb_speed) {
FAIL("Playback speed failed: Incorrect playback speed\n");
}
printk("Playback speed set succeeded\n");
/* Read seeking speed *************************************/
UNSET_FLAG(seeking_speed_read);
err = bt_mcc_read_seeking_speed(default_conn);
if (err) {
FAIL("Failed to read seeking speed: %d", err);
return;
}
WAIT_FOR_FLAG(seeking_speed_read);
printk("Seeking speed read succeeded\n");
/* Read track segments object *****************************************/
UNSET_FLAG(track_segments_object_id_read);
err = bt_mcc_read_segments_obj_id(default_conn);
if (err) {
FAIL("Failed to read track segments object ID: %d", err);
return;
}
WAIT_FOR_FLAG(track_segments_object_id_read);
printk("Track Segments Object ID read succeeded\n");
select_read_meta(g_track_segments_object_id);
UNSET_FLAG(object_read);
err = bt_mcc_otc_read_track_segments_object(default_conn);
if (err) {
FAIL("Failed to read track segments object\n");
return;
}
WAIT_FOR_FLAG(object_read);
printk("Reading Track Segments Object succeeded\n");
/* Current track object ***************************************/
UNSET_FLAG(current_track_object_id_set);
tmp_object_id = 0x103;
err = bt_mcc_set_current_track_obj_id(default_conn, tmp_object_id);
if (err) {
FAIL("Failed to set current track object ID: %d", err);
return;
}
WAIT_FOR_FLAG(current_track_object_id_set);
if (g_current_track_object_id != tmp_object_id) {
FAIL("Current track object ID not the one that was set");
return;
}
printk("Current Track Object ID set succeeded\n");
UNSET_FLAG(current_track_object_id_read);
err = bt_mcc_read_current_track_obj_id(default_conn);
if (err) {
FAIL("Failed to read current track object ID: %d", err);
return;
}
WAIT_FOR_FLAG(current_track_object_id_read);
if (g_current_track_object_id != tmp_object_id) {
FAIL("Current track object ID not the one that was set");
return;
}
printk("Current Track Object ID read succeeded\n");
UNSET_FLAG(object_read);
select_read_meta(g_current_track_object_id);
err = bt_mcc_otc_read_current_track_object(default_conn);
if (err) {
FAIL("Failed to current track object\n");
return;
}
WAIT_FOR_FLAG(object_read);
printk("Current Track Object read succeeded\n");
/* Next track object ***************************************************/
UNSET_FLAG(next_track_object_id_set);
tmp_object_id = 0x102;
err = bt_mcc_set_next_track_obj_id(default_conn, tmp_object_id);
if (err) {
FAIL("Failed to set next track object ID: %d", err);
return;
}
WAIT_FOR_FLAG(next_track_object_id_set);
if (g_next_track_object_id != tmp_object_id) {
FAIL("Next track object ID not the one that was set");
return;
}
printk("Next Track Object ID set succeeded\n");
UNSET_FLAG(next_track_object_id_read);
err = bt_mcc_read_next_track_obj_id(default_conn);
if (err) {
FAIL("Failed to read next track object ID: %d", err);
return;
}
WAIT_FOR_FLAG(next_track_object_id_read);
if (g_next_track_object_id != tmp_object_id) {
FAIL("Next track object ID not the one that was set");
return;
}
printk("Next Track Object ID read succeeded\n");
select_read_meta(g_next_track_object_id);
UNSET_FLAG(object_read);
err = bt_mcc_otc_read_next_track_object(default_conn);
if (err) {
FAIL("Failed to read next track object\n");
return;
}
WAIT_FOR_FLAG(object_read);
printk("Next Track Object read succeeded\n");
/* Read parent group object ******************************************/
UNSET_FLAG(parent_group_object_id_read);
err = bt_mcc_read_parent_group_obj_id(default_conn);
if (err) {
FAIL("Failed to read parent group object ID: %d", err);
return;
}
WAIT_FOR_FLAG(parent_group_object_id_read);
printk("Parent Group Object ID read succeeded\n");
select_read_meta(g_parent_group_object_id);
UNSET_FLAG(object_read);
err = bt_mcc_otc_read_parent_group_object(default_conn);
if (err) {
FAIL("Failed to read parent group object\n");
return;
}
WAIT_FOR_FLAG(object_read);
printk("Parent Group Object read succeeded\n");
/* Current group object ******************************************/
UNSET_FLAG(current_group_object_id_set);
tmp_object_id = 0x10e; /* ID of third group */
err = bt_mcc_set_current_group_obj_id(default_conn, tmp_object_id);
if (err) {
FAIL("Failed to set current group object ID: %d", err);
return;
}
WAIT_FOR_FLAG(current_group_object_id_set);
if (g_current_group_object_id != tmp_object_id) {
FAIL("Current group object ID not the one that was set");
return;
}
printk("Current Group Object ID set succeeded\n");
UNSET_FLAG(current_group_object_id_read);
err = bt_mcc_read_current_group_obj_id(default_conn);
if (err) {
FAIL("Failed to read current group object ID: %d", err);
return;
}
WAIT_FOR_FLAG(current_group_object_id_read);
if (g_current_group_object_id != tmp_object_id) {
FAIL("Current group object ID not the one that was set");
return;
}
printk("Current Group Object ID read succeeded\n");
select_read_meta(g_current_group_object_id);
UNSET_FLAG(object_read);
err = bt_mcc_otc_read_current_group_object(default_conn);
if (err) {
FAIL("Failed to read current group object\n");
return;
}
WAIT_FOR_FLAG(object_read);
printk("Current Group Object read succeeded\n");
/* Set current group back to first group, so that later tests (segments) will work.
* (Only the tracks of the first group has segments in the MPL.)
*/
UNSET_FLAG(current_group_object_id_set);
tmp_object_id = 0x106; /* ID of first group */
err = bt_mcc_set_current_group_obj_id(default_conn, tmp_object_id);
if (err) {
FAIL("Failed to set current group object ID: %d", err);
return;
}
WAIT_FOR_FLAG(current_group_object_id_set);
if (g_current_group_object_id != tmp_object_id) {
FAIL("Current group object ID not the one that was set");
return;
}
/* Read and set playing order *************************************/
UNSET_FLAG(playing_order_read);
err = bt_mcc_read_playing_order(default_conn);
if (err) {
FAIL("Failed to read playing order: %d", err);
return;
}
WAIT_FOR_FLAG(playing_order_read);
printk("Playing order read succeeded\n");
uint8_t playing_order;
if (g_playing_order != BT_MCS_PLAYING_ORDER_SHUFFLE_ONCE) {
playing_order = BT_MCS_PLAYING_ORDER_SHUFFLE_ONCE;
} else {
playing_order = BT_MCS_PLAYING_ORDER_SINGLE_ONCE;
}
UNSET_FLAG(playing_order_set);
err = bt_mcc_set_playing_order(default_conn, playing_order);
if (err) {
FAIL("Failed to set playing_order: %d", err);
return;
}
WAIT_FOR_FLAG(playing_order_set);
if (g_playing_order != playing_order) {
FAIL("Playing order set failed: Incorrect playing_order\n");
}
printk("Playing order set succeeded\n");
/* Read playing orders supported *************************************/
UNSET_FLAG(playing_orders_supported_read);
err = bt_mcc_read_playing_orders_supported(default_conn);
if (err) {
FAIL("Failed to read playing orders supported: %d", err);
return;
}
WAIT_FOR_FLAG(playing_orders_supported_read);
printk("Playing orders supported read succeeded\n");
/* Read media state ***************************************************/
UNSET_FLAG(media_state_read);
err = bt_mcc_read_media_state(default_conn);
if (err) {
FAIL("Failed to read media state: %d", err);
return;
}
WAIT_FOR_FLAG(media_state_read);
printk("Media state read succeeded\n");
/* Read content control ID *******************************************/
UNSET_FLAG(ccid_read);
err = bt_mcc_read_content_control_id(default_conn);
if (err) {
FAIL("Failed to read content control ID: %d", err);
return;
}
WAIT_FOR_FLAG(ccid_read);
printk("Content control ID read succeeded\n");
/* Control point - "state" opcodes */
/* This part of the test not only checks that the opcodes are accepted
* by the server, but also that they actually do lead to the expected
* state changes. This may lean too much upon knowledge or assumptions,
* and therefore be too fragile.
* It may be more robust to just give commands and check for the success
* code in the control point notifications
*/
/* It is assumed that the server starts the test in the paused state */
test_verify_media_state_wait_flags(BT_MCS_MEDIA_STATE_PAUSED);
/* The tests are ordered to ensure that each command changes state */
test_cp_play();
test_cp_fast_forward();
test_cp_pause();
test_cp_fast_rewind();
test_cp_stop();
/* Control point - move relative opcode */
test_cp_move_relative();
/* Control point - segment change opcodes */
test_cp_prev_segment();
test_cp_next_segment();
test_cp_first_segment();
test_cp_last_segment();
test_cp_goto_segment();
/* Control point - track change opcodes */
/* The tests are ordered to ensure that each command changes track */
/* Assumes we are not starting on the last track */
test_cp_next_track_and_track_changed();
test_cp_prev_track();
test_cp_last_track();
test_cp_first_track();
test_cp_goto_track();
/* Control point - group change opcodes *******************************/
/* The tests are ordered to ensure that each command changes group */
/* Assumes we are not starting on the last group */
test_cp_next_group();
test_cp_prev_group();
test_cp_last_group();
test_cp_first_group();
test_cp_goto_group();
/* Search control point */
test_search();
/* TEST IS COMPLETE */
PASS("MCC passed\n");
}
static const struct bst_test_instance test_mcs[] = {
{
.test_id = "mcc",
.test_post_init_f = test_init,
.test_tick_f = test_tick,
.test_main_f = test_main
},
BSTEST_END_MARKER
};
struct bst_test_list *test_mcc_install(struct bst_test_list *tests)
{
return bst_add_tests(tests, test_mcs);
}
#else
struct bst_test_list *test_mcc_install(struct bst_test_list *tests)
{
return tests;
}
#endif /* CONFIG_BT_MCC */