blob: 4a053c64f9ce49cec0558595e07b1a403104df16 [file] [log] [blame]
/******************************************************************************
*
* Copyright 2003-2016 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include <bluetooth/log.h>
#include <string.h>
#include "avrc_api.h"
#include "avrc_defs.h"
#include "avrc_int.h"
#include "os/log.h"
#include "stack/include/bt_types.h"
using namespace bluetooth;
/*****************************************************************************
* Global data
****************************************************************************/
/*******************************************************************************
*
* Function avrc_ctrl_pars_vendor_cmd
*
* Description This function parses the vendor specific commands defined by
* Bluetooth SIG for AVRCP Conroller.
*
* Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed
* successfully.
* Otherwise, the error code defined by AVRCP 1.4
*
******************************************************************************/
static tAVRC_STS avrc_ctrl_pars_vendor_cmd(tAVRC_MSG_VENDOR* p_msg,
tAVRC_COMMAND* p_result) {
tAVRC_STS status = AVRC_STS_NO_ERROR;
if (p_msg->vendor_len < 4) { // 4 == pdu + reserved byte + len as uint16
log::warn("message length {} too short: must be at least 4",
p_msg->vendor_len);
return AVRC_STS_INTERNAL_ERR;
}
uint8_t* p = p_msg->p_vendor_data;
p_result->pdu = *p++;
log::verbose("pdu:0x{:x}", p_result->pdu);
if (!AVRC_IsValidAvcType(p_result->pdu, p_msg->hdr.ctype)) {
log::verbose("detects wrong AV/C type!");
status = AVRC_STS_BAD_CMD;
}
p++; /* skip the reserved byte */
uint16_t len;
BE_STREAM_TO_UINT16(len, p);
if ((len + 4) != (p_msg->vendor_len)) {
status = AVRC_STS_INTERNAL_ERR;
}
if (status != AVRC_STS_NO_ERROR) return status;
switch (p_result->pdu) {
case AVRC_PDU_SET_ABSOLUTE_VOLUME: {
if (len != 1)
status = AVRC_STS_INTERNAL_ERR;
else {
BE_STREAM_TO_UINT8(p_result->volume.volume, p);
p_result->volume.volume = AVRC_MAX_VOLUME & p_result->volume.volume;
}
break;
}
case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */
if (len < 5) return AVRC_STS_INTERNAL_ERR;
BE_STREAM_TO_UINT8(p_result->reg_notif.event_id, p);
BE_STREAM_TO_UINT32(p_result->reg_notif.param, p);
if (p_result->reg_notif.event_id == 0 ||
p_result->reg_notif.event_id > AVRC_NUM_NOTIF_EVENTS) {
status = AVRC_STS_BAD_PARAM;
}
break;
default:
status = AVRC_STS_BAD_CMD;
break;
}
return status;
}
/*******************************************************************************
*
* Function avrc_pars_vendor_cmd
*
* Description This function parses the vendor specific commands defined by
* Bluetooth SIG
*
* Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed
* successfully.
* Otherwise, the error code defined by AVRCP 1.4
*
******************************************************************************/
static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR* p_msg,
tAVRC_COMMAND* p_result, uint8_t* p_buf,
uint16_t buf_len) {
tAVRC_STS status = AVRC_STS_NO_ERROR;
uint8_t* p;
uint16_t len;
uint8_t xx, yy;
uint8_t* p_u8;
uint16_t* p_u16;
uint32_t u32, u32_2, *p_u32;
tAVRC_APP_SETTING* p_app_set;
uint16_t size_needed;
/* Check the vendor data */
if (p_msg->vendor_len == 0) return AVRC_STS_NO_ERROR;
if (p_msg->p_vendor_data == NULL) return AVRC_STS_INTERNAL_ERR;
if (p_msg->vendor_len < 4) {
log::warn("message length {} too short: must be at least 4",
p_msg->vendor_len);
return AVRC_STS_INTERNAL_ERR;
}
p = p_msg->p_vendor_data;
p_result->pdu = *p++;
log::verbose("pdu:0x{:x}", p_result->pdu);
if (!AVRC_IsValidAvcType(p_result->pdu, p_msg->hdr.ctype)) {
log::verbose("detects wrong AV/C type(0x{:x})!", p_msg->hdr.ctype);
status = AVRC_STS_BAD_CMD;
}
p++; /* skip the reserved byte */
BE_STREAM_TO_UINT16(len, p);
if ((len + 4) != (p_msg->vendor_len)) {
log::error("incorrect length :{}, {}", len, p_msg->vendor_len);
status = AVRC_STS_INTERNAL_ERR;
}
if (status != AVRC_STS_NO_ERROR) return status;
switch (p_result->pdu) {
case AVRC_PDU_GET_CAPABILITIES: /* 0x10 */
p_result->get_caps.capability_id = *p++;
if (!AVRC_IS_VALID_CAP_ID(p_result->get_caps.capability_id))
status = AVRC_STS_BAD_PARAM;
else if (len != 1)
return AVRC_STS_INTERNAL_ERR;
break;
case AVRC_PDU_LIST_PLAYER_APP_ATTR: /* 0x11 */
/* no additional parameters */
if (len != 0) return AVRC_STS_INTERNAL_ERR;
break;
case AVRC_PDU_LIST_PLAYER_APP_VALUES: /* 0x12 */
if (len == 0) return AVRC_STS_INTERNAL_ERR;
p_result->list_app_values.attr_id = *p++;
if (!AVRC_IS_VALID_ATTRIBUTE(p_result->list_app_values.attr_id))
status = AVRC_STS_BAD_PARAM;
else if (len != 1)
status = AVRC_STS_INTERNAL_ERR;
break;
case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: /* 0x13 */
case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: /* 0x15 */
if (len == 0) return AVRC_STS_INTERNAL_ERR;
BE_STREAM_TO_UINT8(p_result->get_cur_app_val.num_attr, p);
if (len != (p_result->get_cur_app_val.num_attr + 1)) {
status = AVRC_STS_INTERNAL_ERR;
break;
}
if (p_result->get_cur_app_val.num_attr > AVRC_MAX_APP_ATTR_SIZE) {
p_result->get_cur_app_val.num_attr = AVRC_MAX_APP_ATTR_SIZE;
}
p_u8 = p_result->get_cur_app_val.attrs;
for (xx = 0, yy = 0; xx < p_result->get_cur_app_val.num_attr; xx++) {
/* only report the valid player app attributes */
if (AVRC_IsValidPlayerAttr(*p)) p_u8[yy++] = *p;
p++;
}
p_result->get_cur_app_val.num_attr = yy;
if (yy == 0) {
status = AVRC_STS_BAD_PARAM;
}
break;
case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */
if (len == 0) return AVRC_STS_INTERNAL_ERR;
BE_STREAM_TO_UINT8(p_result->set_app_val.num_val, p);
size_needed = sizeof(tAVRC_APP_SETTING);
if (p_buf && (len == ((p_result->set_app_val.num_val << 1) + 1))) {
p_result->set_app_val.p_vals = (tAVRC_APP_SETTING*)p_buf;
p_app_set = p_result->set_app_val.p_vals;
for (xx = 0;
((xx < p_result->set_app_val.num_val) && (buf_len > size_needed));
xx++) {
p_app_set[xx].attr_id = *p++;
p_app_set[xx].attr_val = *p++;
if (!avrc_is_valid_player_attrib_value(p_app_set[xx].attr_id,
p_app_set[xx].attr_val))
status = AVRC_STS_BAD_PARAM;
}
if (xx != p_result->set_app_val.num_val) {
log::error(
"AVRC_PDU_SET_PLAYER_APP_VALUE not enough room:{} orig "
"num_val:{}",
xx, p_result->set_app_val.num_val);
p_result->set_app_val.num_val = xx;
}
} else {
log::error(
"AVRC_PDU_SET_PLAYER_APP_VALUE NULL decode buffer or bad len");
status = AVRC_STS_INTERNAL_ERR;
}
break;
case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT: /* 0x16 */
if (len < 3)
return AVRC_STS_INTERNAL_ERR;
else {
BE_STREAM_TO_UINT8(p_result->get_app_val_txt.attr_id, p);
if (!AVRC_IS_VALID_ATTRIBUTE(p_result->get_app_val_txt.attr_id))
status = AVRC_STS_BAD_PARAM;
else {
BE_STREAM_TO_UINT8(p_result->get_app_val_txt.num_val, p);
if ((len - 2 /* attr_id & num_val */) !=
p_result->get_app_val_txt.num_val)
status = AVRC_STS_INTERNAL_ERR;
else {
if (p_result->get_app_val_txt.num_val > AVRC_MAX_APP_ATTR_SIZE) {
p_result->get_app_val_txt.num_val = AVRC_MAX_APP_ATTR_SIZE;
}
p_u8 = p_result->get_app_val_txt.vals;
for (xx = 0; xx < p_result->get_app_val_txt.num_val; xx++) {
p_u8[xx] = *p++;
if (!avrc_is_valid_player_attrib_value(
p_result->get_app_val_txt.attr_id, p_u8[xx])) {
status = AVRC_STS_BAD_PARAM;
break;
}
}
}
}
}
break;
case AVRC_PDU_INFORM_DISPLAY_CHARSET: /* 0x17 */
if (len < 3)
return AVRC_STS_INTERNAL_ERR;
else {
BE_STREAM_TO_UINT8(p_result->inform_charset.num_id, p);
if ((len - 1 /* num_id */) != p_result->inform_charset.num_id * 2)
status = AVRC_STS_INTERNAL_ERR;
else {
p_u16 = p_result->inform_charset.charsets;
if (p_result->inform_charset.num_id > AVRC_MAX_CHARSET_SIZE)
p_result->inform_charset.num_id = AVRC_MAX_CHARSET_SIZE;
for (xx = 0; xx < p_result->inform_charset.num_id; xx++) {
BE_STREAM_TO_UINT16(p_u16[xx], p);
}
}
}
break;
case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT: /* 0x18 */
if (len != 1)
return AVRC_STS_INTERNAL_ERR;
else {
p_result->inform_battery_status.battery_status = *p++;
if (!AVRC_IS_VALID_BATTERY_STATUS(
p_result->inform_battery_status.battery_status))
status = AVRC_STS_BAD_PARAM;
}
break;
case AVRC_PDU_GET_ELEMENT_ATTR: /* 0x20 */
if (len < 9) /* UID/8 and num_attr/1 */
return AVRC_STS_INTERNAL_ERR;
else {
BE_STREAM_TO_UINT32(u32, p);
BE_STREAM_TO_UINT32(u32_2, p);
if (u32 == 0 && u32_2 == 0) {
BE_STREAM_TO_UINT8(p_result->get_elem_attrs.num_attr, p);
if ((len - 9 /* UID/8 and num_attr/1 */) !=
(p_result->get_elem_attrs.num_attr * 4))
status = AVRC_STS_INTERNAL_ERR;
else {
p_u32 = p_result->get_elem_attrs.attrs;
if (p_result->get_elem_attrs.num_attr > AVRC_MAX_ELEM_ATTR_SIZE)
p_result->get_elem_attrs.num_attr = AVRC_MAX_ELEM_ATTR_SIZE;
for (xx = 0; xx < p_result->get_elem_attrs.num_attr; xx++) {
BE_STREAM_TO_UINT32(p_u32[xx], p);
}
}
} else
status = AVRC_STS_NOT_FOUND;
}
break;
case AVRC_PDU_GET_PLAY_STATUS: /* 0x30 */
/* no additional parameters */
if (len != 0) return AVRC_STS_INTERNAL_ERR;
break;
case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */
if (len != 5)
return AVRC_STS_INTERNAL_ERR;
else {
BE_STREAM_TO_UINT8(p_result->reg_notif.event_id, p);
if (!AVRC_IS_VALID_EVENT_ID(p_result->reg_notif.event_id)) {
log::error("Invalid event id: {}", p_result->reg_notif.event_id);
return AVRC_STS_BAD_PARAM;
}
BE_STREAM_TO_UINT32(p_result->reg_notif.param, p);
}
break;
case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */
if (len != 1)
return AVRC_STS_INTERNAL_ERR;
else
p_result->volume.volume = *p++;
break;
case AVRC_PDU_REQUEST_CONTINUATION_RSP: /* 0x40 */
if (len != 1) {
return AVRC_STS_INTERNAL_ERR;
}
BE_STREAM_TO_UINT8(p_result->continu.target_pdu, p);
break;
case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */
if (len != 1) {
return AVRC_STS_INTERNAL_ERR;
}
BE_STREAM_TO_UINT8(p_result->abort.target_pdu, p);
break;
case AVRC_PDU_SET_ADDRESSED_PLAYER: /* 0x60 */
if (len != 2) {
log::error("AVRC_PDU_SET_ADDRESSED_PLAYER length is incorrect:{}", len);
return AVRC_STS_INTERNAL_ERR;
}
BE_STREAM_TO_UINT16(p_result->addr_player.player_id, p);
break;
case AVRC_PDU_PLAY_ITEM: /* 0x74 */
case AVRC_PDU_ADD_TO_NOW_PLAYING: /* 0x90 */
if (len != (AVRC_UID_SIZE + 3)) return AVRC_STS_INTERNAL_ERR;
BE_STREAM_TO_UINT8(p_result->play_item.scope, p);
if (p_result->play_item.scope > AVRC_SCOPE_NOW_PLAYING) {
status = AVRC_STS_BAD_SCOPE;
}
BE_STREAM_TO_ARRAY(p, p_result->play_item.uid, AVRC_UID_SIZE);
BE_STREAM_TO_UINT16(p_result->play_item.uid_counter, p);
break;
default:
status = AVRC_STS_BAD_CMD;
break;
}
return status;
}
/*******************************************************************************
*
* Function AVRC_Ctrl_ParsCommand
*
* Description This function is used to parse cmds received for CTRL
* Currently it is for SetAbsVolume and Volume Change
* Notification.
*
* Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed
* successfully.
* Otherwise, the error code defined by AVRCP 1.4
*
******************************************************************************/
tAVRC_STS AVRC_Ctrl_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result) {
tAVRC_STS status = AVRC_STS_INTERNAL_ERR;
if (p_msg && p_result) {
switch (p_msg->hdr.opcode) {
case AVRC_OP_VENDOR: /* 0x00 Vendor-dependent commands */
status = avrc_ctrl_pars_vendor_cmd(&p_msg->vendor, p_result);
break;
default:
log::error("unknown opcode:0x{:x}", p_msg->hdr.opcode);
break;
}
p_result->cmd.opcode = p_msg->hdr.opcode;
p_result->cmd.status = status;
}
log::verbose("return status:0x{:x}", status);
return status;
}
#define RETURN_STATUS_IF_FALSE(_status_, _b_, _msg_, ...) \
if (!(_b_)) { \
LOG_VERBOSE(_msg_, ##__VA_ARGS__); \
return _status_; \
}
/*******************************************************************************
*
* Function avrc_pars_browsing_cmd
*
* Description This function parses the commands that go through the
* browsing channel
*
* Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed
* successfully.
* Otherwise, the error code defined by AVRCP+1
*
******************************************************************************/
static tAVRC_STS avrc_pars_browsing_cmd(tAVRC_MSG_BROWSE* p_msg,
tAVRC_COMMAND* p_result, uint8_t* p_buf,
uint16_t buf_len) {
tAVRC_STS status = AVRC_STS_NO_ERROR;
uint8_t* p = p_msg->p_browse_data;
int count;
uint32_t min_len = 3;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
p_result->pdu = *p++;
log::verbose("avrc_pars_browsing_cmd() pdu:0x{:x}", p_result->pdu);
/* skip over len */
p += 2;
switch (p_result->pdu) {
case AVRC_PDU_SET_BROWSED_PLAYER: /* 0x70 */
min_len += 2;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
// For current implementation all players are browsable.
BE_STREAM_TO_UINT16(p_result->br_player.player_id, p);
break;
case AVRC_PDU_GET_FOLDER_ITEMS: /* 0x71 */
min_len += 10;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
STREAM_TO_UINT8(p_result->get_items.scope, p);
// To be modified later here (Scope) when all browsing commands are
// supported
if (p_result->get_items.scope > AVRC_SCOPE_NOW_PLAYING) {
status = AVRC_STS_BAD_SCOPE;
}
BE_STREAM_TO_UINT32(p_result->get_items.start_item, p);
BE_STREAM_TO_UINT32(p_result->get_items.end_item, p);
if (p_result->get_items.start_item > p_result->get_items.end_item) {
status = AVRC_STS_BAD_RANGE;
}
STREAM_TO_UINT8(p_result->get_items.attr_count, p);
p_result->get_items.p_attr_list = NULL;
if (p_result->get_items.attr_count && p_buf &&
(p_result->get_items.attr_count != AVRC_FOLDER_ITEM_COUNT_NONE)) {
p_result->get_items.p_attr_list = (uint32_t*)p_buf;
count = p_result->get_items.attr_count;
if (buf_len < (count << 2))
p_result->get_items.attr_count = count = (buf_len >> 2);
for (int idx = 0; idx < count; idx++) {
min_len += 4;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD,
(p_msg->browse_len >= min_len),
"msg too short");
BE_STREAM_TO_UINT32(p_result->get_items.p_attr_list[idx], p);
}
}
break;
case AVRC_PDU_CHANGE_PATH: /* 0x72 */
min_len += 11;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
BE_STREAM_TO_UINT16(p_result->chg_path.uid_counter, p);
BE_STREAM_TO_UINT8(p_result->chg_path.direction, p);
if (p_result->chg_path.direction != AVRC_DIR_UP &&
p_result->chg_path.direction != AVRC_DIR_DOWN) {
status = AVRC_STS_BAD_DIR;
}
BE_STREAM_TO_ARRAY(p, p_result->chg_path.folder_uid, AVRC_UID_SIZE);
break;
case AVRC_PDU_GET_ITEM_ATTRIBUTES: /* 0x73 */
min_len += 12;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
BE_STREAM_TO_UINT8(p_result->get_attrs.scope, p);
if (p_result->get_attrs.scope > AVRC_SCOPE_NOW_PLAYING) {
status = AVRC_STS_BAD_SCOPE;
break;
}
BE_STREAM_TO_ARRAY(p, p_result->get_attrs.uid, AVRC_UID_SIZE);
BE_STREAM_TO_UINT16(p_result->get_attrs.uid_counter, p);
BE_STREAM_TO_UINT8(p_result->get_attrs.attr_count, p);
p_result->get_attrs.p_attr_list = NULL;
if (p_result->get_attrs.attr_count && p_buf) {
p_result->get_attrs.p_attr_list = (uint32_t*)p_buf;
count = p_result->get_attrs.attr_count;
if (buf_len < (count << 2))
p_result->get_attrs.attr_count = count = (buf_len >> 2);
for (int idx = 0, count = 0; idx < p_result->get_attrs.attr_count;
idx++) {
min_len += 4;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD,
(p_msg->browse_len >= min_len),
"msg too short");
BE_STREAM_TO_UINT32(p_result->get_attrs.p_attr_list[count], p);
if (AVRC_IS_VALID_MEDIA_ATTRIBUTE(
p_result->get_attrs.p_attr_list[count])) {
count++;
}
}
if (p_result->get_attrs.attr_count != count && count == 0)
status = AVRC_STS_BAD_PARAM;
else
p_result->get_attrs.attr_count = count;
}
break;
case AVRC_PDU_GET_TOTAL_NUM_OF_ITEMS: /* 0x75 */
++min_len;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
BE_STREAM_TO_UINT8(p_result->get_num_of_items.scope, p);
if (p_result->get_num_of_items.scope > AVRC_SCOPE_NOW_PLAYING) {
status = AVRC_STS_BAD_SCOPE;
}
break;
case AVRC_PDU_SEARCH: /* 0x80 */
min_len += 4;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
BE_STREAM_TO_UINT16(p_result->search.string.charset_id, p);
BE_STREAM_TO_UINT16(p_result->search.string.str_len, p);
p_result->search.string.p_str = p_buf;
if (p_buf) {
if (p_result->search.string.str_len > buf_len) {
p_result->search.string.str_len = buf_len;
}
min_len += p_result->search.string.str_len;
RETURN_STATUS_IF_FALSE(AVRC_STS_BAD_CMD, (p_msg->browse_len >= min_len),
"msg too short");
BE_STREAM_TO_ARRAY(p, p_buf, p_result->search.string.str_len);
} else {
status = AVRC_STS_INTERNAL_ERR;
}
break;
default:
status = AVRC_STS_BAD_CMD;
break;
}
return status;
}
/*******************************************************************************
*
* Function AVRC_ParsCommand
*
* Description This function is a superset of AVRC_ParsMetadata to parse
* the command.
*
* Returns AVRC_STS_NO_ERROR, if the message in p_data is parsed
* successfully.
* Otherwise, the error code defined by AVRCP 1.4
*
******************************************************************************/
tAVRC_STS AVRC_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result,
uint8_t* p_buf, uint16_t buf_len) {
tAVRC_STS status = AVRC_STS_INTERNAL_ERR;
uint16_t id;
if (p_msg && p_result) {
switch (p_msg->hdr.opcode) {
case AVRC_OP_VENDOR: /* 0x00 Vendor-dependent commands */
status = avrc_pars_vendor_cmd(&p_msg->vendor, p_result, p_buf, buf_len);
break;
case AVRC_OP_PASS_THRU: /* 0x7C panel subunit opcode */
status = avrc_pars_pass_thru(&p_msg->pass, &id);
if (status == AVRC_STS_NO_ERROR) {
p_result->pdu = (uint8_t)id;
}
break;
case AVRC_OP_BROWSE:
status =
avrc_pars_browsing_cmd(&p_msg->browse, p_result, p_buf, buf_len);
break;
default:
log::error("unknown opcode:0x{:x}", p_msg->hdr.opcode);
break;
}
p_result->cmd.opcode = p_msg->hdr.opcode;
p_result->cmd.status = status;
}
log::verbose("return status:0x{:x}", status);
return status;
}