| /****************************************************************************** |
| * |
| * 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; |
| } |