diff options
Diffstat (limited to 'stack/avrc/avrc_pars_tg.c')
-rw-r--r-- | stack/avrc/avrc_pars_tg.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/stack/avrc/avrc_pars_tg.c b/stack/avrc/avrc_pars_tg.c new file mode 100644 index 0000000..8237655 --- /dev/null +++ b/stack/avrc/avrc_pars_tg.c @@ -0,0 +1,452 @@ +/***************************************************************************** +** +** Name: avrc_pars_tg.c +** +** Description:Interface to AVRCP parse message functions for the Target Role +** +** Copyright (c) 2008-2008, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> + +#include "gki.h" +#include "avrc_api.h" +#include "avrc_defs.h" +#include "avrc_int.h" + +/***************************************************************************** +** Global data +*****************************************************************************/ +#if (AVRC_METADATA_INCLUDED == TRUE) + +/******************************************************************************* +** +** 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 *p_buf, UINT16 buf_len) +{ + tAVRC_STS status = AVRC_STS_NO_ERROR; + UINT8 *p = p_msg->p_vendor_data; + UINT16 len; + UINT8 xx, yy; + UINT8 *p_u8; + UINT16 *p_u16; + UINT32 u32, u32_2, *p_u32; + tAVRC_APP_SETTING *p_app_set; + UINT16 size_needed; + + p_result->pdu = *p++; + AVRC_TRACE_DEBUG1("avrc_pars_vendor_cmd() pdu:0x%x", p_result->pdu); + if (!AVRC_IsValidAvcType (p_result->pdu, p_msg->hdr.ctype)) + { + AVRC_TRACE_DEBUG0("avrc_pars_vendor_cmd() detects wrong AV/C type!"); + status = AVRC_STS_BAD_CMD; + } + + p++; /* skip the reserved byte */ + 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_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) + status = AVRC_STS_INTERNAL_ERR; + break; + + case AVRC_PDU_LIST_PLAYER_APP_ATTR: /* 0x11 */ + /* no additional parameters */ + if (len != 0) + status = AVRC_STS_INTERNAL_ERR; + break; + + case AVRC_PDU_LIST_PLAYER_APP_VALUES: /* 0x12 */ + 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 */ + 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; + } + 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 */ + 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) + { + AVRC_TRACE_ERROR2("AVRC_PDU_SET_PLAYER_APP_VALUE not enough room:%d orig num_val:%d", + xx, p_result->set_app_val.num_val); + p_result->set_app_val.num_val = xx; + } + } + else + { + AVRC_TRACE_ERROR0("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) + status = 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 + { + 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) + status = 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) + status = 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 */ + status = 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) + status = AVRC_STS_INTERNAL_ERR; + break; + + case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */ + if (len != 5) + status = 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); + break; + + /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */ + /* case AVRC_PDU_ABORT_CONTINUATION_RSP: 0x41 */ + +#if (AVRC_ADV_CTRL_INCLUDED == TRUE) + case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */ + if (len != 1) + status = AVRC_STS_INTERNAL_ERR; + p_result->volume.volume = *p++; + break; + + case AVRC_PDU_SET_ADDRESSED_PLAYER: /* 0x60 */ + if (len != 2) + status = 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)) + status = 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; +#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */ + + default: + status = AVRC_STS_BAD_CMD; + break; + } + + return status; +} + +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avrc_pars_browsing +** +** 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 *p_buf, UINT16 buf_len) +{ + tAVRC_STS status = AVRC_STS_NO_ERROR; + UINT8 *p = p_msg->p_browse_data; + UINT16 len; + int i, count; + + p_result->pdu = *p++; + AVRC_TRACE_DEBUG1("avrc_pars_browsing_cmd() pdu:0x%x", p_result->pdu); + BE_STREAM_TO_UINT16 (len, p); + switch (p_result->pdu) + { + case AVRC_PDU_SET_BROWSED_PLAYER: /* 0x70 */ + BE_STREAM_TO_UINT16 (p_result->br_player.player_id, p); + break; + + case AVRC_PDU_GET_FOLDER_ITEMS: /* 0x71 */ + BE_STREAM_TO_UINT8 (p_result->get_items.scope, p); + 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; + } + BE_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 *)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 (i=0; i<count; i++) + { + BE_STREAM_TO_UINT32 (p_result->get_items.p_attr_list[i], p); + } + } + break; + + case AVRC_PDU_CHANGE_PATH: /* 0x72 */ + 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 */ + 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 *)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 (i=0, count=0; i<p_result->get_attrs.attr_count; i++) + { + 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_SEARCH: /* 0x80 */ + 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 (buf_len > p_result->search.string.str_len) + buf_len = p_result->search.string.str_len; + 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; +} +#endif /* AVCT_BROWSE_INCLUDED == TRUE*/ + +/******************************************************************************* +** +** 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 *p_buf, UINT16 buf_len) +{ + tAVRC_STS status = AVRC_STS_INTERNAL_ERR; + UINT16 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)id; + } + break; + +#if (AVCT_BROWSE_INCLUDED == TRUE) + case AVRC_OP_BROWSE: + status = avrc_pars_browsing_cmd(&p_msg->browse, p_result, p_buf, buf_len); + break; +#endif /*(AVCT_BROWSE_INCLUDED == TRUE)*/ + + default: + AVRC_TRACE_ERROR1("AVRC_ParsCommand() unknown opcode:0x%x", p_msg->hdr.opcode); + break; + } + p_result->cmd.opcode = p_msg->hdr.opcode; + p_result->cmd.status = status; + } + AVRC_TRACE_DEBUG1("AVRC_ParsCommand() return status:0x%x", status); + return status; +} + +#endif /* (AVRC_METADATA_INCLUDED == TRUE) */ + |