| /* btp_mcp.c - Bluetooth MCP Tester */ |
| |
| /* |
| * Copyright (c) 2023 Codecoup |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <stddef.h> |
| #include <errno.h> |
| #include <stdio.h> |
| |
| #include <zephyr/types.h> |
| #include <zephyr/kernel.h> |
| |
| #include <zephyr/bluetooth/bluetooth.h> |
| #include <zephyr/bluetooth/audio/audio.h> |
| #include <zephyr/bluetooth/audio/mcc.h> |
| #include <zephyr/bluetooth/audio/mcs.h> |
| #include <../../subsys/bluetooth/audio/mpl_internal.h> |
| #include <../../subsys/bluetooth/audio/mcc_internal.h> |
| #include <zephyr/bluetooth/services/ots.h> |
| #include <zephyr/bluetooth/audio/media_proxy.h> |
| |
| #include <zephyr/logging/log.h> |
| #include <zephyr/sys/byteorder.h> |
| |
| #include "bap_endpoint.h" |
| #include "btp/btp.h" |
| #include "../../../../include/zephyr/bluetooth/audio/media_proxy.h" |
| |
| #define LOG_MODULE_NAME bttester_mcp |
| LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL); |
| |
| static struct media_player *mcs_media_player; |
| static uint64_t current_track_obj_id; |
| static uint64_t next_track_obj_id; |
| static uint8_t media_player_state; |
| static uint64_t current_id; |
| static uint64_t parent_id; |
| struct service_handles { |
| struct { |
| uint16_t player_name; |
| uint16_t icon_obj_id; |
| uint16_t icon_url; |
| uint16_t track_changed; |
| uint16_t track_title; |
| uint16_t track_duration; |
| uint16_t track_position; |
| uint16_t playback_speed; |
| uint16_t seeking_speed; |
| uint16_t segments_obj_id; |
| uint16_t current_track_obj_id; |
| uint16_t next_track_obj_id; |
| uint16_t current_group_obj_id; |
| uint16_t parent_group_obj_id; |
| uint16_t playing_order; |
| uint16_t playing_orders_supported; |
| uint16_t media_state; |
| uint16_t cp; |
| uint16_t opcodes_supported; |
| uint16_t search_results_obj_id; |
| uint16_t scp; |
| uint16_t content_control_id; |
| } gmcs_handles; |
| |
| struct { |
| uint16_t feature; |
| uint16_t obj_name; |
| uint16_t obj_type; |
| uint16_t obj_size; |
| uint16_t obj_properties; |
| uint16_t obj_created; |
| uint16_t obj_modified; |
| uint16_t obj_id; |
| uint16_t oacp; |
| uint16_t olcp; |
| } ots_handles; |
| }; |
| |
| struct service_handles svc_chrc_handles; |
| |
| #define SEARCH_LEN_MAX 64 |
| |
| static struct net_buf_simple *rx_ev_buf = NET_BUF_SIMPLE(SEARCH_LEN_MAX + |
| sizeof(struct btp_mcp_search_cp_ev)); |
| |
| /* Media Control Profile */ |
| static void btp_send_mcp_found_ev(struct bt_conn *conn, uint8_t status, |
| const struct service_handles svc_chrc_handles) |
| { |
| struct btp_mcp_discovered_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.gmcs_handles.player_name = sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.player_name); |
| ev.gmcs_handles.icon_obj_id = sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.icon_obj_id); |
| ev.gmcs_handles.icon_url = sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.icon_url); |
| ev.gmcs_handles.track_changed = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.track_changed); |
| ev.gmcs_handles.track_title = sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.track_title); |
| ev.gmcs_handles.track_duration = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.track_duration); |
| ev.gmcs_handles.track_position = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.track_position); |
| ev.gmcs_handles.playback_speed = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.playback_speed); |
| ev.gmcs_handles.seeking_speed = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.seeking_speed); |
| ev.gmcs_handles.segments_obj_id = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.segments_obj_id); |
| ev.gmcs_handles.current_track_obj_id = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.current_track_obj_id); |
| ev.gmcs_handles.next_track_obj_id = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.next_track_obj_id); |
| ev.gmcs_handles.current_group_obj_id = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.current_group_obj_id); |
| ev.gmcs_handles.parent_group_obj_id = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.parent_group_obj_id); |
| ev.gmcs_handles.playing_order = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.playing_order); |
| ev.gmcs_handles.playing_orders_supported = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.playing_orders_supported); |
| ev.gmcs_handles.media_state = sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.media_state); |
| ev.gmcs_handles.cp = sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.cp); |
| ev.gmcs_handles.opcodes_supported = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.opcodes_supported); |
| ev.gmcs_handles.search_results_obj_id = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.search_results_obj_id); |
| ev.gmcs_handles.scp = sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.scp); |
| ev.gmcs_handles.content_control_id = |
| sys_cpu_to_le16(svc_chrc_handles.gmcs_handles.content_control_id); |
| ev.ots_handles.feature = sys_cpu_to_le16(svc_chrc_handles.ots_handles.feature); |
| ev.ots_handles.obj_name = sys_cpu_to_le16(svc_chrc_handles.ots_handles.obj_name); |
| ev.ots_handles.obj_type = sys_cpu_to_le16(svc_chrc_handles.ots_handles.obj_type); |
| ev.ots_handles.obj_size = sys_cpu_to_le16(svc_chrc_handles.ots_handles.obj_size); |
| ev.ots_handles.obj_properties = |
| sys_cpu_to_le16(svc_chrc_handles.ots_handles.obj_properties); |
| ev.ots_handles.obj_created = sys_cpu_to_le16(svc_chrc_handles.ots_handles.obj_created); |
| ev.ots_handles.obj_modified = sys_cpu_to_le16(svc_chrc_handles.ots_handles.obj_modified); |
| ev.ots_handles.obj_id = sys_cpu_to_le16(svc_chrc_handles.ots_handles.obj_id); |
| ev.ots_handles.oacp = sys_cpu_to_le16(svc_chrc_handles.ots_handles.oacp); |
| ev.ots_handles.olcp = sys_cpu_to_le16(svc_chrc_handles.ots_handles.olcp); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_DISCOVERED_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_track_duration_ev(struct bt_conn *conn, uint8_t status, int32_t dur) |
| { |
| struct btp_mcp_track_duration_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.dur = sys_cpu_to_le32(dur); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_TRACK_DURATION_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_track_position_ev(struct bt_conn *conn, uint8_t status, int32_t pos) |
| { |
| struct btp_mcp_track_position_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.pos = sys_cpu_to_le32(pos); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_TRACK_POSITION_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_playback_speed_ev(struct bt_conn *conn, uint8_t status, int8_t speed) |
| { |
| struct btp_mcp_playback_speed_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.speed = speed; |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_PLAYBACK_SPEED_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_seeking_speed_ev(struct bt_conn *conn, uint8_t status, int8_t speed) |
| { |
| struct btp_mcp_seeking_speed_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.speed = speed; |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_SEEKING_SPEED_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_icon_obj_id_ev(struct bt_conn *conn, uint8_t status, uint64_t id) |
| { |
| struct btp_mcp_icon_obj_id_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| sys_put_le48(id, ev.id); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_ICON_OBJ_ID_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_next_track_obj_id_ev(struct bt_conn *conn, uint8_t status, |
| uint64_t id) |
| { |
| struct btp_mcp_next_track_obj_id_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| sys_put_le48(id, ev.id); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_NEXT_TRACK_OBJ_ID_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_parent_group_obj_id_ev(struct bt_conn *conn, uint8_t status, uint64_t id) |
| { |
| struct btp_mcp_parent_group_obj_id_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| sys_put_le48(id, ev.id); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_PARENT_GROUP_OBJ_ID_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_current_group_obj_id_ev(struct bt_conn *conn, uint8_t status, uint64_t id) |
| { |
| struct btp_mcp_current_group_obj_id_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| sys_put_le48(id, ev.id); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_CURRENT_GROUP_OBJ_ID_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_playing_order_ev(struct bt_conn *conn, uint8_t status, uint8_t order) |
| { |
| struct btp_mcp_playing_order_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.order = order; |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_PLAYING_ORDER_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_playing_orders_supported_ev(struct bt_conn *conn, uint8_t status, |
| uint16_t orders) |
| { |
| struct btp_mcp_playing_orders_supported_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.orders = sys_cpu_to_le16(orders); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_PLAYING_ORDERS_SUPPORTED_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_media_state_ev(struct bt_conn *conn, uint8_t status, uint8_t state) |
| { |
| struct btp_mcp_media_state_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.state = state; |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_MEDIA_STATE_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_opcodes_supported_ev(struct bt_conn *conn, uint8_t status, |
| uint32_t opcodes) |
| { |
| struct btp_mcp_opcodes_supported_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.opcodes = sys_cpu_to_le32(opcodes); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_OPCODES_SUPPORTED_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_mcp_content_control_id_ev(struct bt_conn *conn, uint8_t status, |
| uint8_t ccid) |
| { |
| struct btp_mcp_content_control_id_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.ccid = ccid; |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_CONTENT_CONTROL_ID_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_segments_obj_id_ev(struct bt_conn *conn, uint8_t status, uint64_t id) |
| { |
| struct btp_mcp_segments_obj_id_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| sys_put_le48(id, ev.id); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_SEGMENTS_OBJ_ID_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_current_track_obj_id_ev(struct bt_conn *conn, uint8_t status, uint64_t id) |
| { |
| struct btp_mcp_current_track_obj_id_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| sys_put_le48(id, ev.id); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_CURRENT_TRACK_OBJ_ID_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_media_cp_ev(struct bt_conn *conn, uint8_t status, |
| const struct mpl_cmd *cmd) |
| { |
| struct btp_mcp_media_cp_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.opcode = cmd->opcode; |
| ev.use_param = cmd->use_param; |
| ev.param = sys_cpu_to_le32(cmd->param); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_MEDIA_CP_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_search_cp_ev(struct bt_conn *conn, uint8_t status, |
| const struct mpl_search *search) |
| { |
| struct btp_mcp_search_cp_ev *ev; |
| uint8_t param[SEARCH_LEN_MAX]; |
| |
| net_buf_simple_init(rx_ev_buf, 0); |
| |
| ev = net_buf_simple_add(rx_ev_buf, sizeof(*ev)); |
| |
| bt_addr_le_copy(&ev->address, bt_conn_get_dst(conn)); |
| |
| ev->status = status; |
| ev->param_len = (uint8_t)search->search[0]; |
| |
| if (ev->param_len > (SEARCH_LEN_MAX - sizeof(ev->param_len))) { |
| return; |
| } |
| |
| ev->search_type = search->search[1]; |
| strcpy(param, &search->search[2]); |
| net_buf_simple_add_mem(rx_ev_buf, param, ev->param_len); |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_SEARCH_CP_EV, ev, sizeof(*ev) + ev->param_len); |
| } |
| |
| static void btp_send_command_notifications_ev(struct bt_conn *conn, uint8_t status, |
| const struct mpl_cmd_ntf *ntf) |
| { |
| struct btp_mcp_cmd_ntf_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.requested_opcode = ntf->requested_opcode; |
| ev.result_code = ntf->result_code; |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_MCP_NTF_EV, &ev, sizeof(ev)); |
| } |
| |
| static void btp_send_search_notifications_ev(struct bt_conn *conn, uint8_t status, |
| uint8_t result_code) |
| { |
| struct btp_scp_cmd_ntf_ev ev; |
| |
| bt_addr_le_copy(&ev.address, bt_conn_get_dst(conn)); |
| |
| ev.status = status; |
| ev.result_code = result_code; |
| |
| tester_event(BTP_SERVICE_ID_MCP, BTP_SCP_NTF_EV, &ev, sizeof(ev)); |
| } |
| |
| static void mcc_discover_cb(struct bt_conn *conn, int err) |
| { |
| struct mcs_instance_t *mcc_inst; |
| |
| if (err) { |
| LOG_DBG("Discovery failed (%d)", err); |
| } |
| |
| mcc_inst = lookup_inst_by_conn(conn); |
| |
| svc_chrc_handles.gmcs_handles.player_name = mcc_inst->player_name_handle; |
| svc_chrc_handles.gmcs_handles.icon_obj_id = mcc_inst->icon_obj_id_handle; |
| svc_chrc_handles.gmcs_handles.icon_url = mcc_inst->icon_url_handle; |
| svc_chrc_handles.gmcs_handles.track_changed = mcc_inst->track_changed_handle; |
| svc_chrc_handles.gmcs_handles.track_title = mcc_inst->track_title_handle; |
| svc_chrc_handles.gmcs_handles.track_duration = mcc_inst->track_duration_handle; |
| svc_chrc_handles.gmcs_handles.track_position = mcc_inst->track_position_handle; |
| svc_chrc_handles.gmcs_handles.playback_speed = mcc_inst->playback_speed_handle; |
| svc_chrc_handles.gmcs_handles.seeking_speed = mcc_inst->seeking_speed_handle; |
| svc_chrc_handles.gmcs_handles.segments_obj_id = mcc_inst->segments_obj_id_handle; |
| svc_chrc_handles.gmcs_handles.current_track_obj_id = mcc_inst->current_track_obj_id_handle; |
| svc_chrc_handles.gmcs_handles.next_track_obj_id = mcc_inst->next_track_obj_id_handle; |
| svc_chrc_handles.gmcs_handles.current_group_obj_id = mcc_inst->current_group_obj_id_handle; |
| svc_chrc_handles.gmcs_handles.parent_group_obj_id = mcc_inst->parent_group_obj_id_handle; |
| svc_chrc_handles.gmcs_handles.playing_order = mcc_inst->playing_order_handle; |
| svc_chrc_handles.gmcs_handles.playing_orders_supported = |
| mcc_inst->playing_orders_supported_handle; |
| svc_chrc_handles.gmcs_handles.media_state = mcc_inst->media_state_handle; |
| svc_chrc_handles.gmcs_handles.cp = mcc_inst->cp_handle; |
| svc_chrc_handles.gmcs_handles.opcodes_supported = mcc_inst->opcodes_supported_handle; |
| svc_chrc_handles.gmcs_handles.search_results_obj_id = |
| mcc_inst->search_results_obj_id_handle; |
| svc_chrc_handles.gmcs_handles.scp = mcc_inst->scp_handle; |
| svc_chrc_handles.gmcs_handles.content_control_id = mcc_inst->content_control_id_handle; |
| svc_chrc_handles.ots_handles.feature = mcc_inst->otc.feature_handle; |
| svc_chrc_handles.ots_handles.obj_name = mcc_inst->otc.obj_name_handle; |
| svc_chrc_handles.ots_handles.obj_type = mcc_inst->otc.obj_type_handle; |
| svc_chrc_handles.ots_handles.obj_size = mcc_inst->otc.obj_size_handle; |
| svc_chrc_handles.ots_handles.obj_id = mcc_inst->otc.obj_id_handle; |
| svc_chrc_handles.ots_handles.obj_properties = mcc_inst->otc.obj_properties_handle; |
| svc_chrc_handles.ots_handles.obj_created = mcc_inst->otc.obj_created_handle; |
| svc_chrc_handles.ots_handles.obj_modified = mcc_inst->otc.obj_modified_handle; |
| svc_chrc_handles.ots_handles.oacp = mcc_inst->otc.oacp_handle; |
| svc_chrc_handles.ots_handles.olcp = mcc_inst->otc.olcp_handle; |
| |
| btp_send_mcp_found_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, svc_chrc_handles); |
| } |
| |
| static void mcc_read_track_duration_cb(struct bt_conn *conn, int err, int32_t dur) |
| { |
| LOG_DBG("MCC Read track duration cb (%d)", err); |
| |
| btp_send_mcp_track_duration_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, dur); |
| } |
| |
| static void mcc_read_track_position_cb(struct bt_conn *conn, int err, int32_t pos) |
| { |
| LOG_DBG("MCC Read track position cb (%d)", err); |
| |
| btp_send_mcp_track_position_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, pos); |
| } |
| |
| static void mcc_set_track_position_cb(struct bt_conn *conn, int err, int32_t pos) |
| { |
| LOG_DBG("MCC Set track position cb (%d)", err); |
| |
| btp_send_mcp_track_position_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, pos); |
| } |
| |
| static void mcc_read_playback_speed_cb(struct bt_conn *conn, int err, int8_t speed) |
| { |
| LOG_DBG("MCC read playback speed cb (%d)", err); |
| |
| btp_send_mcp_playback_speed_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, speed); |
| } |
| |
| static void mcc_set_playback_speed_cb(struct bt_conn *conn, int err, int8_t speed) |
| { |
| LOG_DBG("MCC set playback speed cb (%d)", err); |
| |
| btp_send_mcp_playback_speed_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, speed); |
| } |
| |
| static void mcc_read_seeking_speed_cb(struct bt_conn *conn, int err, int8_t speed) |
| { |
| LOG_DBG("MCC read seeking speed cb (%d)", err); |
| |
| btp_send_mcp_seeking_speed_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, speed); |
| } |
| |
| static void mcc_read_icon_obj_id_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC read Icon Object ID cb (%d)", err); |
| |
| btp_send_mcp_icon_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_read_next_track_obj_id_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC read next track obj ID cb (%d)", err); |
| |
| btp_send_mcp_next_track_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_set_next_track_obj_id_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC set next track obj ID cb (%d)", err); |
| |
| btp_send_mcp_next_track_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_read_parent_group_obj_id_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC read parent group obj ID cb (%d)", err); |
| |
| btp_send_parent_group_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_read_current_group_obj_id_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC read current group obj ID cb (%d)", err); |
| |
| btp_send_current_group_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_set_current_group_obj_id_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC read current group obj ID cb (%d)", err); |
| |
| btp_send_current_group_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_read_playing_order_cb(struct bt_conn *conn, int err, uint8_t order) |
| { |
| LOG_DBG("MCC read playing order cb (%d)", err); |
| |
| btp_send_mcp_playing_order_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, order); |
| } |
| |
| static void mcc_set_playing_order_cb(struct bt_conn *conn, int err, uint8_t order) |
| { |
| LOG_DBG("MCC set playing order cb (%d)", err); |
| |
| btp_send_mcp_playing_order_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, order); |
| } |
| |
| static void mcc_read_playing_orders_supported_cb(struct bt_conn *conn, int err, uint16_t orders) |
| { |
| LOG_DBG("MCC set playing order cb (%d)", err); |
| |
| btp_send_mcp_playing_orders_supported_ev(conn, |
| err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, |
| orders); |
| } |
| |
| static void mcc_media_state_read_cb(struct bt_conn *conn, int err, uint8_t state) |
| { |
| LOG_DBG("MCC media state read cb (%d)", err); |
| |
| btp_send_mcp_media_state_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, state); |
| } |
| |
| static void mcc_opcodes_supported_cb(struct bt_conn *conn, int err, uint32_t opcodes) |
| { |
| LOG_DBG("MCC opcodes supported cb (%d)", err); |
| |
| btp_send_mcp_opcodes_supported_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, |
| opcodes); |
| } |
| |
| static void mcc_content_control_id_cb(struct bt_conn *conn, int err, uint8_t ccid) |
| { |
| LOG_DBG("MCC Content control ID cb (%d)", err); |
| |
| btp_send_mcp_content_control_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, |
| ccid); |
| } |
| |
| static void mcc_segments_object_id_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC Segments Object ID cb (%d)", err); |
| |
| btp_send_segments_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_current_track_obj_id_read_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC Segments Object ID read cb (%d)", err); |
| |
| btp_send_current_track_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_current_track_obj_id_set_cb(struct bt_conn *conn, int err, uint64_t id) |
| { |
| LOG_DBG("MCC Segments Object ID set cb (%d)", err); |
| |
| btp_send_current_track_obj_id_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, id); |
| } |
| |
| static void mcc_send_cmd_cb(struct bt_conn *conn, int err, const struct mpl_cmd *cmd) |
| { |
| LOG_DBG("MCC Send Command cb (%d)", err); |
| |
| btp_send_media_cp_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, cmd); |
| } |
| |
| static void mcc_send_search_cb(struct bt_conn *conn, int err, const struct mpl_search *search) |
| { |
| LOG_DBG("MCC Send Search cb (%d)", err); |
| |
| btp_send_search_cp_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, search); |
| } |
| |
| static void mcc_cmd_ntf_cb(struct bt_conn *conn, int err, const struct mpl_cmd_ntf *ntf) |
| { |
| LOG_DBG("MCC Media Control Point Command Notify cb (%d)", err); |
| |
| btp_send_command_notifications_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, ntf); |
| } |
| |
| static void mcc_search_ntf_cb(struct bt_conn *conn, int err, uint8_t result_code) |
| { |
| LOG_DBG("MCC Search Control Point Notify cb (%d)", err); |
| |
| btp_send_search_notifications_ev(conn, err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS, |
| result_code); |
| } |
| |
| static struct bt_mcc_cb mcp_cb = { |
| .discover_mcs = mcc_discover_cb, |
| .read_track_duration = mcc_read_track_duration_cb, |
| .read_track_position = mcc_read_track_position_cb, |
| .set_track_position = mcc_set_track_position_cb, |
| .read_playback_speed = mcc_read_playback_speed_cb, |
| .set_playback_speed = mcc_set_playback_speed_cb, |
| .read_seeking_speed = mcc_read_seeking_speed_cb, |
| .read_playing_order = mcc_read_playing_order_cb, |
| .set_playing_order = mcc_set_playing_order_cb, |
| .read_playing_orders_supported = mcc_read_playing_orders_supported_cb, |
| .read_media_state = mcc_media_state_read_cb, |
| .read_opcodes_supported = mcc_opcodes_supported_cb, |
| .read_content_control_id = mcc_content_control_id_cb, |
| .send_cmd = mcc_send_cmd_cb, |
| .cmd_ntf = mcc_cmd_ntf_cb, |
| #ifdef CONFIG_BT_OTS_CLIENT |
| .read_icon_obj_id = mcc_read_icon_obj_id_cb, |
| .read_next_track_obj_id = mcc_read_next_track_obj_id_cb, |
| .set_next_track_obj_id = mcc_set_next_track_obj_id_cb, |
| .read_parent_group_obj_id = mcc_read_parent_group_obj_id_cb, |
| .read_current_group_obj_id = mcc_read_current_group_obj_id_cb, |
| .set_current_group_obj_id = mcc_set_current_group_obj_id_cb, |
| .read_segments_obj_id = mcc_segments_object_id_cb, |
| .read_current_track_obj_id = mcc_current_track_obj_id_read_cb, |
| .set_current_track_obj_id = mcc_current_track_obj_id_set_cb, |
| .send_search = mcc_send_search_cb, |
| .search_ntf = mcc_search_ntf_cb, |
| #endif /* CONFIG_BT_OTS_CLIENT */ |
| }; |
| |
| static uint8_t mcp_supported_commands(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| struct btp_mcp_read_supported_commands_rp *rp = rsp; |
| |
| /* octet 0 */ |
| tester_set_bit(rp->data, BTP_MCP_READ_SUPPORTED_COMMANDS); |
| tester_set_bit(rp->data, BTP_MCP_DISCOVER); |
| tester_set_bit(rp->data, BTP_MCP_TRACK_DURATION_READ); |
| tester_set_bit(rp->data, BTP_MCP_TRACK_POSITION_READ); |
| tester_set_bit(rp->data, BTP_MCP_TRACK_POSITION_SET); |
| tester_set_bit(rp->data, BTP_MCP_PLAYBACK_SPEED_READ); |
| tester_set_bit(rp->data, BTP_MCP_PLAYBACK_SPEED_SET); |
| |
| /* octet 1 */ |
| tester_set_bit(rp->data, BTP_MCP_SEEKING_SPEED_READ); |
| tester_set_bit(rp->data, BTP_MCP_ICON_OBJ_ID_READ); |
| tester_set_bit(rp->data, BTP_MCP_NEXT_TRACK_OBJ_ID_READ); |
| tester_set_bit(rp->data, BTP_MCP_NEXT_TRACK_OBJ_ID_SET); |
| tester_set_bit(rp->data, BTP_MCP_PARENT_GROUP_OBJ_ID_READ); |
| tester_set_bit(rp->data, BTP_MCP_CURRENT_GROUP_OBJ_ID_READ); |
| tester_set_bit(rp->data, BTP_MCP_CURRENT_GROUP_OBJ_ID_SET); |
| |
| /* octet 2 */ |
| tester_set_bit(rp->data, BTP_MCP_PLAYING_ORDER_READ); |
| tester_set_bit(rp->data, BTP_MCP_PLAYING_ORDER_SET); |
| tester_set_bit(rp->data, BTP_MCP_PLAYING_ORDERS_SUPPORTED_READ); |
| tester_set_bit(rp->data, BTP_MCP_MEDIA_STATE_READ); |
| tester_set_bit(rp->data, BTP_MCP_OPCODES_SUPPORTED_READ); |
| tester_set_bit(rp->data, BTP_MCP_CONTENT_CONTROL_ID_READ); |
| tester_set_bit(rp->data, BTP_MCP_SEGMENTS_OBJ_ID_READ); |
| |
| /* octet 3 */ |
| tester_set_bit(rp->data, BTP_MCP_CURRENT_TRACK_OBJ_ID_READ); |
| tester_set_bit(rp->data, BTP_MCP_CURRENT_TRACK_OBJ_ID_SET); |
| tester_set_bit(rp->data, BTP_MCP_CMD_SEND); |
| tester_set_bit(rp->data, BTP_MCP_CMD_SEARCH); |
| |
| *rsp_len = sizeof(*rp) + 1; |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_discover(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_mcp_discover_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_discover_mcs(conn, true); |
| if (err) { |
| LOG_DBG("Discovery failed: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_track_duration_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_track_duration_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read track duration"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_track_duration(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_track_position_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_track_position_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read track position"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_track_position(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_track_position_set(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_track_position_set_cmd *cp = cmd; |
| uint32_t pos = sys_le32_to_cpu(cp->pos); |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Set track position"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_set_track_position(conn, pos); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_playback_speed_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_playback_speed_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read playback speed"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_playback_speed(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_playback_speed_set(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_playback_speed_set *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Set playback speed"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_set_playback_speed(conn, cp->speed); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_seeking_speed_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_seeking_speed_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read seeking speed"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_seeking_speed(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_read_icon_obj_id(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_icon_obj_id_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read Icon Object ID"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_icon_obj_id(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_read_next_track_obj_id(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_next_track_obj_id_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read Next Track Object ID"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_next_track_obj_id(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_set_next_track_obj_id(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_set_next_track_obj_id_cmd *cp = cmd; |
| uint64_t id = sys_get_le48(cp->id); |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Set Next Track Object ID"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_set_next_track_obj_id(conn, id); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_parent_group_obj_id_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_parent_group_obj_id_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read Parent Group Object ID"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_parent_group_obj_id(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_current_group_obj_id_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_current_group_obj_id_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read Current Group Object ID"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_current_group_obj_id(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_set_current_group_obj_id(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_current_group_obj_id_set_cmd *cp = cmd; |
| uint64_t id = sys_get_le48(cp->id); |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Set Next Track Object ID"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_set_current_group_obj_id(conn, id); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_playing_order_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_playing_order_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Read Playing Order"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_playing_order(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_playing_order_set(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_playing_order_set_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Set Playing Order"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_set_playing_order(conn, cp->order); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_playing_orders_supported_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_playing_orders_supported_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Playing orders supported read"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_playing_orders_supported(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_media_state_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_media_state_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Media State read"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_media_state(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_opcodes_supported_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_opcodes_supported_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Supported opcodes read"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_opcodes_supported(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_content_control_id_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_content_control_id_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Content Control ID read"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_content_control_id(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_segments_obj_id_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_segments_obj_id_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Track Segments Object ID read"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_segments_obj_id(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_current_track_obj_id_read(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_current_track_obj_id_read_cmd *cp = cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Current Track Object ID read"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_read_current_track_obj_id(conn); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_current_track_obj_id_set(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| const struct btp_mcp_current_track_obj_id_set_cmd *cp = cmd; |
| uint64_t id = sys_get_le48(cp->id); |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Set Current Track Object ID"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = bt_mcc_set_current_track_obj_id(conn, id); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_cmd_send(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_mcp_send_cmd *cp = cmd; |
| struct mpl_cmd mcp_cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Send Command"); |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| mcp_cmd.opcode = cp->opcode; |
| mcp_cmd.use_param = cp->use_param; |
| mcp_cmd.param = sys_le32_to_cpu(cp->param); |
| |
| err = bt_mcc_send_cmd(conn, &mcp_cmd); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcp_cmd_search(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_mcp_search_cmd *cp = cmd; |
| struct mpl_search search_items; |
| struct mpl_sci scp_cmd; |
| struct bt_conn *conn; |
| int err; |
| |
| LOG_DBG("MCC Send Search Control Point Command"); |
| |
| if (cmd_len < sizeof(*cp) || cmd_len != sizeof(*cp) + cp->param_len) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &cp->address); |
| if (!conn) { |
| LOG_ERR("Unknown connection"); |
| return BTP_STATUS_FAILED; |
| } |
| |
| search_items.len = 0; |
| scp_cmd.type = cp->type; |
| |
| if (scp_cmd.type == BT_MCS_SEARCH_TYPE_ONLY_TRACKS || |
| scp_cmd.type == BT_MCS_SEARCH_TYPE_ONLY_GROUPS) { |
| scp_cmd.len = sizeof(scp_cmd.type); |
| |
| if (ARRAY_SIZE(search_items.search) < (sizeof(scp_cmd.len) + |
| sizeof(scp_cmd.type))) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| memcpy(&search_items.search[search_items.len], &scp_cmd.len, sizeof(scp_cmd.len)); |
| search_items.len += sizeof(scp_cmd.len); |
| |
| memcpy(&search_items.search[search_items.len], &scp_cmd.type, |
| sizeof(scp_cmd.type)); |
| search_items.len += sizeof(scp_cmd.type); |
| } else { |
| if (cp->param_len >= (SEARCH_LEN_MAX - 1)) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| strcpy(scp_cmd.param, cp->param); |
| scp_cmd.len = sizeof(scp_cmd.type) + strlen(scp_cmd.param); |
| |
| if (ARRAY_SIZE(search_items.search) < (sizeof(scp_cmd.len) + sizeof(scp_cmd.len) + |
| strlen(scp_cmd.param))) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| memcpy(&search_items.search[search_items.len], &scp_cmd.len, sizeof(scp_cmd.len)); |
| search_items.len += sizeof(scp_cmd.len); |
| |
| memcpy(&search_items.search[search_items.len], &scp_cmd.type, |
| sizeof(scp_cmd.type)); |
| search_items.len += sizeof(scp_cmd.type); |
| |
| strcpy(&search_items.search[search_items.len], scp_cmd.param); |
| search_items.len += strlen(scp_cmd.param); |
| search_items.search[search_items.len] = '\0'; |
| } |
| |
| err = bt_mcc_send_search(conn, &search_items); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static const struct btp_handler mcp_handlers[] = { |
| { |
| .opcode = BTP_MCP_READ_SUPPORTED_COMMANDS, |
| .index = BTP_INDEX_NONE, |
| .expect_len = 0, |
| .func = mcp_supported_commands, |
| }, |
| { |
| .opcode = BTP_MCP_DISCOVER, |
| .expect_len = sizeof(struct btp_mcp_discover_cmd), |
| .func = mcp_discover, |
| }, |
| { |
| .opcode = BTP_MCP_TRACK_DURATION_READ, |
| .expect_len = sizeof(struct btp_mcp_track_duration_cmd), |
| .func = mcp_track_duration_read, |
| }, |
| { |
| .opcode = BTP_MCP_TRACK_POSITION_READ, |
| .expect_len = sizeof(struct btp_mcp_track_position_read_cmd), |
| .func = mcp_track_position_read, |
| }, |
| { |
| .opcode = BTP_MCP_TRACK_POSITION_SET, |
| .expect_len = sizeof(struct btp_mcp_track_position_set_cmd), |
| .func = mcp_track_position_set, |
| }, |
| { |
| .opcode = BTP_MCP_PLAYBACK_SPEED_READ, |
| .expect_len = sizeof(struct btp_mcp_playback_speed_read_cmd), |
| .func = mcp_playback_speed_read, |
| }, |
| { |
| .opcode = BTP_MCP_PLAYBACK_SPEED_SET, |
| .expect_len = sizeof(struct btp_mcp_playback_speed_set), |
| .func = mcp_playback_speed_set, |
| }, |
| { |
| .opcode = BTP_MCP_SEEKING_SPEED_READ, |
| .expect_len = sizeof(struct btp_mcp_seeking_speed_read_cmd), |
| .func = mcp_seeking_speed_read, |
| }, |
| { |
| .opcode = BTP_MCP_ICON_OBJ_ID_READ, |
| .expect_len = sizeof(struct btp_mcp_icon_obj_id_read_cmd), |
| .func = mcp_read_icon_obj_id, |
| }, |
| { |
| .opcode = BTP_MCP_NEXT_TRACK_OBJ_ID_READ, |
| .expect_len = sizeof(struct btp_mcp_next_track_obj_id_cmd), |
| .func = mcp_read_next_track_obj_id, |
| }, |
| { |
| .opcode = BTP_MCP_NEXT_TRACK_OBJ_ID_SET, |
| .expect_len = sizeof(struct btp_mcp_set_next_track_obj_id_cmd), |
| .func = mcp_set_next_track_obj_id, |
| }, |
| { |
| .opcode = BTP_MCP_PARENT_GROUP_OBJ_ID_READ, |
| .expect_len = sizeof(struct btp_mcp_parent_group_obj_id_read_cmd), |
| .func = mcp_parent_group_obj_id_read, |
| }, |
| { |
| .opcode = BTP_MCP_CURRENT_GROUP_OBJ_ID_READ, |
| .expect_len = sizeof(struct btp_mcp_current_group_obj_id_read_cmd), |
| .func = mcp_current_group_obj_id_read, |
| }, |
| { |
| .opcode = BTP_MCP_CURRENT_GROUP_OBJ_ID_SET, |
| .expect_len = sizeof(struct btp_mcp_current_group_obj_id_set_cmd), |
| .func = mcp_set_current_group_obj_id, |
| }, |
| { |
| .opcode = BTP_MCP_PLAYING_ORDER_READ, |
| .expect_len = sizeof(struct btp_mcp_playing_order_read_cmd), |
| .func = mcp_playing_order_read, |
| }, |
| { |
| .opcode = BTP_MCP_PLAYING_ORDER_SET, |
| .expect_len = sizeof(struct btp_mcp_playing_order_set_cmd), |
| .func = mcp_playing_order_set, |
| }, |
| { |
| .opcode = BTP_MCP_PLAYING_ORDERS_SUPPORTED_READ, |
| .expect_len = sizeof(struct btp_mcp_playing_orders_supported_read_cmd), |
| .func = mcp_playing_orders_supported_read, |
| }, |
| { |
| .opcode = BTP_MCP_MEDIA_STATE_READ, |
| .expect_len = sizeof(struct btp_mcp_media_state_read_cmd), |
| .func = mcp_media_state_read, |
| }, |
| { |
| .opcode = BTP_MCP_OPCODES_SUPPORTED_READ, |
| .expect_len = sizeof(struct btp_mcp_opcodes_supported_read_cmd), |
| .func = mcp_opcodes_supported_read, |
| }, |
| { |
| .opcode = BTP_MCP_CONTENT_CONTROL_ID_READ, |
| .expect_len = sizeof(struct btp_mcp_content_control_id_read_cmd), |
| .func = mcp_content_control_id_read, |
| }, |
| { |
| .opcode = BTP_MCP_SEGMENTS_OBJ_ID_READ, |
| .expect_len = sizeof(struct btp_mcp_segments_obj_id_read_cmd), |
| .func = mcp_segments_obj_id_read, |
| }, |
| { |
| .opcode = BTP_MCP_CURRENT_TRACK_OBJ_ID_READ, |
| .expect_len = sizeof(struct btp_mcp_current_track_obj_id_read_cmd), |
| .func = mcp_current_track_obj_id_read, |
| }, |
| { |
| .opcode = BTP_MCP_CURRENT_TRACK_OBJ_ID_SET, |
| .expect_len = sizeof(struct btp_mcp_current_track_obj_id_set_cmd), |
| .func = mcp_current_track_obj_id_set, |
| }, |
| { |
| .opcode = BTP_MCP_CMD_SEND, |
| .expect_len = sizeof(struct btp_mcp_send_cmd), |
| .func = mcp_cmd_send, |
| }, |
| { |
| .opcode = BTP_MCP_CMD_SEARCH, |
| .expect_len = BTP_HANDLER_LENGTH_VARIABLE, |
| .func = mcp_cmd_search, |
| }, |
| }; |
| |
| uint8_t tester_init_mcp(void) |
| { |
| int err; |
| |
| err = bt_mcc_init(&mcp_cb); |
| if (err) { |
| LOG_DBG("Failed to initialize Media Control Client: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| tester_register_command_handlers(BTP_SERVICE_ID_MCP, mcp_handlers, |
| ARRAY_SIZE(mcp_handlers)); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| uint8_t tester_unregister_mcp(void) |
| { |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| /* Media Control Service */ |
| static uint8_t mcs_supported_commands(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| struct btp_mcs_read_supported_commands_rp *rp = rsp; |
| |
| /* octet 0 */ |
| tester_set_bit(rp->data, BTP_MCS_READ_SUPPORTED_COMMANDS); |
| tester_set_bit(rp->data, BTP_MCS_CMD_SEND); |
| tester_set_bit(rp->data, BTP_MCS_CURRENT_TRACK_OBJ_ID_GET); |
| tester_set_bit(rp->data, BTP_MCS_NEXT_TRACK_OBJ_ID_GET); |
| tester_set_bit(rp->data, BTP_MCS_INACTIVE_STATE_SET); |
| tester_set_bit(rp->data, BTP_MCS_PARENT_GROUP_SET); |
| |
| *rsp_len = sizeof(*rp) + 1; |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcs_cmd_send(const void *cmd, uint16_t cmd_len, void *rsp, uint16_t *rsp_len) |
| { |
| const struct btp_mcs_send_cmd *cp = cmd; |
| struct mpl_cmd mcp_cmd; |
| int err; |
| |
| LOG_DBG("MCS Send Command"); |
| |
| mcp_cmd.opcode = cp->opcode; |
| mcp_cmd.use_param = cp->use_param; |
| mcp_cmd.param = (cp->use_param != 0) ? sys_le32_to_cpu(cp->param) : 0; |
| |
| err = media_proxy_ctrl_send_command(mcs_media_player, &mcp_cmd); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcs_next_track_obj_id_get(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| struct btp_mcs_next_track_obj_id_rp *rp = rsp; |
| int err; |
| |
| LOG_DBG("MCS Read Next Track Obj Id"); |
| |
| err = media_proxy_ctrl_get_next_track_id(mcs_media_player); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| sys_put_le48(next_track_obj_id, rp->id); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcs_current_track_obj_id_get(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| struct btp_mcs_current_track_obj_id_rp *rp = rsp; |
| int err; |
| |
| LOG_DBG("MCS Read Current Track Obj Id"); |
| |
| err = media_proxy_ctrl_get_current_track_id(mcs_media_player); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| sys_put_le48(current_track_obj_id, rp->id); |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcs_parent_group_set(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| int err; |
| |
| LOG_DBG("MCS Set Current Group to be it's own parent"); |
| |
| err = media_proxy_ctrl_get_current_group_id(mcs_media_player); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| /* Setting current group to be it's own parent */ |
| mpl_test_unset_parent_group(); |
| |
| err = media_proxy_ctrl_get_parent_group_id(mcs_media_player); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| if (current_id != parent_id) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static uint8_t mcs_inactive_state_set(const void *cmd, uint16_t cmd_len, void *rsp, |
| uint16_t *rsp_len) |
| { |
| struct btp_mcs_state_set_rp *rp = rsp; |
| |
| LOG_DBG("MCS Set Media Player to inactive state"); |
| |
| mpl_test_media_state_set(MEDIA_PROXY_STATE_INACTIVE); |
| |
| rp->state = media_player_state; |
| |
| *rsp_len = sizeof(*rp); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| static void mcs_player_instance_cb(struct media_player *plr, int err) |
| { |
| mcs_media_player = plr; |
| |
| LOG_DBG("Media PLayer Instance cb"); |
| } |
| |
| static void mcs_command_send_cb(struct media_player *player, int err, const struct mpl_cmd *cmd) |
| { |
| LOG_DBG("Media PLayer Send Command cb"); |
| } |
| |
| static void mcs_current_track_obj_id_cb(struct media_player *player, int err, uint64_t id) |
| { |
| LOG_DBG("Media Player Current Track Object Id cb"); |
| |
| current_track_obj_id = id; |
| } |
| |
| static void mcs_next_track_obj_id_cb(struct media_player *player, int err, uint64_t id) |
| { |
| LOG_DBG("Media PLayer Next Track Object ID cb"); |
| |
| next_track_obj_id = id; |
| } |
| |
| static void mcs_media_state_cb(struct media_player *player, int err, uint8_t state) |
| { |
| LOG_DBG("Media Player State cb"); |
| |
| media_player_state = state; |
| } |
| |
| static void mcs_current_group_id_cb(struct media_player *player, int err, uint64_t id) |
| { |
| LOG_DBG("Media Player Current Group ID cb"); |
| |
| current_id = id; |
| } |
| |
| static void mcs_parent_group_id_cb(struct media_player *player, int err, uint64_t id) |
| { |
| LOG_DBG("Media Player Parent Group ID cb"); |
| |
| parent_id = id; |
| } |
| |
| static struct media_proxy_ctrl_cbs mcs_cbs = { |
| .local_player_instance = mcs_player_instance_cb, |
| .command_send = mcs_command_send_cb, |
| .current_track_id_recv = mcs_current_track_obj_id_cb, |
| .next_track_id_recv = mcs_next_track_obj_id_cb, |
| .media_state_recv = mcs_media_state_cb, |
| .current_group_id_recv = mcs_current_group_id_cb, |
| .parent_group_id_recv = mcs_parent_group_id_cb, |
| }; |
| |
| static const struct btp_handler mcs_handlers[] = { |
| { |
| .opcode = BTP_MCS_READ_SUPPORTED_COMMANDS, |
| .index = BTP_INDEX_NONE, |
| .expect_len = 0, |
| .func = mcs_supported_commands, |
| }, |
| { |
| .opcode = BTP_MCS_CMD_SEND, |
| .expect_len = BTP_HANDLER_LENGTH_VARIABLE, |
| .func = mcs_cmd_send, |
| }, |
| { |
| .opcode = BTP_MCS_CURRENT_TRACK_OBJ_ID_GET, |
| .expect_len = 0, |
| .func = mcs_current_track_obj_id_get, |
| }, |
| { |
| .opcode = BTP_MCS_NEXT_TRACK_OBJ_ID_GET, |
| .expect_len = 0, |
| .func = mcs_next_track_obj_id_get, |
| }, |
| { |
| .opcode = BTP_MCS_INACTIVE_STATE_SET, |
| .expect_len = 0, |
| .func = mcs_inactive_state_set, |
| }, |
| { |
| .opcode = BTP_MCS_PARENT_GROUP_SET, |
| .expect_len = 0, |
| .func = mcs_parent_group_set, |
| }, |
| }; |
| |
| uint8_t tester_init_mcs(void) |
| { |
| int err; |
| |
| err = media_proxy_pl_init(); |
| if (err) { |
| LOG_DBG("Failed to initialize Media Player: %d", err); |
| return BTP_STATUS_FAILED; |
| } |
| |
| err = media_proxy_ctrl_register(&mcs_cbs); |
| if (err) { |
| return BTP_STATUS_FAILED; |
| } |
| |
| tester_register_command_handlers(BTP_SERVICE_ID_GMCS, mcs_handlers, |
| ARRAY_SIZE(mcs_handlers)); |
| |
| return BTP_STATUS_SUCCESS; |
| } |
| |
| uint8_t tester_unregister_mcs(void) |
| { |
| return BTP_STATUS_SUCCESS; |
| } |