1 /******************************************************************************
2  *
3  *  Copyright (C) 2003-2016 Broadcom Corporation
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 #include <string.h>
19 
20 #include "avrc_api.h"
21 #include "avrc_defs.h"
22 #include "avrc_int.h"
23 #include "bt_common.h"
24 
25 /*****************************************************************************
26  *  Global data
27  ****************************************************************************/
28 #if (AVRC_METADATA_INCLUDED == TRUE)
29 
30 /*******************************************************************************
31  *
32  * Function         avrc_ctrl_pars_vendor_cmd
33  *
34  * Description      This function parses the vendor specific commands defined by
35  *                  Bluetooth SIG for AVRCP Conroller.
36  *
37  * Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed
38  *                  successfully.
39  *                  Otherwise, the error code defined by AVRCP 1.4
40  *
41  ******************************************************************************/
avrc_ctrl_pars_vendor_cmd(tAVRC_MSG_VENDOR * p_msg,tAVRC_COMMAND * p_result)42 static tAVRC_STS avrc_ctrl_pars_vendor_cmd(tAVRC_MSG_VENDOR* p_msg,
43                                            tAVRC_COMMAND* p_result) {
44   tAVRC_STS status = AVRC_STS_NO_ERROR;
45 
46   uint8_t* p = p_msg->p_vendor_data;
47   p_result->pdu = *p++;
48   AVRC_TRACE_DEBUG("%s pdu:0x%x", __func__, p_result->pdu);
49   if (!AVRC_IsValidAvcType(p_result->pdu, p_msg->hdr.ctype)) {
50     AVRC_TRACE_DEBUG("%s detects wrong AV/C type!", __func__);
51     status = AVRC_STS_BAD_CMD;
52   }
53 
54   p++; /* skip the reserved byte */
55   uint16_t len;
56   BE_STREAM_TO_UINT16(len, p);
57   if ((len + 4) != (p_msg->vendor_len)) {
58     status = AVRC_STS_INTERNAL_ERR;
59   }
60 
61   if (status != AVRC_STS_NO_ERROR) return status;
62 
63   switch (p_result->pdu) {
64     case AVRC_PDU_SET_ABSOLUTE_VOLUME: {
65       if (len != 1)
66         status = AVRC_STS_INTERNAL_ERR;
67       else {
68         BE_STREAM_TO_UINT8(p_result->volume.volume, p);
69         p_result->volume.volume = AVRC_MAX_VOLUME & p_result->volume.volume;
70       }
71       break;
72     }
73     case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */
74       BE_STREAM_TO_UINT8(p_result->reg_notif.event_id, p);
75       BE_STREAM_TO_UINT32(p_result->reg_notif.param, p);
76       break;
77     default:
78       status = AVRC_STS_BAD_CMD;
79       break;
80   }
81   return status;
82 }
83 
84 /*******************************************************************************
85  *
86  * Function         avrc_pars_vendor_cmd
87  *
88  * Description      This function parses the vendor specific commands defined by
89  *                  Bluetooth SIG
90  *
91  * Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed
92  *                  successfully.
93  *                  Otherwise, the error code defined by AVRCP 1.4
94  *
95  ******************************************************************************/
avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR * p_msg,tAVRC_COMMAND * p_result,uint8_t * p_buf,uint16_t buf_len)96 static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR* p_msg,
97                                       tAVRC_COMMAND* p_result, uint8_t* p_buf,
98                                       uint16_t buf_len) {
99   tAVRC_STS status = AVRC_STS_NO_ERROR;
100   uint8_t* p;
101   uint16_t len;
102   uint8_t xx, yy;
103   uint8_t* p_u8;
104   uint16_t* p_u16;
105   uint32_t u32, u32_2, *p_u32;
106   tAVRC_APP_SETTING* p_app_set;
107   uint16_t size_needed;
108 
109   /* Check the vendor data */
110   if (p_msg->vendor_len == 0) return AVRC_STS_NO_ERROR;
111   if (p_msg->p_vendor_data == NULL) return AVRC_STS_INTERNAL_ERR;
112 
113   p = p_msg->p_vendor_data;
114   p_result->pdu = *p++;
115   AVRC_TRACE_DEBUG("%s pdu:0x%x", __func__, p_result->pdu);
116   if (!AVRC_IsValidAvcType(p_result->pdu, p_msg->hdr.ctype)) {
117     AVRC_TRACE_DEBUG("%s detects wrong AV/C type(0x%x)!", __func__,
118                      p_msg->hdr.ctype);
119     status = AVRC_STS_BAD_CMD;
120   }
121 
122   p++; /* skip the reserved byte */
123   BE_STREAM_TO_UINT16(len, p);
124   if ((len + 4) != (p_msg->vendor_len)) {
125     AVRC_TRACE_ERROR("%s incorrect length :%d, %d", __func__, len,
126                      p_msg->vendor_len);
127     status = AVRC_STS_INTERNAL_ERR;
128   }
129 
130   if (status != AVRC_STS_NO_ERROR) return status;
131 
132   switch (p_result->pdu) {
133     case AVRC_PDU_GET_CAPABILITIES: /* 0x10 */
134       p_result->get_caps.capability_id = *p++;
135       if (!AVRC_IS_VALID_CAP_ID(p_result->get_caps.capability_id))
136         status = AVRC_STS_BAD_PARAM;
137       else if (len != 1)
138         status = AVRC_STS_INTERNAL_ERR;
139       break;
140 
141     case AVRC_PDU_LIST_PLAYER_APP_ATTR: /* 0x11 */
142       /* no additional parameters */
143       if (len != 0) status = AVRC_STS_INTERNAL_ERR;
144       break;
145 
146     case AVRC_PDU_LIST_PLAYER_APP_VALUES: /* 0x12 */
147       p_result->list_app_values.attr_id = *p++;
148       if (!AVRC_IS_VALID_ATTRIBUTE(p_result->list_app_values.attr_id))
149         status = AVRC_STS_BAD_PARAM;
150       else if (len != 1)
151         status = AVRC_STS_INTERNAL_ERR;
152       break;
153 
154     case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: /* 0x13 */
155     case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: /* 0x15 */
156       BE_STREAM_TO_UINT8(p_result->get_cur_app_val.num_attr, p);
157       if (len != (p_result->get_cur_app_val.num_attr + 1)) {
158         status = AVRC_STS_INTERNAL_ERR;
159         break;
160       }
161       p_u8 = p_result->get_cur_app_val.attrs;
162       for (xx = 0, yy = 0; xx < p_result->get_cur_app_val.num_attr; xx++) {
163         /* only report the valid player app attributes */
164         if (AVRC_IsValidPlayerAttr(*p)) p_u8[yy++] = *p;
165         p++;
166       }
167       p_result->get_cur_app_val.num_attr = yy;
168       if (yy == 0) {
169         status = AVRC_STS_BAD_PARAM;
170       }
171       break;
172 
173     case AVRC_PDU_SET_PLAYER_APP_VALUE: /* 0x14 */
174       BE_STREAM_TO_UINT8(p_result->set_app_val.num_val, p);
175       size_needed = sizeof(tAVRC_APP_SETTING);
176       if (p_buf && (len == ((p_result->set_app_val.num_val << 1) + 1))) {
177         p_result->set_app_val.p_vals = (tAVRC_APP_SETTING*)p_buf;
178         p_app_set = p_result->set_app_val.p_vals;
179         for (xx = 0;
180              ((xx < p_result->set_app_val.num_val) && (buf_len > size_needed));
181              xx++) {
182           p_app_set[xx].attr_id = *p++;
183           p_app_set[xx].attr_val = *p++;
184           if (!avrc_is_valid_player_attrib_value(p_app_set[xx].attr_id,
185                                                  p_app_set[xx].attr_val))
186             status = AVRC_STS_BAD_PARAM;
187         }
188         if (xx != p_result->set_app_val.num_val) {
189           AVRC_TRACE_ERROR(
190               "%s AVRC_PDU_SET_PLAYER_APP_VALUE not enough room:%d orig "
191               "num_val:%d",
192               __func__, xx, p_result->set_app_val.num_val);
193           p_result->set_app_val.num_val = xx;
194         }
195       } else {
196         AVRC_TRACE_ERROR(
197             "%s AVRC_PDU_SET_PLAYER_APP_VALUE NULL decode buffer or bad len",
198             __func__);
199         status = AVRC_STS_INTERNAL_ERR;
200       }
201       break;
202 
203     case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT: /* 0x16 */
204       if (len < 3)
205         status = AVRC_STS_INTERNAL_ERR;
206       else {
207         BE_STREAM_TO_UINT8(p_result->get_app_val_txt.attr_id, p);
208         if (!AVRC_IS_VALID_ATTRIBUTE(p_result->get_app_val_txt.attr_id))
209           status = AVRC_STS_BAD_PARAM;
210         else {
211           BE_STREAM_TO_UINT8(p_result->get_app_val_txt.num_val, p);
212           if ((len - 2 /* attr_id & num_val */) !=
213               p_result->get_app_val_txt.num_val)
214             status = AVRC_STS_INTERNAL_ERR;
215           else {
216             p_u8 = p_result->get_app_val_txt.vals;
217             for (xx = 0; xx < p_result->get_app_val_txt.num_val; xx++) {
218               p_u8[xx] = *p++;
219               if (!avrc_is_valid_player_attrib_value(
220                       p_result->get_app_val_txt.attr_id, p_u8[xx])) {
221                 status = AVRC_STS_BAD_PARAM;
222                 break;
223               }
224             }
225           }
226         }
227       }
228       break;
229 
230     case AVRC_PDU_INFORM_DISPLAY_CHARSET: /* 0x17 */
231       if (len < 3)
232         status = AVRC_STS_INTERNAL_ERR;
233       else {
234         BE_STREAM_TO_UINT8(p_result->inform_charset.num_id, p);
235         if ((len - 1 /* num_id */) != p_result->inform_charset.num_id * 2)
236           status = AVRC_STS_INTERNAL_ERR;
237         else {
238           p_u16 = p_result->inform_charset.charsets;
239           if (p_result->inform_charset.num_id > AVRC_MAX_CHARSET_SIZE)
240             p_result->inform_charset.num_id = AVRC_MAX_CHARSET_SIZE;
241           for (xx = 0; xx < p_result->inform_charset.num_id; xx++) {
242             BE_STREAM_TO_UINT16(p_u16[xx], p);
243           }
244         }
245       }
246       break;
247 
248     case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT: /* 0x18 */
249       if (len != 1)
250         status = AVRC_STS_INTERNAL_ERR;
251       else {
252         p_result->inform_battery_status.battery_status = *p++;
253         if (!AVRC_IS_VALID_BATTERY_STATUS(
254                 p_result->inform_battery_status.battery_status))
255           status = AVRC_STS_BAD_PARAM;
256       }
257       break;
258 
259     case AVRC_PDU_GET_ELEMENT_ATTR: /* 0x20 */
260       if (len < 9)                  /* UID/8 and num_attr/1 */
261         status = AVRC_STS_INTERNAL_ERR;
262       else {
263         BE_STREAM_TO_UINT32(u32, p);
264         BE_STREAM_TO_UINT32(u32_2, p);
265         if (u32 == 0 && u32_2 == 0) {
266           BE_STREAM_TO_UINT8(p_result->get_elem_attrs.num_attr, p);
267           if ((len - 9 /* UID/8 and num_attr/1 */) !=
268               (p_result->get_elem_attrs.num_attr * 4))
269             status = AVRC_STS_INTERNAL_ERR;
270           else {
271             p_u32 = p_result->get_elem_attrs.attrs;
272             if (p_result->get_elem_attrs.num_attr > AVRC_MAX_ELEM_ATTR_SIZE)
273               p_result->get_elem_attrs.num_attr = AVRC_MAX_ELEM_ATTR_SIZE;
274             for (xx = 0; xx < p_result->get_elem_attrs.num_attr; xx++) {
275               BE_STREAM_TO_UINT32(p_u32[xx], p);
276             }
277           }
278         } else
279           status = AVRC_STS_NOT_FOUND;
280       }
281       break;
282 
283     case AVRC_PDU_GET_PLAY_STATUS: /* 0x30 */
284       /* no additional parameters */
285       if (len != 0) status = AVRC_STS_INTERNAL_ERR;
286       break;
287 
288     case AVRC_PDU_REGISTER_NOTIFICATION: /* 0x31 */
289       if (len != 5)
290         status = AVRC_STS_INTERNAL_ERR;
291       else {
292         BE_STREAM_TO_UINT8(p_result->reg_notif.event_id, p);
293         BE_STREAM_TO_UINT32(p_result->reg_notif.param, p);
294       }
295       break;
296 
297     case AVRC_PDU_SET_ABSOLUTE_VOLUME: /* 0x50 */
298       if (len != 1)
299         status = AVRC_STS_INTERNAL_ERR;
300       else
301         p_result->volume.volume = *p++;
302       break;
303 
304     case AVRC_PDU_REQUEST_CONTINUATION_RSP: /* 0x40 */
305       if (len != 1) {
306         status = AVRC_STS_INTERNAL_ERR;
307       }
308       BE_STREAM_TO_UINT8(p_result->continu.target_pdu, p);
309       break;
310 
311     case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */
312       if (len != 1) {
313         status = AVRC_STS_INTERNAL_ERR;
314       }
315       BE_STREAM_TO_UINT8(p_result->abort.target_pdu, p);
316       break;
317 
318     case AVRC_PDU_SET_ADDRESSED_PLAYER: /* 0x60 */
319       if (len != 2) {
320         AVRC_TRACE_ERROR("AVRC_PDU_SET_ADDRESSED_PLAYER length is incorrect:%d",
321                          len);
322         status = AVRC_STS_INTERNAL_ERR;
323       }
324       BE_STREAM_TO_UINT16(p_result->addr_player.player_id, p);
325       break;
326 
327     case AVRC_PDU_PLAY_ITEM:          /* 0x74 */
328     case AVRC_PDU_ADD_TO_NOW_PLAYING: /* 0x90 */
329       if (len != (AVRC_UID_SIZE + 3)) status = AVRC_STS_INTERNAL_ERR;
330       BE_STREAM_TO_UINT8(p_result->play_item.scope, p);
331       if (p_result->play_item.scope > AVRC_SCOPE_NOW_PLAYING) {
332         status = AVRC_STS_BAD_SCOPE;
333       }
334       BE_STREAM_TO_ARRAY(p, p_result->play_item.uid, AVRC_UID_SIZE);
335       BE_STREAM_TO_UINT16(p_result->play_item.uid_counter, p);
336       break;
337 
338     default:
339       status = AVRC_STS_BAD_CMD;
340       break;
341   }
342 
343   return status;
344 }
345 
346 /*******************************************************************************
347  *
348  * Function         AVRC_Ctrl_ParsCommand
349  *
350  * Description      This function is used to parse cmds received for CTRL
351  *                  Currently it is for SetAbsVolume and Volume Change
352  *                  Notification..
353  *
354  * Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed
355  *                  successfully.
356  *                  Otherwise, the error code defined by AVRCP 1.4
357  *
358  ******************************************************************************/
AVRC_Ctrl_ParsCommand(tAVRC_MSG * p_msg,tAVRC_COMMAND * p_result)359 tAVRC_STS AVRC_Ctrl_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result) {
360   tAVRC_STS status = AVRC_STS_INTERNAL_ERR;
361 
362   if (p_msg && p_result) {
363     switch (p_msg->hdr.opcode) {
364       case AVRC_OP_VENDOR: /*  0x00    Vendor-dependent commands */
365         status = avrc_ctrl_pars_vendor_cmd(&p_msg->vendor, p_result);
366         break;
367 
368       default:
369         AVRC_TRACE_ERROR("%s unknown opcode:0x%x", __func__, p_msg->hdr.opcode);
370         break;
371     }
372     p_result->cmd.opcode = p_msg->hdr.opcode;
373     p_result->cmd.status = status;
374   }
375   AVRC_TRACE_DEBUG("%s return status:0x%x", __func__, status);
376   return status;
377 }
378 
379 /*******************************************************************************
380  *
381  * Function         avrc_pars_browsing_cmd
382  *
383  * Description      This function parses the commands that go through the
384  *                  browsing channel
385  *
386  * Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed
387  *                  successfully.
388  *                  Otherwise, the error code defined by AVRCP+1
389  *
390  ******************************************************************************/
avrc_pars_browsing_cmd(tAVRC_MSG_BROWSE * p_msg,tAVRC_COMMAND * p_result,uint8_t * p_buf,uint16_t buf_len)391 static tAVRC_STS avrc_pars_browsing_cmd(tAVRC_MSG_BROWSE* p_msg,
392                                         tAVRC_COMMAND* p_result, uint8_t* p_buf,
393                                         uint16_t buf_len) {
394   tAVRC_STS status = AVRC_STS_NO_ERROR;
395   uint8_t* p = p_msg->p_browse_data;
396   int count;
397 
398   p_result->pdu = *p++;
399   AVRC_TRACE_DEBUG("avrc_pars_browsing_cmd() pdu:0x%x", p_result->pdu);
400   /* skip over len */
401   p += 2;
402 
403   switch (p_result->pdu) {
404     case AVRC_PDU_SET_BROWSED_PLAYER: /* 0x70 */
405       // For current implementation all players are browsable.
406       BE_STREAM_TO_UINT16(p_result->br_player.player_id, p);
407       break;
408 
409     case AVRC_PDU_GET_FOLDER_ITEMS: /* 0x71 */
410       STREAM_TO_UINT8(p_result->get_items.scope, p);
411       // To be modified later here (Scope) when all browsing commands are
412       // supported
413       if (p_result->get_items.scope > AVRC_SCOPE_NOW_PLAYING) {
414         status = AVRC_STS_BAD_SCOPE;
415       }
416       BE_STREAM_TO_UINT32(p_result->get_items.start_item, p);
417       BE_STREAM_TO_UINT32(p_result->get_items.end_item, p);
418       if (p_result->get_items.start_item > p_result->get_items.end_item) {
419         status = AVRC_STS_BAD_RANGE;
420       }
421       STREAM_TO_UINT8(p_result->get_items.attr_count, p);
422       p_result->get_items.p_attr_list = NULL;
423       if (p_result->get_items.attr_count && p_buf &&
424           (p_result->get_items.attr_count != AVRC_FOLDER_ITEM_COUNT_NONE)) {
425         p_result->get_items.p_attr_list = (uint32_t*)p_buf;
426         count = p_result->get_items.attr_count;
427         if (buf_len < (count << 2))
428           p_result->get_items.attr_count = count = (buf_len >> 2);
429         for (int idx = 0; idx < count; idx++) {
430           BE_STREAM_TO_UINT32(p_result->get_items.p_attr_list[idx], p);
431         }
432       }
433       break;
434 
435     case AVRC_PDU_CHANGE_PATH: /* 0x72 */
436       BE_STREAM_TO_UINT16(p_result->chg_path.uid_counter, p);
437       BE_STREAM_TO_UINT8(p_result->chg_path.direction, p);
438       if (p_result->chg_path.direction != AVRC_DIR_UP &&
439           p_result->chg_path.direction != AVRC_DIR_DOWN) {
440         status = AVRC_STS_BAD_DIR;
441       }
442       BE_STREAM_TO_ARRAY(p, p_result->chg_path.folder_uid, AVRC_UID_SIZE);
443       break;
444 
445     case AVRC_PDU_GET_ITEM_ATTRIBUTES: /* 0x73 */
446       BE_STREAM_TO_UINT8(p_result->get_attrs.scope, p);
447       if (p_result->get_attrs.scope > AVRC_SCOPE_NOW_PLAYING) {
448         status = AVRC_STS_BAD_SCOPE;
449         break;
450       }
451       BE_STREAM_TO_ARRAY(p, p_result->get_attrs.uid, AVRC_UID_SIZE);
452       BE_STREAM_TO_UINT16(p_result->get_attrs.uid_counter, p);
453       BE_STREAM_TO_UINT8(p_result->get_attrs.attr_count, p);
454       p_result->get_attrs.p_attr_list = NULL;
455       if (p_result->get_attrs.attr_count && p_buf) {
456         p_result->get_attrs.p_attr_list = (uint32_t*)p_buf;
457         count = p_result->get_attrs.attr_count;
458         if (buf_len < (count << 2))
459           p_result->get_attrs.attr_count = count = (buf_len >> 2);
460         for (int idx = 0, count = 0; idx < p_result->get_attrs.attr_count;
461              idx++) {
462           BE_STREAM_TO_UINT32(p_result->get_attrs.p_attr_list[count], p);
463           if (AVRC_IS_VALID_MEDIA_ATTRIBUTE(
464                   p_result->get_attrs.p_attr_list[count])) {
465             count++;
466           }
467         }
468 
469         if (p_result->get_attrs.attr_count != count && count == 0)
470           status = AVRC_STS_BAD_PARAM;
471         else
472           p_result->get_attrs.attr_count = count;
473       }
474       break;
475 
476     case AVRC_PDU_GET_TOTAL_NUM_OF_ITEMS: /* 0x75 */
477       BE_STREAM_TO_UINT8(p_result->get_num_of_items.scope, p);
478       if (p_result->get_num_of_items.scope > AVRC_SCOPE_NOW_PLAYING) {
479         status = AVRC_STS_BAD_SCOPE;
480       }
481       break;
482 
483     case AVRC_PDU_SEARCH: /* 0x80 */
484       BE_STREAM_TO_UINT16(p_result->search.string.charset_id, p);
485       BE_STREAM_TO_UINT16(p_result->search.string.str_len, p);
486       p_result->search.string.p_str = p_buf;
487       if (p_buf) {
488         if (buf_len > p_result->search.string.str_len)
489           buf_len = p_result->search.string.str_len;
490         BE_STREAM_TO_ARRAY(p, p_buf, p_result->search.string.str_len);
491       } else {
492         status = AVRC_STS_INTERNAL_ERR;
493       }
494       break;
495 
496     default:
497       status = AVRC_STS_BAD_CMD;
498       break;
499   }
500   return status;
501 }
502 
503 /*******************************************************************************
504  *
505  * Function         AVRC_ParsCommand
506  *
507  * Description      This function is a superset of AVRC_ParsMetadata to parse
508  *                  the command.
509  *
510  * Returns          AVRC_STS_NO_ERROR, if the message in p_data is parsed
511  *                  successfully.
512  *                  Otherwise, the error code defined by AVRCP 1.4
513  *
514  ******************************************************************************/
AVRC_ParsCommand(tAVRC_MSG * p_msg,tAVRC_COMMAND * p_result,uint8_t * p_buf,uint16_t buf_len)515 tAVRC_STS AVRC_ParsCommand(tAVRC_MSG* p_msg, tAVRC_COMMAND* p_result,
516                            uint8_t* p_buf, uint16_t buf_len) {
517   tAVRC_STS status = AVRC_STS_INTERNAL_ERR;
518   uint16_t id;
519 
520   if (p_msg && p_result) {
521     switch (p_msg->hdr.opcode) {
522       case AVRC_OP_VENDOR: /*  0x00    Vendor-dependent commands */
523         status = avrc_pars_vendor_cmd(&p_msg->vendor, p_result, p_buf, buf_len);
524         break;
525 
526       case AVRC_OP_PASS_THRU: /*  0x7C    panel subunit opcode */
527         status = avrc_pars_pass_thru(&p_msg->pass, &id);
528         if (status == AVRC_STS_NO_ERROR) {
529           p_result->pdu = (uint8_t)id;
530         }
531         break;
532 
533       case AVRC_OP_BROWSE:
534         status =
535             avrc_pars_browsing_cmd(&p_msg->browse, p_result, p_buf, buf_len);
536         break;
537 
538       default:
539         AVRC_TRACE_ERROR("%s unknown opcode:0x%x", __func__, p_msg->hdr.opcode);
540         break;
541     }
542     p_result->cmd.opcode = p_msg->hdr.opcode;
543     p_result->cmd.status = status;
544   }
545   AVRC_TRACE_DEBUG("%s return status:0x%x", __func__, status);
546   return status;
547 }
548 
549 #endif /* (AVRC_METADATA_INCLUDED == true) */
550