| /* |
| * Copyright (c) 2021 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifdef CONFIG_BT_MCS |
| |
| #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 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 uint32_t g_commands_supported; |
| static uint8_t g_search_control_point_result_code; |
| |
| CREATE_FLAG(ble_is_initialized); |
| CREATE_FLAG(ble_link_is_ready); |
| CREATE_FLAG(local_player_instance); |
| CREATE_FLAG(remote_player_instance); |
| CREATE_FLAG(player_name_read); |
| CREATE_FLAG(icon_object_id_read); |
| CREATE_FLAG(icon_url_read); |
| CREATE_FLAG(track_title_read); |
| CREATE_FLAG(track_duration_read); |
| CREATE_FLAG(track_position); |
| CREATE_FLAG(playback_speed); |
| CREATE_FLAG(seeking_speed_read); |
| CREATE_FLAG(track_segments_object_id_read); |
| CREATE_FLAG(current_track_object_id_read); |
| CREATE_FLAG(next_track_object_id_read); |
| CREATE_FLAG(parent_group_object_id_read); |
| CREATE_FLAG(current_group_object_id_read); |
| CREATE_FLAG(search_results_object_id_read); |
| CREATE_FLAG(playing_order_flag); |
| CREATE_FLAG(playing_orders_supported_read); |
| CREATE_FLAG(ccid_read); |
| CREATE_FLAG(media_state_read); |
| CREATE_FLAG(command_sent_flag); |
| CREATE_FLAG(command_results_flag); |
| CREATE_FLAG(commands_supported); |
| CREATE_FLAG(search_sent_flag); |
| CREATE_FLAG(search_result_code_flag); |
| |
| |
| static struct media_proxy_ctrl_cbs cbs; |
| static struct media_player *local_player; |
| static struct media_player *remote_player; |
| static struct media_player *current_player; |
| |
| static void local_player_instance_cb(struct media_player *player, int err) |
| { |
| if (err) { |
| FAIL("Local player instance failed (%d)", err); |
| return; |
| } |
| |
| local_player = player; |
| SET_FLAG(local_player_instance); |
| } |
| |
| static void discover_player_cb(struct media_player *player, int err) |
| { |
| if (err) { |
| FAIL("Discover player failed (%d)\n", err); |
| return; |
| } |
| |
| remote_player = player; |
| SET_FLAG(remote_player_instance); |
| } |
| |
| static void player_name_cb(struct media_player *plr, int err, const char *name) |
| { |
| if (err) { |
| FAIL("Player Name read failed (%d)\n", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(player_name_read); |
| } |
| |
| static void icon_id_cb(struct media_player *plr, int err, uint64_t id) |
| { |
| if (err) { |
| FAIL("Icon Object ID read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_icon_object_id = id; |
| SET_FLAG(icon_object_id_read); |
| } |
| |
| static void icon_url_cb(struct media_player *plr, int err, const char *url) |
| { |
| if (err) { |
| FAIL("Icon URL read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| } |
| |
| SET_FLAG(icon_url_read); |
| } |
| |
| static void track_title_cb(struct media_player *plr, int err, const char *title) |
| { |
| if (err) { |
| FAIL("Track title read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(track_title_read); |
| } |
| |
| static void track_duration_cb(struct media_player *plr, int err, int32_t duration) |
| { |
| if (err) { |
| FAIL("Track duration read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(track_duration_read); |
| } |
| |
| static void track_position_recv_cb(struct media_player *plr, int err, int32_t position) |
| { |
| if (err) { |
| FAIL("Track position read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_pos = position; |
| SET_FLAG(track_position); |
| } |
| |
| static void track_position_write_cb(struct media_player *plr, int err, int32_t position) |
| { |
| if (err) { |
| FAIL("Track position write failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_pos = position; |
| SET_FLAG(track_position); |
| } |
| |
| static void playback_speed_recv_cb(struct media_player *plr, int err, int8_t speed) |
| { |
| if (err) { |
| FAIL("Playback speed read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_pb_speed = speed; |
| SET_FLAG(playback_speed); |
| } |
| |
| static void playback_speed_write_cb(struct media_player *plr, int err, int8_t speed) |
| { |
| if (err) { |
| FAIL("Playback speed write failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_pb_speed = speed; |
| SET_FLAG(playback_speed); |
| } |
| |
| static void seeking_speed_cb(struct media_player *plr, int err, int8_t speed) |
| { |
| if (err) { |
| FAIL("Seeking speed read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(seeking_speed_read); |
| } |
| |
| static void track_segments_id_cb(struct media_player *plr, int err, uint64_t id) |
| { |
| if (err) { |
| FAIL("Track Segments ID read failed (%d)\n", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_track_segments_object_id = id; |
| SET_FLAG(track_segments_object_id_read); |
| } |
| |
| static void current_track_id_cb(struct media_player *plr, int err, uint64_t id) |
| { |
| if (err) { |
| FAIL("Current Track Object ID read failed (%d)\n", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_current_track_object_id = id; |
| SET_FLAG(current_track_object_id_read); |
| } |
| |
| static void next_track_id_cb(struct media_player *plr, int err, uint64_t id) |
| { |
| if (err) { |
| FAIL("Next Track Object ID read failed (%d)\n", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_next_track_object_id = id; |
| SET_FLAG(next_track_object_id_read); |
| } |
| |
| static void parent_group_id_cb(struct media_player *plr, int err, uint64_t id) |
| { |
| if (err) { |
| FAIL("Parent Group Object ID read failed (%d)\n", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_parent_group_object_id = id; |
| SET_FLAG(parent_group_object_id_read); |
| } |
| |
| static void current_group_id_cb(struct media_player *plr, int err, uint64_t id) |
| { |
| if (err) { |
| FAIL("Current Group Object ID read failed (%d)\n", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_current_group_object_id = id; |
| SET_FLAG(current_group_object_id_read); |
| } |
| |
| static void playing_order_recv_cb(struct media_player *plr, int err, uint8_t order) |
| { |
| if (err) { |
| FAIL("Playing order read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_playing_order = order; |
| SET_FLAG(playing_order_flag); |
| } |
| |
| static void playing_order_write_cb(struct media_player *plr, int err, uint8_t order) |
| { |
| if (err) { |
| FAIL("Playing order write failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_playing_order = order; |
| SET_FLAG(playing_order_flag); |
| } |
| |
| static void playing_orders_supported_cb(struct media_player *plr, int err, uint16_t orders) |
| { |
| if (err) { |
| FAIL("Playing orders supported read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(playing_orders_supported_read); |
| } |
| |
| static void media_state_cb(struct media_player *plr, int err, uint8_t state) |
| { |
| if (err) { |
| FAIL("Media State read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_state = state; |
| SET_FLAG(media_state_read); |
| } |
| |
| static void command_send_cb(struct media_player *plr, int err, const struct mpl_cmd *cmd) |
| { |
| if (err) { |
| FAIL("Command send failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(command_sent_flag); |
| } |
| |
| static void command_recv_cb(struct media_player *plr, int err, const struct mpl_cmd_ntf *cmd_ntf) |
| { |
| if (err) { |
| FAIL("Command failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_command_result = cmd_ntf->result_code; |
| SET_FLAG(command_results_flag); |
| } |
| |
| static void commands_supported_cb(struct media_player *plr, int err, uint32_t opcodes) |
| { |
| if (err) { |
| FAIL("Commands supported failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_commands_supported = opcodes; |
| SET_FLAG(commands_supported); |
| } |
| |
| |
| |
| static void search_send_cb(struct media_player *plr, int err, const struct mpl_search *search) |
| { |
| if (err) { |
| FAIL("Search failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(search_sent_flag); |
| } |
| |
| static void search_recv_cb(struct media_player *plr, int err, uint8_t result_code) |
| { |
| if (err) { |
| FAIL("Search failed (%d), result code: %u", err, result_code); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_search_control_point_result_code = result_code; |
| SET_FLAG(search_result_code_flag); |
| } |
| |
| static void search_results_id_cb(struct media_player *plr, int err, uint64_t id) |
| { |
| if (err) { |
| FAIL("Search Results Object ID read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| g_search_results_object_id = id; |
| SET_FLAG(search_results_object_id_read); |
| } |
| |
| static void content_ctrl_id_cb(struct media_player *plr, int err, uint8_t ccid) |
| { |
| if (err) { |
| FAIL("Content control ID read failed (%d)", err); |
| return; |
| } |
| |
| if (plr != current_player) { |
| FAIL("Wrong player\n"); |
| return; |
| } |
| |
| SET_FLAG(ccid_read); |
| } |
| |
| void initialize_media(void) |
| { |
| int err = media_proxy_pl_init(); /* TODO: Fix direct call to player */ |
| |
| if (err) { |
| FAIL("Could not init mpl: %d", err); |
| return; |
| } |
| |
| /* Set up the callback structure */ |
| cbs.local_player_instance = local_player_instance_cb; |
| cbs.discover_player = discover_player_cb; |
| cbs.player_name_recv = player_name_cb; |
| cbs.icon_id_recv = icon_id_cb; |
| cbs.icon_url_recv = icon_url_cb; |
| cbs.track_title_recv = track_title_cb; |
| cbs.track_duration_recv = track_duration_cb; |
| cbs.track_position_recv = track_position_recv_cb; |
| cbs.track_position_write = track_position_write_cb; |
| cbs.playback_speed_recv = playback_speed_recv_cb; |
| cbs.playback_speed_write = playback_speed_write_cb; |
| cbs.seeking_speed_recv = seeking_speed_cb; |
| #ifdef CONFIG_BT_OTS |
| cbs.track_segments_id_recv = track_segments_id_cb; |
| cbs.current_track_id_recv = current_track_id_cb; |
| cbs.next_track_id_recv = next_track_id_cb; |
| cbs.parent_group_id_recv = parent_group_id_cb; |
| cbs.current_group_id_recv = current_group_id_cb; |
| #endif /* CONFIG_BT_OTS */ |
| cbs.playing_order_recv = playing_order_recv_cb; |
| cbs.playing_order_write = playing_order_write_cb; |
| cbs.playing_orders_supported_recv = playing_orders_supported_cb; |
| cbs.media_state_recv = media_state_cb; |
| cbs.command_send = command_send_cb; |
| cbs.command_recv = command_recv_cb; |
| cbs.commands_supported_recv = commands_supported_cb; |
| #ifdef CONFIG_BT_OTS |
| cbs.search_send = search_send_cb; |
| cbs.search_recv = search_recv_cb; |
| cbs.search_results_id_recv = search_results_id_cb; |
| #endif /* CONFIG_BT_OTS */ |
| cbs.content_ctrl_id_recv = content_ctrl_id_cb; |
| |
| UNSET_FLAG(local_player_instance); |
| |
| err = media_proxy_ctrl_register(&cbs); |
| if (err) { |
| FAIL("Could not init mpl: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(local_player_instance); |
| printk("media init and local player instance succeeded\n"); |
| } |
| |
| /* 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); |
| } |
| |
| 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) { |
| FAIL("Failed to connect to %s (%u)\n", addr, err); |
| return; |
| } |
| |
| printk("Connected: %s\n", addr); |
| SET_FLAG(ble_link_is_ready); |
| } |
| |
| /* 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 = media_proxy_ctrl_get_media_state(current_player); |
| 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; |
| |
| UNSET_FLAG(command_sent_flag); |
| UNSET_FLAG(command_results_flag); |
| err = media_proxy_ctrl_send_command(current_player, cmd); |
| if (err) { |
| FAIL("Failed to send command: %d, opcode: %u", |
| err, cmd->opcode); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(command_sent_flag); |
| WAIT_FOR_FLAG(command_results_flag); |
| } |
| |
| static void test_cp_play(void) |
| { |
| struct mpl_cmd cmd; |
| |
| cmd.opcode = MEDIA_PROXY_OP_PLAY; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_SUCCESS) { |
| FAIL("PLAY command failed\n"); |
| return; |
| } |
| |
| if (test_verify_media_state_wait_flags(MEDIA_PROXY_STATE_PLAYING)) { |
| printk("PLAY command succeeded\n"); |
| } |
| } |
| |
| static void test_cp_pause(void) |
| { |
| struct mpl_cmd cmd; |
| |
| cmd.opcode = MEDIA_PROXY_OP_PAUSE; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_SUCCESS) { |
| FAIL("PAUSE command failed\n"); |
| return; |
| } |
| |
| if (test_verify_media_state_wait_flags(MEDIA_PROXY_STATE_PAUSED)) { |
| printk("PAUSE command succeeded\n"); |
| } |
| } |
| |
| static void test_cp_fast_rewind(void) |
| { |
| struct mpl_cmd cmd; |
| |
| cmd.opcode = MEDIA_PROXY_OP_FAST_REWIND; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_SUCCESS) { |
| FAIL("FAST REWIND command failed\n"); |
| return; |
| } |
| |
| if (test_verify_media_state_wait_flags(MEDIA_PROXY_STATE_SEEKING)) { |
| printk("FAST REWIND command succeeded\n"); |
| } |
| } |
| |
| static void test_cp_fast_forward(void) |
| { |
| struct mpl_cmd cmd; |
| |
| cmd.opcode = MEDIA_PROXY_OP_FAST_FORWARD; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_SUCCESS) { |
| FAIL("FAST FORWARD command failed\n"); |
| return; |
| } |
| |
| if (test_verify_media_state_wait_flags(MEDIA_PROXY_STATE_SEEKING)) { |
| printk("FAST FORWARD command succeeded\n"); |
| } |
| } |
| |
| static void test_cp_stop(void) |
| { |
| struct mpl_cmd cmd; |
| |
| cmd.opcode = MEDIA_PROXY_OP_STOP; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_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(MEDIA_PROXY_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); |
| err = media_proxy_ctrl_get_track_position(current_player); |
| if (err) { |
| FAIL("Failed to read track position: %d\n", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(track_position); |
| uint32_t tmp_pos = g_pos; |
| |
| cmd.opcode = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_SUCCESS) { |
| FAIL("MOVE RELATIVE command failed\n"); |
| return; |
| } |
| |
| UNSET_FLAG(track_position); |
| err = media_proxy_ctrl_get_track_position(current_player); |
| if (err) { |
| FAIL("Failed to read track position: %d\n", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(track_position); |
| 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 = MEDIA_PROXY_OP_PREV_SEGMENT; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_NEXT_SEGMENT; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_FIRST_SEGMENT; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_LAST_SEGMENT; |
| cmd.use_param = false; |
| |
| test_send_cmd_wait_flags(&cmd); |
| |
| if (g_command_result != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = media_proxy_ctrl_get_current_track_id(current_player); |
| 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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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(void) |
| { |
| uint64_t object_id; |
| struct mpl_cmd cmd; |
| |
| cmd.opcode = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_SUCCESS) { |
| FAIL("NEXT 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("NEXT TRACK command succeeded\n"); |
| } |
| |
| static void test_cp_first_track(void) |
| { |
| uint64_t object_id; |
| struct mpl_cmd cmd; |
| |
| cmd.opcode = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = media_proxy_ctrl_get_current_group_id(current_player); |
| 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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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 = MEDIA_PROXY_OP_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 != MEDIA_PROXY_CMD_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_scp(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 = media_proxy_ctrl_get_search_results_id(current_player); |
| |
| 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 = MEDIA_PROXY_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_flag); |
| UNSET_FLAG(search_result_code_flag); |
| UNSET_FLAG(search_results_object_id_read); |
| |
| err = media_proxy_ctrl_send_search(current_player, &search); |
| if (err) { |
| FAIL("Failed to write to search control point\n"); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(search_sent_flag); |
| WAIT_FOR_FLAG(search_result_code_flag); |
| |
| if (g_search_control_point_result_code != MEDIA_PROXY_SEARCH_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 for the provided player. |
| * (Works by setting the provided player as the "current player".) |
| * |
| * The order of the sequence follows the order of the characterstics in the |
| * Media Control Service specification |
| */ |
| void test_media_controller_player(struct media_player *player) |
| { |
| current_player = player; |
| int err; |
| |
| UNSET_FLAG(player_name_read); |
| err = media_proxy_ctrl_get_player_name(current_player); |
| 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 id ******************************************/ |
| UNSET_FLAG(icon_object_id_read); |
| err = media_proxy_ctrl_get_icon_id(current_player); |
| 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"); |
| |
| /* Read icon url *************************************************/ |
| UNSET_FLAG(icon_url_read); |
| err = media_proxy_ctrl_get_icon_url(current_player); |
| if (err) { |
| FAIL("Failed to read icon url: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(icon_url_read); |
| printk("Icon URL read succeeded\n"); |
| |
| /* Read track_title ******************************************/ |
| UNSET_FLAG(track_title_read); |
| err = media_proxy_ctrl_get_track_title(current_player); |
| 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 = media_proxy_ctrl_get_track_duration(current_player); |
| 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); |
| err = media_proxy_ctrl_get_track_position(current_player); |
| if (err) { |
| FAIL("Failed to read track position: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(track_position); |
| printk("Track position read succeeded\n"); |
| |
| int32_t pos = g_pos + 1200; /*12 seconds further into the track */ |
| |
| UNSET_FLAG(track_position); |
| err = media_proxy_ctrl_set_track_position(current_player, pos); |
| if (err) { |
| FAIL("Failed to set track position: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(track_position); |
| 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); |
| err = media_proxy_ctrl_get_playback_speed(current_player); |
| if (err) { |
| FAIL("Failed to read playback speed: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(playback_speed); |
| 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); |
| err = media_proxy_ctrl_set_playback_speed(current_player, pb_speed); |
| if (err) { |
| FAIL("Failed to set playback speed: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(playback_speed); |
| 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 = media_proxy_ctrl_get_seeking_speed(current_player); |
| 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 = media_proxy_ctrl_get_track_segments_id(current_player); |
| 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"); |
| |
| /* Read current track object ******************************************/ |
| UNSET_FLAG(current_track_object_id_read); |
| err = media_proxy_ctrl_get_current_track_id(current_player); |
| if (err) { |
| FAIL("Failed to read current track object ID: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(current_track_object_id_read); |
| printk("Current Track Object ID read succeeded\n"); |
| |
| /* Read next track object ******************************************/ |
| UNSET_FLAG(next_track_object_id_read); |
| err = media_proxy_ctrl_get_next_track_id(current_player); |
| if (err) { |
| FAIL("Failed to read next track object ID: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(next_track_object_id_read); |
| printk("Next Track Object ID read succeeded\n"); |
| |
| /* Read parent group object ******************************************/ |
| UNSET_FLAG(parent_group_object_id_read); |
| err = media_proxy_ctrl_get_parent_group_id(current_player); |
| 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"); |
| |
| /* Read current group object ******************************************/ |
| UNSET_FLAG(current_group_object_id_read); |
| err = media_proxy_ctrl_get_current_group_id(current_player); |
| if (err) { |
| FAIL("Failed to read current group object ID: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(current_group_object_id_read); |
| printk("Current Group Object ID read succeeded\n"); |
| |
| /* Read and set playing order *************************************/ |
| UNSET_FLAG(playing_order_flag); |
| err = media_proxy_ctrl_get_playing_order(current_player); |
| if (err) { |
| FAIL("Failed to read playing order: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(playing_order_flag); |
| printk("Playing order read succeeded\n"); |
| |
| uint8_t playing_order; |
| |
| if (g_playing_order != MEDIA_PROXY_PLAYING_ORDER_INORDER_ONCE) { |
| playing_order = MEDIA_PROXY_PLAYING_ORDER_INORDER_ONCE; |
| } else { |
| playing_order = MEDIA_PROXY_PLAYING_ORDER_INORDER_REPEAT; |
| } |
| |
| UNSET_FLAG(playing_order_flag); |
| err = media_proxy_ctrl_set_playing_order(current_player, playing_order); |
| if (err) { |
| FAIL("Failed to set playing_order: %d", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(playing_order_flag); |
| 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 = media_proxy_ctrl_get_playing_orders_supported(current_player); |
| 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 = media_proxy_ctrl_get_media_state(current_player); |
| 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 = media_proxy_ctrl_get_content_ctrl_id(current_player); |
| 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(MEDIA_PROXY_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(); |
| 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_scp(); |
| } |
| |
| void initialize_bluetooth(void) |
| { |
| int err; |
| |
| 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"); |
| } |
| |
| void scan_and_connect(void) |
| { |
| char addr[BT_ADDR_LE_STR_LEN]; |
| int err; |
| |
| 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); |
| return; |
| } |
| |
| printk("Scanning started successfully\n"); |
| |
| WAIT_FOR_FLAG(ble_link_is_ready); |
| |
| |
| bt_addr_le_to_str(bt_conn_get_dst(default_conn), addr, sizeof(addr)); |
| printk("Connected: %s\n", addr); |
| } |
| |
| void discover_remote_player(void) |
| { |
| int err; |
| |
| UNSET_FLAG(remote_player_instance); |
| err = media_proxy_ctrl_discover_player(default_conn); |
| if (err) { |
| FAIL("Remote player discovery failed (err %d)\n", err); |
| return; |
| } |
| |
| WAIT_FOR_FLAG(remote_player_instance); |
| } |
| |
| /* BabbleSim entry point for local player test */ |
| void test_media_controller_local_player(void) |
| { |
| printk("Media Control local player test application. Board: %s\n", CONFIG_BOARD); |
| |
| initialize_bluetooth(); |
| initialize_media(); /* Sets local_player global variable */ |
| |
| printk("Local player instance: %p\n", local_player); |
| |
| test_media_controller_player(local_player); |
| |
| /* TEST IS COMPLETE */ |
| PASS("Test media_controller_local_player passed\n"); |
| } |
| |
| /* BabbleSim entry point for remote player test */ |
| void test_media_controller_remote_player(void) |
| { |
| int err; |
| static struct bt_conn_cb conn_callbacks = { |
| .connected = connected, |
| .disconnected = disconnected, |
| }; |
| |
| printk("Media Control remote player test application. Board: %s\n", CONFIG_BOARD); |
| |
| bt_conn_cb_register(&conn_callbacks); |
| |
| initialize_bluetooth(); |
| initialize_media(); |
| |
| UNSET_FLAG(ble_link_is_ready); |
| err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, AD_SIZE, NULL, 0); |
| if (err) { |
| FAIL("Advertising failed to start (err %d)\n", err); |
| } |
| WAIT_FOR_FLAG(ble_link_is_ready); |
| |
| discover_remote_player(); /* Sets global variable */ |
| printk("Remote player instance: %p\n", remote_player); |
| |
| test_media_controller_player(remote_player); |
| |
| /* TEST IS COMPLETE */ |
| PASS("Test media_controller_remote_player passed\n"); |
| } |
| |
| /* BabbleSim entry point for server for remote player test */ |
| void test_media_controller_server(void) |
| { |
| static struct bt_conn_cb conn_callbacks = { |
| .connected = connected, |
| .disconnected = disconnected, |
| }; |
| |
| printk("Media Control server test application. Board: %s\n", CONFIG_BOARD); |
| |
| bt_conn_cb_register(&conn_callbacks); |
| |
| initialize_bluetooth(); |
| initialize_media(); |
| |
| /* The server side will also get callbacks, from its local player. |
| * And if the current player is not set, the callbacks will fail the test. |
| */ |
| printk("Local player instance: %p\n", local_player); |
| current_player = local_player; |
| |
| scan_and_connect(); |
| |
| /* TEST IS COMPLETE */ |
| PASS("Test media_controller_server passed\n"); |
| } |
| |
| static const struct bst_test_instance test_media_controller[] = { |
| { |
| .test_id = "media_controller_local_player", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_media_controller_local_player |
| }, |
| { |
| .test_id = "media_controller_remote_player", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_media_controller_remote_player |
| }, |
| { |
| .test_id = "media_controller_server", |
| .test_post_init_f = test_init, |
| .test_tick_f = test_tick, |
| .test_main_f = test_media_controller_server |
| }, |
| BSTEST_END_MARKER |
| }; |
| |
| struct bst_test_list *test_media_controller_install(struct bst_test_list *tests) |
| { |
| return bst_add_tests(tests, test_media_controller); |
| } |
| |
| #else |
| |
| struct bst_test_list *test_media_controller_install(struct bst_test_list *tests) |
| { |
| return tests; |
| } |
| |
| #endif /* CONFIG_BT_MCS */ |