/******************************************************************************* ** ** Function avrc_prs_get_elem_attrs_rsp ** ** Description This function parses the Get Element Attributes ** response. ** ** Returns AVRC_STS_NO_ERROR, if the response is parsed successfully ** Otherwise, the error code. ** *******************************************************************************/ static tAVRC_STS avrc_prs_get_elem_attrs_rsp (tAVRC_GET_ELEM_ATTRS_RSP *p_rsp, UINT8 *p_data) { UINT8 *p_start, *p_len, *p_count; UINT16 len; UINT8 xx; UINT32 attr_id; if (p_rsp->num_attr == 0) return AVRC_STS_NO_ERROR; AVRC_TRACE_API("avrc_prs_get_elem_attrs_rsp num_attr: %d", p_rsp->num_attr); p_rsp->p_attrs = (tAVRC_ATTR_ENTRY*)malloc(sizeof(tAVRC_ATTR_ENTRY) * p_rsp->num_attr); for (xx=0; xx<p_rsp->num_attr; xx++) { BE_STREAM_TO_UINT32(attr_id, p_data); if (!AVRC_IS_VALID_MEDIA_ATTRIBUTE(attr_id)) { AVRC_TRACE_ERROR("avrc_prs_get_elem_attrs_rsp invalid attr id[%d]: %d", xx, attr_id); continue; } p_rsp->p_attrs[xx].attr_id = attr_id; BE_STREAM_TO_UINT16(p_rsp->p_attrs[xx].name.charset_id, p_data); BE_STREAM_TO_UINT16(p_rsp->p_attrs[xx].name.str_len, p_data); p_rsp->p_attrs[xx].name.p_str = (UINT8*)malloc(sizeof(UINT8) * p_rsp->p_attrs[xx].name.str_len); BE_STREAM_TO_ARRAY(p_data, p_rsp->p_attrs[xx].name.p_str, p_rsp->p_attrs[xx].name.str_len); AVRC_TRACE_DEBUG("avrc_prs_get_elem_attrs_rsp str_len: %d, str: %s", p_rsp->p_attrs[xx].name.str_len, p_rsp->p_attrs[xx].name.p_str); } return AVRC_STS_NO_ERROR; }
/******************************************************************************* ** ** Function AVRC_ParsResponse ** ** Description This function is a superset of AVRC_ParsMetadata to parse the response. ** ** 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_ParsResponse (tAVRC_MSG *p_msg, tAVRC_RESPONSE *p_result, UINT8 *p_buf, UINT16 buf_len) { tAVRC_STS status = AVRC_STS_INTERNAL_ERR; UINT16 id; UNUSED(p_buf); UNUSED(buf_len); if (p_msg && p_result) { switch (p_msg->hdr.opcode) { case AVRC_OP_VENDOR: /* 0x00 Vendor-dependent commands */ status = avrc_pars_vendor_rsp(&p_msg->vendor, p_result); 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; default: AVRC_TRACE_ERROR("AVRC_ParsResponse() unknown opcode:0x%x", p_msg->hdr.opcode); break; } p_result->rsp.opcode = p_msg->hdr.opcode; p_result->rsp.status = status; } 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 *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; default: AVRC_TRACE_ERROR("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_DEBUG("AVRC_ParsCommand() return status:0x%x", status); return status; }
/****************************************************************************** ** ** Function avrc_send_continue_frag ** ** Description This function sends a continue response fragment ** ** Returns Nothing. ** ******************************************************************************/ static void avrc_send_continue_frag(UINT8 handle, UINT8 label) { tAVRC_FRAG_CB *p_fcb; BT_HDR *p_pkt_old, *p_pkt; UINT8 *p_old, *p_data; UINT8 cr = AVCT_RSP; tAVRC_RSP rej_rsp; p_fcb = &avrc_cb.fcb[handle]; p_pkt = p_fcb->p_fmsg; AVRC_TRACE_DEBUG("%s handle = %u label = %u len = %d", __func__, handle, label, p_pkt->len); if (p_pkt->len > AVRC_MAX_CTRL_DATA_LEN) { int offset_len = MAX(AVCT_MSG_OFFSET, p_pkt->offset); p_pkt_old = p_fcb->p_fmsg; p_pkt = (BT_HDR *)osi_malloc((UINT16)(AVRC_PACKET_LEN + offset_len + BT_HDR_SIZE)); if (p_pkt) { p_pkt->len = AVRC_MAX_CTRL_DATA_LEN; p_pkt->offset = AVCT_MSG_OFFSET; p_pkt->layer_specific = p_pkt_old->layer_specific; p_pkt->event = p_pkt_old->event; p_old = (UINT8 *)(p_pkt_old + 1) + p_pkt_old->offset; p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; memcpy (p_data, p_old, AVRC_MAX_CTRL_DATA_LEN); /* use AVRC continue packet type */ p_data += AVRC_VENDOR_HDR_SIZE; p_data++; /* pdu */ *p_data++ = AVRC_PKT_CONTINUE; /* 4=pdu, pkt_type & len */ UINT16_TO_BE_STREAM(p_data, (AVRC_MAX_CTRL_DATA_LEN - AVRC_VENDOR_HDR_SIZE - 4)); /* prepare the left over for as an end fragment */ avrc_prep_end_frag (handle); } else { /* use the current GKI buffer to send Internal error status */ p_pkt = p_fcb->p_fmsg; p_fcb->p_fmsg = NULL; p_fcb->frag_enabled = FALSE; AVRC_TRACE_ERROR ("AVRC_MsgReq no buffers for fragmentation - send internal error" ); p_data = (UINT8 *)(p_pkt + 1) + p_pkt->offset; *p_data++ = AVRC_PDU_REQUEST_CONTINUATION_RSP; *p_data++ = 0; UINT16_TO_BE_STREAM(p_data, 0); p_pkt->len = 4; rej_rsp.pdu = AVRC_PDU_REQUEST_CONTINUATION_RSP; rej_rsp.status = AVRC_STS_INTERNAL_ERR; AVRC_BldResponse( handle, (tAVRC_RESPONSE *)&rej_rsp, &p_pkt); cr = AVCT_RSP; } } else { /* end fragment. clean the control block */ p_fcb->frag_enabled = FALSE; p_fcb->p_fmsg = NULL; } AVCT_MsgReq( handle, label, cr, p_pkt); }
/******************************************************************************* ** ** Function avrc_is_valid_player_attrib_value ** ** Description Check if the given attrib value is valid for its attribute ** ** Returns returns TRUE if it is valid ** *******************************************************************************/ BOOLEAN avrc_is_valid_player_attrib_value(UINT8 attrib, UINT8 value) { BOOLEAN result = FALSE; switch (attrib) { case AVRC_PLAYER_SETTING_EQUALIZER: if ((value > 0) && (value <= AVRC_PLAYER_VAL_ON)) { result = TRUE; } break; case AVRC_PLAYER_SETTING_REPEAT: if ((value > 0) && (value <= AVRC_PLAYER_VAL_GROUP_REPEAT)) { result = TRUE; } break; case AVRC_PLAYER_SETTING_SHUFFLE: case AVRC_PLAYER_SETTING_SCAN: if ((value > 0) && (value <= AVRC_PLAYER_VAL_GROUP_SHUFFLE)) { result = TRUE; } break; } if (attrib >= AVRC_PLAYER_SETTING_LOW_MENU_EXT) { result = TRUE; } if (!result) AVRC_TRACE_ERROR( "avrc_is_valid_player_attrib_value() found not matching attrib(x%x)-value(x%x) pair!", attrib, value); return result; }
/******************************************************************************* ** ** 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; 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; /* 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; } p = p_msg->p_vendor_data; p_result->pdu = *p++; AVRC_TRACE_DEBUG("avrc_pars_vendor_cmd() pdu:0x%x", p_result->pdu); if (!AVRC_IsValidAvcType (p_result->pdu, p_msg->hdr.ctype)) { AVRC_TRACE_DEBUG("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_ERROR("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_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) { 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; } else { 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_SET_ABSOLUTE_VOLUME: { if (len != 1) { status = AVRC_STS_INTERNAL_ERR; } break; } /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */ /* case AVRC_PDU_ABORT_CONTINUATION_RSP: 0x41 */ default: status = AVRC_STS_BAD_CMD; break; } return status; }
/****************************************************************************** ** ** Function avrc_proc_vendor_command ** ** Description This function processes received vendor command. ** ** Returns if not NULL, the response to send right away. ** ******************************************************************************/ static BT_HDR *avrc_proc_vendor_command(UINT8 handle, UINT8 label, BT_HDR *p_pkt, tAVRC_MSG_VENDOR *p_msg) { BT_HDR *p_rsp = NULL; UINT8 *p_data; UINT8 *p_begin; UINT8 pkt_type; BOOLEAN abort_frag = FALSE; tAVRC_STS status = AVRC_STS_NO_ERROR; tAVRC_FRAG_CB *p_fcb; p_begin = (UINT8 *)(p_pkt + 1) + p_pkt->offset; p_data = p_begin + AVRC_VENDOR_HDR_SIZE; pkt_type = *(p_data + 1) & AVRC_PKT_TYPE_MASK; if (pkt_type != AVRC_PKT_SINGLE) { /* reject - commands can only be in single packets at AVRCP level */ AVRC_TRACE_ERROR ("commands must be in single packet pdu:0x%x", *p_data ); /* use the current GKI buffer to send the reject */ status = AVRC_STS_BAD_CMD; } /* check if there are fragments waiting to be sent */ else if (avrc_cb.fcb[handle].frag_enabled) { p_fcb = &avrc_cb.fcb[handle]; if (p_msg->company_id == AVRC_CO_METADATA) { switch (*p_data) { case AVRC_PDU_ABORT_CONTINUATION_RSP: /* aborted by CT - send accept response */ abort_frag = TRUE; p_begin = (UINT8 *)(p_pkt + 1) + p_pkt->offset; *p_begin = (AVRC_RSP_ACCEPT & AVRC_CTYPE_MASK); if (*(p_data + 4) != p_fcb->frag_pdu) { *p_begin = (AVRC_RSP_REJ & AVRC_CTYPE_MASK); *(p_data + 4) = AVRC_STS_BAD_PARAM; } else { p_data = (p_begin + AVRC_VENDOR_HDR_SIZE + 2); UINT16_TO_BE_STREAM(p_data, 0); p_pkt->len = (p_data - p_begin); } AVCT_MsgReq( handle, label, AVCT_RSP, p_pkt); p_msg->hdr.opcode = AVRC_OP_DROP; /* used the p_pkt to send response */ break; case AVRC_PDU_REQUEST_CONTINUATION_RSP: if (*(p_data + 4) == p_fcb->frag_pdu) { avrc_send_continue_frag(handle, label); p_msg->hdr.opcode = AVRC_OP_DROP_N_FREE; } else { /* the pdu id does not match - reject the command using the current GKI buffer */ AVRC_TRACE_ERROR("avrc_proc_vendor_command continue pdu: 0x%x does not match \ current re-assembly pdu: 0x%x", *(p_data + 4), p_fcb->frag_pdu); status = AVRC_STS_BAD_PARAM; abort_frag = TRUE; } break; default: /* implicit abort */ abort_frag = TRUE; } } else {