| /****************************************************************************** |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| #define LOG_TAG "avrcp" |
| |
| #include <bluetooth/log.h> |
| #include <string.h> |
| |
| #include "avrc_api.h" |
| #include "avrc_defs.h" |
| #include "avrc_int.h" |
| #include "include/check.h" |
| #include "internal_include/bt_target.h" |
| #include "os/log.h" |
| #include "osi/include/allocator.h" |
| #include "osi/include/osi.h" |
| #include "stack/avct/avct_defs.h" |
| #include "stack/include/bt_hdr.h" |
| #include "stack/include/bt_types.h" |
| |
| using namespace bluetooth; |
| |
| /***************************************************************************** |
| * Global data |
| ****************************************************************************/ |
| #define AVRC_ITEM_PLAYER_IS_VALID(_p_player) \ |
| ((_p_player)->name.p_str && \ |
| ((_p_player)->major_type & AVRC_MJ_TYPE_INVALID) == 0 && \ |
| ((_p_player)->sub_type & AVRC_SUB_TYPE_INVALID) == 0 && \ |
| (((_p_player)->play_status <= AVRC_PLAYSTATE_REV_SEEK) || \ |
| ((_p_player)->play_status == AVRC_PLAYSTATE_ERROR))) |
| |
| /* 17 = item_type(1) + item len(2) + min item (14) */ |
| #define AVRC_MIN_LEN_GET_FOLDER_ITEMS_RSP 17 |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_capability_rsp |
| * |
| * Description This function builds the Get Capability response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_capability_rsp(tAVRC_GET_CAPS_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start, *p_len, *p_count; |
| uint16_t len = 0; |
| uint8_t xx; |
| uint32_t* p_company_id; |
| uint8_t* p_event_id; |
| tAVRC_STS status = AVRC_STS_NO_ERROR; |
| |
| if (!(AVRC_IS_VALID_CAP_ID(p_rsp->capability_id))) { |
| log::error("bad parameter. p_rsp: {}", fmt::ptr(p_rsp)); |
| status = AVRC_STS_BAD_PARAM; |
| return status; |
| } |
| |
| log::verbose(""); |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 2; /* pdu + rsvd */ |
| |
| BE_STREAM_TO_UINT16(len, p_data); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->capability_id); |
| p_count = p_data; |
| |
| if (len == 0) { |
| *p_count = p_rsp->count; |
| p_data++; |
| len = 2; /* move past the capability_id and count */ |
| } else { |
| p_data = p_start + p_pkt->len; |
| *p_count += p_rsp->count; |
| } |
| |
| if (p_rsp->capability_id == AVRC_CAP_COMPANY_ID) { |
| p_company_id = p_rsp->param.company_id; |
| for (xx = 0; xx < p_rsp->count; xx++) { |
| UINT24_TO_BE_STREAM(p_data, p_company_id[xx]); |
| } |
| len += p_rsp->count * 3; |
| } else { |
| p_event_id = p_rsp->param.event_id; |
| *p_count = 0; |
| for (xx = 0; xx < p_rsp->count; xx++) { |
| if (AVRC_IS_VALID_EVENT_ID(p_event_id[xx])) { |
| (*p_count)++; |
| UINT8_TO_BE_STREAM(p_data, p_event_id[xx]); |
| } |
| } |
| len += (*p_count); |
| } |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| status = AVRC_STS_NO_ERROR; |
| |
| return status; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_list_app_settings_attr_rsp |
| * |
| * Description This function builds the List Application Settings Attribute |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_list_app_settings_attr_rsp( |
| tAVRC_LIST_APP_ATTR_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start, *p_len, *p_num; |
| uint16_t len = 0; |
| uint8_t xx; |
| |
| log::verbose(""); |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 2; /* pdu + rsvd */ |
| |
| BE_STREAM_TO_UINT16(len, p_data); |
| p_num = p_data; |
| if (len == 0) { |
| /* first time initialize the attribute count */ |
| *p_num = 0; |
| p_data++; |
| } else { |
| p_data = p_start + p_pkt->len; |
| } |
| |
| for (xx = 0; xx < p_rsp->num_attr; xx++) { |
| if (AVRC_IsValidPlayerAttr(p_rsp->attrs[xx])) { |
| (*p_num)++; |
| UINT8_TO_BE_STREAM(p_data, p_rsp->attrs[xx]); |
| } |
| } |
| |
| len = *p_num + 1; |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_list_app_settings_values_rsp |
| * |
| * Description This function builds the List Application Setting Values |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_list_app_settings_values_rsp( |
| tAVRC_LIST_APP_VALUES_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start, *p_len, *p_num; |
| uint8_t xx; |
| uint16_t len; |
| |
| log::verbose(""); |
| |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 2; /* pdu + rsvd */ |
| |
| /* get the existing length, if any, and also the num attributes */ |
| BE_STREAM_TO_UINT16(len, p_data); |
| p_num = p_data; |
| /* first time initialize the attribute count */ |
| if (len == 0) { |
| *p_num = p_rsp->num_val; |
| p_data++; |
| } else { |
| p_data = p_start + p_pkt->len; |
| *p_num += p_rsp->num_val; |
| } |
| |
| for (xx = 0; xx < p_rsp->num_val; xx++) { |
| UINT8_TO_BE_STREAM(p_data, p_rsp->vals[xx]); |
| } |
| |
| len = *p_num + 1; |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_cur_app_setting_value_rsp |
| * |
| * Description This function builds the Get Current Application Setting |
| * Value response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_cur_app_setting_value_rsp( |
| tAVRC_GET_CUR_APP_VALUE_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start, *p_len, *p_count; |
| uint16_t len; |
| uint8_t xx; |
| |
| if (!p_rsp->p_vals) { |
| log::error("NULL parameter"); |
| return AVRC_STS_BAD_PARAM; |
| } |
| |
| log::verbose(""); |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 2; /* pdu + rsvd */ |
| |
| BE_STREAM_TO_UINT16(len, p_data); |
| p_count = p_data; |
| if (len == 0) { |
| /* first time initialize the attribute count */ |
| *p_count = 0; |
| p_data++; |
| } else { |
| p_data = p_start + p_pkt->len; |
| } |
| |
| for (xx = 0; xx < p_rsp->num_val; xx++) { |
| if (avrc_is_valid_player_attrib_value(p_rsp->p_vals[xx].attr_id, |
| p_rsp->p_vals[xx].attr_val)) { |
| (*p_count)++; |
| UINT8_TO_BE_STREAM(p_data, p_rsp->p_vals[xx].attr_id); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->p_vals[xx].attr_val); |
| } |
| } |
| len = ((*p_count) << 1) + 1; |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_set_app_setting_value_rsp |
| * |
| * Description This function builds the Set Application Setting Value |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_set_app_setting_value_rsp( |
| UNUSED_ATTR tAVRC_RSP* p_rsp, UNUSED_ATTR BT_HDR* p_pkt) { |
| /* nothing to be added. */ |
| log::verbose(""); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_app_setting_text_rsp |
| * |
| * Description This function builds the Get Application Settings Attribute |
| * Text or Get Application Settings Value Text response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_app_setting_text_rsp( |
| tAVRC_GET_APP_ATTR_TXT_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start, *p_len, *p_count; |
| uint16_t len, len_left; |
| uint8_t xx; |
| tAVRC_STS sts = AVRC_STS_NO_ERROR; |
| uint8_t num_added = 0; |
| |
| if (!p_rsp->p_attrs) { |
| log::error("NULL parameter"); |
| return AVRC_STS_BAD_PARAM; |
| } |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 2; /* pdu + rsvd */ |
| |
| /* |
| * NOTE: The buffer is allocated within avrc_bld_init_rsp_buffer(), and is |
| * always of size BT_DEFAULT_BUFFER_SIZE. |
| */ |
| len_left = BT_DEFAULT_BUFFER_SIZE - BT_HDR_SIZE - p_pkt->offset - p_pkt->len; |
| |
| BE_STREAM_TO_UINT16(len, p_data); |
| p_count = p_data; |
| |
| if (len == 0) { |
| *p_count = 0; |
| p_data++; |
| } else { |
| p_data = p_start + p_pkt->len; |
| } |
| |
| for (xx = 0; xx < p_rsp->num_attr; xx++) { |
| if (len_left < (p_rsp->p_attrs[xx].str_len + 4)) { |
| log::error("out of room (str_len:{}, left:{})", |
| p_rsp->p_attrs[xx].str_len, len_left); |
| p_rsp->num_attr = num_added; |
| sts = AVRC_STS_INTERNAL_ERR; |
| break; |
| } |
| if (!p_rsp->p_attrs[xx].str_len || !p_rsp->p_attrs[xx].p_str) { |
| log::error("NULL attr text[{}]", xx); |
| continue; |
| } |
| UINT8_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].attr_id); |
| UINT16_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].charset_id); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].str_len); |
| ARRAY_TO_BE_STREAM(p_data, p_rsp->p_attrs[xx].p_str, |
| p_rsp->p_attrs[xx].str_len); |
| (*p_count)++; |
| num_added++; |
| } |
| len = p_data - p_count; |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| |
| return sts; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_app_setting_attr_text_rsp |
| * |
| * Description This function builds the Get Application Setting Attribute |
| * Text response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_app_setting_attr_text_rsp( |
| tAVRC_GET_APP_ATTR_TXT_RSP* p_rsp, BT_HDR* p_pkt) { |
| log::verbose(""); |
| return avrc_bld_app_setting_text_rsp(p_rsp, p_pkt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_app_setting_value_text_rsp |
| * |
| * Description This function builds the Get Application Setting Value Text |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_app_setting_value_text_rsp( |
| tAVRC_GET_APP_ATTR_TXT_RSP* p_rsp, BT_HDR* p_pkt) { |
| log::verbose(""); |
| return avrc_bld_app_setting_text_rsp(p_rsp, p_pkt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_inform_charset_rsp |
| * |
| * Description This function builds the Inform Displayable Character Set |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_inform_charset_rsp(UNUSED_ATTR tAVRC_RSP* p_rsp, |
| UNUSED_ATTR BT_HDR* p_pkt) { |
| /* nothing to be added. */ |
| log::verbose(""); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_inform_battery_status_rsp |
| * |
| * Description This function builds the Inform Battery Status |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_inform_battery_status_rsp( |
| UNUSED_ATTR tAVRC_RSP* p_rsp, UNUSED_ATTR BT_HDR* p_pkt) { |
| /* nothing to be added. */ |
| log::verbose(""); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| static void avrc_build_attribute_entries(int num_attrs, |
| tAVRC_ATTR_ENTRY* p_attrs, |
| int remaining_buffer_capacity, |
| uint8_t** pp_data, |
| uint8_t* p_attribute_count) { |
| log::verbose("num_attrs: {}, remaining_buffer_capacity: {}", num_attrs, |
| remaining_buffer_capacity); |
| uint8_t* p_data = *pp_data; |
| /* Fill in the Attribute ID, Character Set, Length and Values */ |
| for (int index = 0; index < num_attrs; index++) { |
| log::verbose("attr id[{}]: {}", index, p_attrs[index].attr_id); |
| CHECK(AVRC_IS_VALID_MEDIA_ATTRIBUTE(p_attrs[index].attr_id)); |
| if (!p_attrs[index].name.p_str) { |
| p_attrs[index].name.str_len = 0; |
| } |
| /* 8 is the size of attr_id, char set and str_len */ |
| remaining_buffer_capacity -= 8; |
| if (remaining_buffer_capacity < 0) { |
| log::warn( |
| "not enough buffer space for attr_id[{}]: {}, skipping {} attributes", |
| index, p_attrs[index].attr_id, num_attrs - index); |
| break; |
| } |
| if (remaining_buffer_capacity < p_attrs[index].name.str_len) { |
| log::warn( |
| "not enough buffer space for attr_id[{}]: {}, truncating attribute", |
| index, p_attrs[index].attr_id); |
| p_attrs[index].name.str_len = remaining_buffer_capacity; |
| remaining_buffer_capacity = 0; |
| } |
| remaining_buffer_capacity -= p_attrs[index].name.str_len; |
| UINT32_TO_BE_STREAM(p_data, p_attrs[index].attr_id); |
| UINT16_TO_BE_STREAM(p_data, p_attrs[index].name.charset_id); |
| UINT16_TO_BE_STREAM(p_data, p_attrs[index].name.str_len); |
| ARRAY_TO_BE_STREAM(p_data, p_attrs[index].name.p_str, |
| p_attrs[index].name.str_len); |
| (*p_attribute_count)++; |
| } |
| *pp_data = p_data; |
| log::verbose("filled attributes, remaining_buffer_capacity: {}", |
| remaining_buffer_capacity); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_elem_attrs_rsp |
| * |
| * Description This function builds the Get Element Attributes |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_elem_attrs_rsp(tAVRC_GET_ATTRS_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| log::verbose(""); |
| if (!p_rsp->p_attrs) { |
| log::error("NULL p_attrs"); |
| return AVRC_STS_BAD_PARAM; |
| } |
| /* Figure out how much we have left in current buffer */ |
| int remaining_buffer_capacity = |
| BT_DEFAULT_BUFFER_SIZE - BT_HDR_SIZE - p_pkt->offset; |
| if (remaining_buffer_capacity < 5) { |
| log::error("{} not enough buffer for packet header", |
| remaining_buffer_capacity); |
| return AVRC_STS_INTERNAL_ERR; |
| } |
| /* Get to the beginning of PDU */ |
| uint8_t* p_pdu_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| /* Skip PDU ID and Reserved byte to get pointer to Parameter Length */ |
| uint8_t *p_data, *p_parameter_len; |
| p_data = p_parameter_len = p_pdu_start + 2; |
| /* Parse parameter length */ |
| uint16_t parameter_len; |
| BE_STREAM_TO_UINT16(parameter_len, p_data); |
| /* Get pointer to Attribute Count */ |
| uint8_t* p_attribute_count = p_data; |
| /* Initialize field values when Parameter Length is 0 */ |
| if (parameter_len == 0) { |
| *p_attribute_count = 0; |
| p_data++; |
| } else { |
| // TODO: Why do we need this case? |
| p_data = p_pdu_start + p_pkt->len; |
| } |
| remaining_buffer_capacity -= p_data - p_pdu_start; |
| ; |
| if (remaining_buffer_capacity < 0) { |
| log::error("not enough buffer capacity for response"); |
| return AVRC_STS_BAD_PARAM; |
| } |
| /* Fill in the Attribute ID, Character Set, Length and Values */ |
| avrc_build_attribute_entries(p_rsp->num_attrs, p_rsp->p_attrs, |
| remaining_buffer_capacity, &p_data, |
| p_attribute_count); |
| parameter_len = p_data - p_attribute_count; |
| UINT16_TO_BE_STREAM(p_parameter_len, parameter_len); |
| p_pkt->len = (p_data - p_pdu_start); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_play_status_rsp |
| * |
| * Description This function builds the Get Play Status |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_play_status_rsp(tAVRC_GET_PLAY_STATUS_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start; |
| |
| log::verbose(""); |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_start + 2; |
| |
| /* add fixed lenth - song len(4) + song position(4) + status(1) */ |
| UINT16_TO_BE_STREAM(p_data, 9); |
| UINT32_TO_BE_STREAM(p_data, p_rsp->song_len); |
| UINT32_TO_BE_STREAM(p_data, p_rsp->song_pos); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->play_status); |
| p_pkt->len = (p_data - p_start); |
| |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_notify_rsp |
| * |
| * Description This function builds the Notification response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_notify_rsp(tAVRC_REG_NOTIF_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start; |
| uint8_t* p_len; |
| uint16_t len = 0; |
| uint8_t xx; |
| tAVRC_STS status = AVRC_STS_NO_ERROR; |
| |
| log::verbose("event_id {}", p_rsp->event_id); |
| |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 2; /* pdu + rsvd */ |
| p_data += 2; |
| |
| UINT8_TO_BE_STREAM(p_data, p_rsp->event_id); |
| switch (p_rsp->event_id) { |
| case AVRC_EVT_PLAY_STATUS_CHANGE: /* 0x01 */ |
| /* p_rsp->param.play_status >= AVRC_PLAYSTATE_STOPPED is always true */ |
| if ((p_rsp->param.play_status <= AVRC_PLAYSTATE_REV_SEEK) || |
| (p_rsp->param.play_status == AVRC_PLAYSTATE_ERROR)) { |
| UINT8_TO_BE_STREAM(p_data, p_rsp->param.play_status); |
| len = 2; |
| } else { |
| log::error("bad play state"); |
| status = AVRC_STS_BAD_PARAM; |
| } |
| break; |
| |
| case AVRC_EVT_TRACK_CHANGE: /* 0x02 */ |
| ARRAY_TO_BE_STREAM(p_data, p_rsp->param.track, AVRC_UID_SIZE); |
| len = (uint8_t)(AVRC_UID_SIZE + 1); |
| break; |
| |
| case AVRC_EVT_TRACK_REACHED_END: /* 0x03 */ |
| case AVRC_EVT_TRACK_REACHED_START: /* 0x04 */ |
| case AVRC_EVT_NOW_PLAYING_CHANGE: /* 0x09 */ |
| case AVRC_EVT_AVAL_PLAYERS_CHANGE: /* 0x0a */ |
| len = 1; |
| break; |
| |
| case AVRC_EVT_PLAY_POS_CHANGED: /* 0x05 */ |
| UINT32_TO_BE_STREAM(p_data, p_rsp->param.play_pos); |
| len = 5; |
| break; |
| |
| case AVRC_EVT_BATTERY_STATUS_CHANGE: /* 0x06 */ |
| if (AVRC_IS_VALID_BATTERY_STATUS(p_rsp->param.battery_status)) { |
| UINT8_TO_BE_STREAM(p_data, p_rsp->param.battery_status); |
| len = 2; |
| } else { |
| log::error("bad battery status"); |
| status = AVRC_STS_BAD_PARAM; |
| } |
| break; |
| |
| case AVRC_EVT_SYSTEM_STATUS_CHANGE: /* 0x07 */ |
| if (AVRC_IS_VALID_SYSTEM_STATUS(p_rsp->param.system_status)) { |
| UINT8_TO_BE_STREAM(p_data, p_rsp->param.system_status); |
| len = 2; |
| } else { |
| log::error("bad system status"); |
| status = AVRC_STS_BAD_PARAM; |
| } |
| break; |
| |
| case AVRC_EVT_APP_SETTING_CHANGE: /* 0x08 */ |
| if (p_rsp->param.player_setting.num_attr > AVRC_MAX_APP_SETTINGS) |
| p_rsp->param.player_setting.num_attr = AVRC_MAX_APP_SETTINGS; |
| |
| if (p_rsp->param.player_setting.num_attr > 0) { |
| UINT8_TO_BE_STREAM(p_data, p_rsp->param.player_setting.num_attr); |
| len = 2; |
| for (xx = 0; xx < p_rsp->param.player_setting.num_attr; xx++) { |
| if (avrc_is_valid_player_attrib_value( |
| p_rsp->param.player_setting.attr_id[xx], |
| p_rsp->param.player_setting.attr_value[xx])) { |
| UINT8_TO_BE_STREAM(p_data, p_rsp->param.player_setting.attr_id[xx]); |
| UINT8_TO_BE_STREAM(p_data, |
| p_rsp->param.player_setting.attr_value[xx]); |
| } else { |
| log::error("bad player app seeting attribute or value"); |
| status = AVRC_STS_BAD_PARAM; |
| break; |
| } |
| len += 2; |
| } |
| } else |
| status = AVRC_STS_BAD_PARAM; |
| break; |
| |
| case AVRC_EVT_VOLUME_CHANGE: /* 0x0d */ |
| len = 2; |
| UINT8_TO_BE_STREAM(p_data, (AVRC_MAX_VOLUME & p_rsp->param.volume)); |
| break; |
| |
| case AVRC_EVT_ADDR_PLAYER_CHANGE: /* 0x0b */ |
| UINT16_TO_BE_STREAM(p_data, p_rsp->param.addr_player.player_id); |
| UINT16_TO_BE_STREAM(p_data, p_rsp->param.addr_player.uid_counter); |
| len = 5; |
| break; |
| |
| case AVRC_EVT_UIDS_CHANGE: /* 0x0c */ |
| UINT16_TO_BE_STREAM(p_data, p_rsp->param.uid_counter); /* uid counter */ |
| len = 3; |
| break; |
| |
| default: |
| status = AVRC_STS_BAD_PARAM; |
| log::error("unknown event_id"); |
| } |
| |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| |
| return status; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_next_rsp |
| * |
| * Description This function builds the Request Continue or Abort |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_next_rsp(tAVRC_NEXT_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t* p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| uint8_t* p_data = (p_start + 2); /* Skip the pdu and reserved bits */ |
| |
| UINT16_TO_BE_STREAM(p_data, 0x0001); /* only one attribute to be sent */ |
| UINT8_TO_BE_STREAM(p_data, p_rsp->target_pdu); |
| |
| log::verbose("target_pdu: 0x{:02x}", p_rsp->target_pdu); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /***************************************************************************** |
| * |
| * Function avrc_bld_set_absolute_volume_rsp |
| * |
| * Description This function builds the set absolute volume response |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is build successfully |
| * |
| *****************************************************************************/ |
| static tAVRC_STS avrc_bld_set_absolute_volume_rsp(uint8_t abs_vol, |
| BT_HDR* p_pkt) { |
| log::verbose(""); |
| uint8_t* p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| /* To calculate length */ |
| uint8_t* p_data = p_start + 2; |
| /* add fixed lenth status(1) */ |
| UINT16_TO_BE_STREAM(p_data, 1); |
| UINT8_TO_BE_STREAM(p_data, abs_vol); |
| p_pkt->len = (p_data - p_start); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_group_navigation_rsp |
| * |
| * Description This function builds the Group Navigation |
| * response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| tAVRC_STS avrc_bld_group_navigation_rsp(uint16_t navi_id, BT_HDR* p_pkt) { |
| if (!AVRC_IS_VALID_GROUP(navi_id)) { |
| log::error("bad navigation op id: {}", navi_id); |
| return AVRC_STS_BAD_PARAM; |
| } |
| log::verbose(""); |
| uint8_t* p_data = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| UINT16_TO_BE_STREAM(p_data, navi_id); |
| p_pkt->len = 2; |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_rejected_rsp |
| * |
| * Description This function builds the General Response response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_rejected_rsp(tAVRC_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t* p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| uint8_t* p_data; |
| uint8_t opcode = p_rsp->opcode; |
| |
| log::verbose("status={}, pdu:x{:x}, opcode={:x}", p_rsp->status, p_rsp->pdu, |
| opcode); |
| |
| if (opcode == AVRC_OP_BROWSE) { |
| p_data = p_start + 1; |
| if ((AVRC_PDU_INVALID == *p_start) || |
| (avrc_opcode_from_pdu(*p_start) != AVRC_OP_BROWSE)) { |
| /* if invalid or the given opcode is not recognized as a browsing command |
| * opcode, */ |
| /* use general reject command */ |
| *p_start = AVRC_PDU_GENERAL_REJECT; |
| } |
| } else { |
| p_data = p_start + 2; |
| } |
| log::verbose("pdu:x{:x}, Opcode:{:x}", *p_start, opcode); |
| UINT16_TO_BE_STREAM(p_data, 1); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| p_pkt->len = p_data - p_start; |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /***************************************************************************** |
| * the following commands are introduced in AVRCP 1.4 |
| ****************************************************************************/ |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_ctrl_status_rsp |
| * |
| * Description This function builds the responses with a uint8_t parameter. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_ctrl_status_rsp(tAVRC_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t* p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| log::verbose("pdu:x{:x}", *p_start); |
| |
| /* To calculate length */ |
| uint8_t* p_data = p_start + 2; /* pdu + rsvd */ |
| |
| /* add fixed lenth - status(1) */ |
| UINT16_TO_BE_STREAM(p_data, 1); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| p_pkt->len = (p_data - p_start); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_set_addr_player_rsp |
| * |
| * Description This function builds the Set Addresses Player response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_set_addr_player_rsp(tAVRC_RSP* p_rsp, BT_HDR* p_pkt) { |
| log::verbose(""); |
| return avrc_bld_ctrl_status_rsp(p_rsp, p_pkt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_set_browsed_player_rsp |
| * |
| * Description This function builds the Set Browsed Player response. |
| * |
| * This message goes through the Browsing channel |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_set_browsed_player_rsp(tAVRC_SET_BR_PLAYER_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start; |
| uint8_t* p_len; |
| uint16_t len; |
| tAVRC_NAME* p_folders = p_rsp->p_folders; |
| uint16_t len_left; |
| uint8_t* p_folder_depth; |
| uint16_t mtu; |
| |
| /* make sure the given buffer can accomodate this response */ |
| len_left = BT_DEFAULT_BUFFER_SIZE - BT_HDR_SIZE; |
| p_data = (uint8_t*)(p_pkt + 1); |
| BE_STREAM_TO_UINT16(mtu, p_data); |
| if (len_left > mtu) { |
| len_left = mtu; |
| } |
| len_left = len_left - p_pkt->offset - p_pkt->len; |
| log::verbose("len_left:{}, mtu:{} ", len_left, mtu); |
| |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 1; /* pdu */ |
| |
| /* the existing len */ |
| BE_STREAM_TO_UINT16(len, p_data); |
| /* find the position to add the folder depth. |
| * 9 is sizeof (status + uid_counter + num_items + charset_id) */ |
| p_folder_depth = p_data + 9; |
| if (len == 0) { |
| /* first time initialize the attribute count */ |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| UINT16_TO_BE_STREAM(p_data, p_rsp->uid_counter); |
| UINT32_TO_BE_STREAM(p_data, p_rsp->num_items); |
| UINT16_TO_BE_STREAM(p_data, p_rsp->charset_id); |
| *p_folder_depth = 0; |
| p_data++; |
| len = 10; |
| /* assuming that we would never use a buffer that is too small for headers |
| */ |
| len_left -= 12; |
| } else { |
| p_data = p_start + p_pkt->len; |
| } |
| |
| for (uint8_t xx = 0; |
| (xx < p_rsp->folder_depth) && (len_left > (p_folders[xx].str_len + 2)); |
| xx++) { |
| (*p_folder_depth)++; |
| UINT16_TO_BE_STREAM(p_data, p_folders[xx].str_len); |
| ARRAY_TO_BE_STREAM(p_data, p_folders[xx].p_str, p_folders[xx].str_len); |
| len += (p_folders[xx].str_len + 2); |
| } |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_folder_items_rsp |
| * |
| * Description This function builds the Get Folder Items response. |
| * The error code is returned in *p_status. |
| * AVRC_STS_INTERNAL_ERR means no buffers. |
| * Try again later or with smaller item_count |
| * |
| * This message goes through the Browsing channel |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * AVRC_STS_INTERNAL_ERR, if the given buffer does not have |
| * enough room |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_folder_items_rsp(tAVRC_GET_ITEMS_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start; |
| uint8_t *p_len, xx; |
| uint16_t len; |
| size_t item_len; |
| uint8_t *p_item_len, yy; |
| tAVRC_ITEM_PLAYER* p_player; |
| tAVRC_ITEM_FOLDER* p_folder; |
| tAVRC_ITEM_MEDIA* p_media; |
| tAVRC_ATTR_ENTRY* p_attr; |
| tAVRC_ITEM* p_item_list = p_rsp->p_item_list; |
| tAVRC_STS status = AVRC_STS_NO_ERROR; |
| uint16_t len_left; |
| uint8_t *p_num, *p; |
| uint8_t *p_item_start, *p_attr_count; |
| uint16_t item_count; |
| uint16_t mtu; |
| bool multi_items_add_fail = false; |
| log::verbose(""); |
| |
| /* make sure the given buffer can accomodate this response */ |
| len_left = BT_DEFAULT_BUFFER_SIZE - BT_HDR_SIZE; |
| p = (uint8_t*)(p_pkt + 1); |
| BE_STREAM_TO_UINT16(mtu, p); |
| if (len_left > mtu) len_left = mtu; |
| |
| // according to spec |
| // Version 5.3 | Vol 3, Part A, Chapter 5 |
| // MTU may be controlled by the peer |
| if (len_left < p_pkt->offset + p_pkt->len) { |
| log::error("memory not enough (len_left={})", len_left); |
| return AVRC_STS_INTERNAL_ERR; |
| } |
| |
| len_left = len_left - p_pkt->offset - p_pkt->len; |
| |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 1; /* pdu */ |
| |
| /* the existing len */ |
| BE_STREAM_TO_UINT16(len, p_data); |
| p_num = p_data + 3; |
| if (len == 0) { |
| /* first time initialize the attribute count */ |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| UINT16_TO_BE_STREAM(p_data, p_rsp->uid_counter); |
| item_count = 0; |
| p_data += 2; |
| len = 5; |
| |
| if (len_left < 5) { |
| log::error("memory not enough (len_left={})", len_left); |
| return AVRC_STS_INTERNAL_ERR; |
| } |
| |
| len_left -= 5; |
| } else { |
| p_data = p_start + p_pkt->len; |
| p = p_num; |
| BE_STREAM_TO_UINT16(item_count, p); |
| } |
| log::verbose("len:{}, len_left:{}, num:{}", len, len_left, item_count); |
| |
| /* min len required = item_type(1) + item len(2) + min item (14) = 17 */ |
| for (xx = 0; |
| xx < p_rsp->item_count && len_left > AVRC_MIN_LEN_GET_FOLDER_ITEMS_RSP && |
| !multi_items_add_fail; |
| xx++) { |
| p_item_start = p_data; |
| UINT8_TO_BE_STREAM(p_data, p_item_list[xx].item_type); |
| /* variable item lenth - save the location to add length */ |
| p_item_len = p_data; |
| p_data += 2; |
| item_len = 0; |
| const uint16_t item_header_len = 3; /* item_type(1) + item len(2) */ |
| uint16_t item_len_left = len_left - item_header_len; |
| switch (p_item_list[xx].item_type) { |
| case AVRC_ITEM_PLAYER: |
| /* min len required: 2 + 1 + 4 + 1 + 16 + 2 + 2 = 30 + str_len */ |
| p_player = &p_item_list[xx].u.player; |
| item_len = AVRC_FEATURE_MASK_SIZE + p_player->name.str_len + 12; |
| |
| if ((item_len_left < item_len) || |
| !AVRC_ITEM_PLAYER_IS_VALID(p_player)) { |
| if (item_len_left < item_len && item_count > 0) { |
| multi_items_add_fail = true; |
| } |
| p_data = p_item_start; |
| break; |
| } |
| UINT16_TO_BE_STREAM(p_data, p_player->player_id); |
| UINT8_TO_BE_STREAM(p_data, p_player->major_type); |
| UINT32_TO_BE_STREAM(p_data, p_player->sub_type); |
| UINT8_TO_BE_STREAM(p_data, p_player->play_status); |
| ARRAY_TO_BE_STREAM(p_data, p_player->features, AVRC_FEATURE_MASK_SIZE); |
| UINT16_TO_BE_STREAM(p_data, p_player->name.charset_id); |
| UINT16_TO_BE_STREAM(p_data, p_player->name.str_len); |
| ARRAY_TO_BE_STREAM(p_data, p_player->name.p_str, |
| p_player->name.str_len); |
| break; |
| |
| case AVRC_ITEM_FOLDER: |
| /* min len required: 8 + 1 + 1 + 2 + 2 = 14 + str_len */ |
| p_folder = &p_item_list[xx].u.folder; |
| item_len = AVRC_UID_SIZE + p_folder->name.str_len + 6; |
| |
| if ((item_len_left < item_len) || !p_folder->name.p_str || |
| p_folder->type > AVRC_FOLDER_TYPE_YEARS) { |
| if (item_len_left < item_len && item_count > 0) { |
| multi_items_add_fail = true; |
| } |
| p_data = p_item_start; |
| break; |
| } |
| ARRAY_TO_BE_STREAM(p_data, p_folder->uid, AVRC_UID_SIZE); |
| UINT8_TO_BE_STREAM(p_data, p_folder->type); |
| UINT8_TO_BE_STREAM(p_data, p_folder->playable); |
| UINT16_TO_BE_STREAM(p_data, p_folder->name.charset_id); |
| UINT16_TO_BE_STREAM(p_data, p_folder->name.str_len); |
| ARRAY_TO_BE_STREAM(p_data, p_folder->name.p_str, |
| p_folder->name.str_len); |
| break; |
| |
| case AVRC_ITEM_MEDIA: |
| /* min len required: 8 + 1 + 2 + 2 + 1 = 14 + str_len */ |
| p_media = &p_item_list[xx].u.media; |
| item_len = AVRC_UID_SIZE + p_media->name.str_len + 6; |
| |
| if ((item_len_left < item_len) || !p_media->name.p_str || |
| p_media->type > AVRC_MEDIA_TYPE_VIDEO) { |
| if (item_len_left < item_len && item_count > 0) { |
| multi_items_add_fail = true; |
| } |
| p_data = p_item_start; |
| break; |
| } |
| ARRAY_TO_BE_STREAM(p_data, p_media->uid, AVRC_UID_SIZE); |
| UINT8_TO_BE_STREAM(p_data, p_media->type); |
| UINT16_TO_BE_STREAM(p_data, p_media->name.charset_id); |
| UINT16_TO_BE_STREAM(p_data, p_media->name.str_len); |
| ARRAY_TO_BE_STREAM(p_data, p_media->name.p_str, p_media->name.str_len); |
| p_attr_count = p_data++; |
| *p_attr_count = 0; |
| uint16_t attribute_len_left = item_len_left - item_len; |
| p_attr = p_media->p_attr_list; |
| for (yy = 0; yy < p_media->attr_count; yy++) { |
| /* len required: 4 + 2 + 2 + str_len */ |
| const size_t attribute_len = p_attr[yy].name.str_len + 8; |
| if (attribute_len_left < attribute_len || !p_attr[yy].name.p_str || |
| AVRC_IS_VALID_MEDIA_ATTRIBUTE(p_attr[yy].attr_id)) { |
| if (attribute_len_left < attribute_len && item_count > 0) { |
| multi_items_add_fail = true; |
| p_data = p_item_start; |
| break; |
| } |
| continue; |
| } |
| (*p_attr_count)++; |
| UINT32_TO_BE_STREAM(p_data, p_attr[yy].attr_id); |
| UINT16_TO_BE_STREAM(p_data, p_attr[yy].name.charset_id); |
| UINT16_TO_BE_STREAM(p_data, p_attr[yy].name.str_len); |
| ARRAY_TO_BE_STREAM(p_data, p_attr[yy].name.p_str, |
| p_attr[yy].name.str_len); |
| item_len += attribute_len; |
| attribute_len_left -= attribute_len; |
| } |
| break; |
| } /* switch item_type */ |
| |
| if (p_item_start != p_data) { |
| /* successfully added the item */ |
| item_count++; |
| /* fill in variable item lenth */ |
| UINT16_TO_BE_STREAM(p_item_len, item_len); |
| len_left -= item_len + item_header_len; |
| len += item_len + item_header_len; |
| } else if (!multi_items_add_fail) { |
| /* some item is not added properly - set an error status */ |
| if (item_len_left < item_len) |
| status = AVRC_STS_INTERNAL_ERR; |
| else |
| status = AVRC_STS_BAD_PARAM; |
| break; |
| } |
| log::verbose("len:{}, len_left:{}, num:{}, item_len:{}", len, len_left, |
| item_count, item_len); |
| } /* for item_count */ |
| |
| UINT16_TO_BE_STREAM(p_num, item_count); |
| UINT16_TO_BE_STREAM(p_len, len); |
| p_pkt->len = (p_data - p_start); |
| |
| return status; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_change_path_rsp |
| * |
| * Description This function builds the Change Path response. |
| * |
| * This message goes through the Browsing channel |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_change_path_rsp(tAVRC_CHG_PATH_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start; |
| |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_start + 1; /* pdu */ |
| /* add fixed length - status(1) + num_items(4) */ |
| UINT16_TO_BE_STREAM(p_data, 5); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| UINT32_TO_BE_STREAM(p_data, p_rsp->num_items); |
| p_pkt->len = (p_data - p_start); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_attrs_rsp |
| * |
| * Description This function builds the GetItemAttributes response, |
| * |
| * The Get Item Attributes message goes through the |
| * Browsing channel (already specified in the |p_pkt|) |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * AVRC_STS_INTERNAL_ERR, if the given buffer does not have |
| * enough room |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_item_attrs_rsp(tAVRC_GET_ATTRS_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| log::verbose(""); |
| if (!p_rsp->p_attrs) { |
| log::error("NULL p_attrs"); |
| return AVRC_STS_BAD_PARAM; |
| } |
| /* Figure out how much we have left in current buffer */ |
| int remaining_buffer_capacity = |
| BT_DEFAULT_BUFFER_SIZE - BT_HDR_SIZE - p_pkt->offset; |
| /* Get to the beginning of data section in buffer */ |
| uint8_t* p_data = (uint8_t*)(p_pkt + 1); |
| /* Get the MTU size that is filled in earlier */ |
| uint16_t mtu; |
| BE_STREAM_TO_UINT16(mtu, p_data); |
| if (remaining_buffer_capacity > mtu) { |
| remaining_buffer_capacity = mtu; |
| } |
| log::verbose("remaining_buffer_capacity:{}, mtu:{}", |
| remaining_buffer_capacity, mtu); |
| if (remaining_buffer_capacity < 5) { |
| log::error("not enough space for packet header, remaining:{} < 5", |
| remaining_buffer_capacity); |
| return AVRC_STS_INTERNAL_ERR; |
| } |
| /* Get to the beginning of PDU */ |
| uint8_t* p_pdu_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| /* Skip PDU ID to get pointer to Parameter length */ |
| uint8_t* p_parameter_len; |
| p_data = p_parameter_len = p_pdu_start + 1; |
| /* Parse existing parameter length */ |
| uint16_t parameter_len; |
| BE_STREAM_TO_UINT16(parameter_len, p_data); |
| /* Skip one byte to Number of Attributes */ |
| uint8_t* p_status = p_data++; |
| uint8_t* p_attribute_count = p_data++; |
| if (parameter_len == 0) { |
| /* First time, initialize the status byte */ |
| *p_status = p_rsp->status; |
| if (p_rsp->status != AVRC_STS_NO_ERROR) { |
| // TODO(siyuanh): This is a hack |
| parameter_len = 1; |
| UINT16_TO_BE_STREAM(p_parameter_len, parameter_len); |
| p_pkt->len = p_status - p_pdu_start; |
| return AVRC_STS_NO_ERROR; |
| } |
| *p_attribute_count = 0; |
| } else { |
| // TODO(siyuanh): Why do wee need this case? |
| p_data = p_pdu_start + p_pkt->len; |
| } |
| remaining_buffer_capacity -= p_data - p_pdu_start; |
| /* Fill in the Attribute ID, Character Set, Length and Values */ |
| avrc_build_attribute_entries(p_rsp->num_attrs, p_rsp->p_attrs, |
| remaining_buffer_capacity, &p_data, |
| p_attribute_count); |
| parameter_len = p_data - p_status; |
| UINT16_TO_BE_STREAM(p_parameter_len, parameter_len); |
| p_pkt->len = p_data - p_pdu_start; |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_get_num_of_item_rsp |
| * |
| * Description This function builds the Get Total Number of Items response. |
| * |
| * This message goes through the Browsing channel |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * AVRC_STS_INTERNAL_ERR, if the given buffer does not have |
| * enough room |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_get_num_of_item_rsp(tAVRC_GET_NUM_OF_ITEMS_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start, *p_len; |
| |
| log::verbose(""); |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 1; /* pdu */ |
| |
| if (p_rsp->status == AVRC_STS_NO_ERROR) { |
| /* add fixed lenth - status(1) + uid_counter(2) + num_items(4) */ |
| UINT16_TO_BE_STREAM(p_data, 7); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| UINT16_TO_BE_STREAM(p_data, p_rsp->uid_counter); |
| UINT32_TO_BE_STREAM(p_data, p_rsp->num_items); |
| p_pkt->len = (p_data - p_start); |
| return AVRC_STS_NO_ERROR; |
| } else { |
| /* add fixed lenth - status(1) */ |
| UINT16_TO_BE_STREAM(p_data, 7); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| p_pkt->len = (p_data - p_start); |
| return p_rsp->status; |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_search_rsp |
| * |
| * Description This function builds the Search response. |
| * |
| * This message goes through the Browsing channel |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_search_rsp(tAVRC_SEARCH_RSP* p_rsp, BT_HDR* p_pkt) { |
| uint8_t *p_data, *p_start, *p_len; |
| |
| log::verbose(""); |
| /* get the existing length, if any, and also the num attributes */ |
| p_start = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_data = p_len = p_start + 1; /* pdu */ |
| |
| /* add fixed lenth - status(1) + uid_counter(2) + num_items(4) */ |
| UINT16_TO_BE_STREAM(p_data, 7); |
| UINT8_TO_BE_STREAM(p_data, p_rsp->status); |
| UINT16_TO_BE_STREAM(p_data, p_rsp->uid_counter); |
| UINT32_TO_BE_STREAM(p_data, p_rsp->num_items); |
| p_pkt->len = (p_data - p_start); |
| return AVRC_STS_NO_ERROR; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_play_item_rsp |
| * |
| * Description This function builds the Play Item response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_play_item_rsp(tAVRC_RSP* p_rsp, BT_HDR* p_pkt) { |
| log::verbose(""); |
| return avrc_bld_ctrl_status_rsp(p_rsp, p_pkt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_add_to_now_playing_rsp |
| * |
| * Description This function builds the Add to Now Playing response. |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| static tAVRC_STS avrc_bld_add_to_now_playing_rsp(tAVRC_RSP* p_rsp, |
| BT_HDR* p_pkt) { |
| log::verbose(""); |
| return avrc_bld_ctrl_status_rsp(p_rsp, p_pkt); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avrc_bld_init_rsp_buffer |
| * |
| * Description This function initializes the response buffer based on PDU |
| * |
| * Returns NULL, if no buffer or failure to build the message. |
| * Otherwise, the buffer that contains the initialized message. |
| * |
| ******************************************************************************/ |
| static BT_HDR* avrc_bld_init_rsp_buffer(tAVRC_RESPONSE* p_rsp) { |
| uint16_t offset = 0; |
| uint16_t chnl = AVCT_DATA_CTRL; |
| uint8_t opcode = avrc_opcode_from_pdu(p_rsp->pdu); |
| |
| log::verbose("pdu={:x}, opcode={:x}/{:x}", p_rsp->pdu, opcode, |
| p_rsp->rsp.opcode); |
| if (opcode != p_rsp->rsp.opcode && p_rsp->rsp.status != AVRC_STS_NO_ERROR && |
| avrc_is_valid_opcode(p_rsp->rsp.opcode)) { |
| opcode = p_rsp->rsp.opcode; |
| log::verbose("opcode={:x}", opcode); |
| } |
| |
| switch (opcode) { |
| case AVRC_OP_BROWSE: |
| chnl = AVCT_DATA_BROWSE; |
| offset = AVCT_BROWSE_OFFSET; |
| break; |
| |
| case AVRC_OP_PASS_THRU: |
| offset = AVRC_MSG_PASS_THRU_OFFSET; |
| break; |
| |
| case AVRC_OP_VENDOR: |
| offset = AVRC_MSG_VENDOR_OFFSET; |
| break; |
| } |
| |
| /* allocate and initialize the buffer */ |
| BT_HDR* p_pkt = (BT_HDR*)osi_calloc(BT_DEFAULT_BUFFER_SIZE); |
| uint8_t *p_data, *p_start; |
| |
| p_pkt->layer_specific = chnl; |
| p_pkt->event = opcode; |
| p_pkt->offset = offset; |
| p_data = (uint8_t*)(p_pkt + 1) + p_pkt->offset; |
| p_start = p_data; |
| |
| /* pass thru - group navigation - has a two byte op_id, so dont do it here */ |
| if (opcode != AVRC_OP_PASS_THRU) *p_data++ = p_rsp->pdu; |
| |
| switch (opcode) { |
| case AVRC_OP_VENDOR: |
| /* reserved 0, packet_type 0 */ |
| UINT8_TO_BE_STREAM(p_data, 0); |
| [[fallthrough]]; |
| case AVRC_OP_BROWSE: |
| /* add fixed lenth - 0 */ |
| UINT16_TO_BE_STREAM(p_data, 0); |
| break; |
| } |
| |
| p_pkt->len = (p_data - p_start); |
| p_rsp->rsp.opcode = opcode; |
| |
| return p_pkt; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function AVRC_BldResponse |
| * |
| * Description This function builds the given AVRCP response to the given |
| * buffer |
| * |
| * Returns AVRC_STS_NO_ERROR, if the response is built successfully |
| * Otherwise, the error code. |
| * |
| ******************************************************************************/ |
| tAVRC_STS AVRC_BldResponse(uint8_t handle, tAVRC_RESPONSE* p_rsp, |
| BT_HDR** pp_pkt) { |
| tAVRC_STS status = AVRC_STS_BAD_PARAM; |
| BT_HDR* p_pkt; |
| bool alloc = false; |
| uint8_t* p; |
| uint16_t peer_mtu; |
| |
| if (!p_rsp || !pp_pkt) { |
| log::verbose("Invalid parameters passed. p_rsp={}, pp_pkt={}", |
| fmt::ptr(p_rsp), fmt::ptr(pp_pkt)); |
| return AVRC_STS_BAD_PARAM; |
| } |
| |
| if (*pp_pkt == NULL) { |
| *pp_pkt = avrc_bld_init_rsp_buffer(p_rsp); |
| if (*pp_pkt == NULL) { |
| log::verbose("Failed to initialize response buffer"); |
| return AVRC_STS_INTERNAL_ERR; |
| } |
| |
| if ((*pp_pkt)->layer_specific == AVCT_DATA_BROWSE) { |
| p = (uint8_t*)((*pp_pkt) + 1); |
| peer_mtu = AVCT_GetBrowseMtu(handle) - AVCT_HDR_LEN_SINGLE; |
| UINT16_TO_BE_STREAM(p, peer_mtu); |
| } |
| |
| alloc = true; |
| } |
| status = AVRC_STS_NO_ERROR; |
| p_pkt = *pp_pkt; |
| |
| log::verbose("pdu={:x} status={:x}", p_rsp->rsp.pdu, p_rsp->rsp.status); |
| if (p_rsp->rsp.status != AVRC_STS_NO_ERROR) { |
| return (avrc_bld_rejected_rsp(&p_rsp->rsp, p_pkt)); |
| } |
| |
| switch (p_rsp->pdu) { |
| case AVRC_PDU_NEXT_GROUP: |
| case AVRC_PDU_PREV_GROUP: |
| status = avrc_bld_group_navigation_rsp(p_rsp->pdu, p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_CAPABILITIES: |
| status = avrc_bld_get_capability_rsp(&p_rsp->get_caps, p_pkt); |
| break; |
| |
| case AVRC_PDU_LIST_PLAYER_APP_ATTR: |
| status = |
| avrc_bld_list_app_settings_attr_rsp(&p_rsp->list_app_attr, p_pkt); |
| break; |
| |
| case AVRC_PDU_LIST_PLAYER_APP_VALUES: |
| status = |
| avrc_bld_list_app_settings_values_rsp(&p_rsp->list_app_values, p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: |
| status = avrc_bld_get_cur_app_setting_value_rsp(&p_rsp->get_cur_app_val, |
| p_pkt); |
| break; |
| |
| case AVRC_PDU_SET_PLAYER_APP_VALUE: |
| status = avrc_bld_set_app_setting_value_rsp(&p_rsp->set_app_val, p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: |
| status = avrc_bld_get_app_setting_attr_text_rsp(&p_rsp->get_app_attr_txt, |
| p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT: |
| status = avrc_bld_get_app_setting_value_text_rsp(&p_rsp->get_app_val_txt, |
| p_pkt); |
| break; |
| |
| case AVRC_PDU_INFORM_DISPLAY_CHARSET: |
| status = avrc_bld_inform_charset_rsp(&p_rsp->inform_charset, p_pkt); |
| break; |
| |
| case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT: |
| status = avrc_bld_inform_battery_status_rsp(&p_rsp->inform_battery_status, |
| p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_ELEMENT_ATTR: |
| status = avrc_bld_get_elem_attrs_rsp(&p_rsp->get_attrs, p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_PLAY_STATUS: |
| status = avrc_bld_get_play_status_rsp(&p_rsp->get_play_status, p_pkt); |
| break; |
| |
| case AVRC_PDU_REGISTER_NOTIFICATION: |
| status = avrc_bld_notify_rsp(&p_rsp->reg_notif, p_pkt); |
| break; |
| |
| case AVRC_PDU_REQUEST_CONTINUATION_RSP: |
| status = avrc_bld_next_rsp(&p_rsp->continu, p_pkt); |
| break; |
| |
| case AVRC_PDU_ABORT_CONTINUATION_RSP: |
| status = avrc_bld_next_rsp(&p_rsp->abort, p_pkt); |
| break; |
| |
| case AVRC_PDU_SET_ADDRESSED_PLAYER: |
| status = avrc_bld_set_addr_player_rsp(&p_rsp->addr_player, p_pkt); |
| break; |
| |
| case AVRC_PDU_PLAY_ITEM: |
| status = avrc_bld_play_item_rsp(&p_rsp->play_item, p_pkt); |
| break; |
| |
| case AVRC_PDU_SET_ABSOLUTE_VOLUME: |
| status = avrc_bld_set_absolute_volume_rsp(p_rsp->volume.volume, p_pkt); |
| break; |
| |
| case AVRC_PDU_ADD_TO_NOW_PLAYING: |
| status = avrc_bld_add_to_now_playing_rsp(&p_rsp->add_to_play, p_pkt); |
| break; |
| |
| case AVRC_PDU_SET_BROWSED_PLAYER: |
| status = avrc_bld_set_browsed_player_rsp(&p_rsp->br_player, p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_FOLDER_ITEMS: |
| status = avrc_bld_get_folder_items_rsp(&p_rsp->get_items, p_pkt); |
| break; |
| |
| case AVRC_PDU_CHANGE_PATH: |
| status = avrc_bld_change_path_rsp(&p_rsp->chg_path, p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_ITEM_ATTRIBUTES: |
| status = avrc_bld_get_item_attrs_rsp(&p_rsp->get_attrs, p_pkt); |
| break; |
| |
| case AVRC_PDU_GET_TOTAL_NUM_OF_ITEMS: |
| status = avrc_bld_get_num_of_item_rsp(&p_rsp->get_num_of_items, p_pkt); |
| break; |
| |
| case AVRC_PDU_SEARCH: |
| status = avrc_bld_search_rsp(&p_rsp->search, p_pkt); |
| break; |
| } |
| |
| if (alloc && (status != AVRC_STS_NO_ERROR)) { |
| osi_free(p_pkt); |
| *pp_pkt = NULL; |
| } |
| log::verbose("returning {}", status); |
| return status; |
| } |