Home | History | Annotate | Download | only in avrc
      1 /******************************************************************************
      2  *
      3  *  Copyright (C) 2003-2012 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 
     19 /******************************************************************************
     20  *
     21  *  nterface to AVRCP mandatory commands
     22  *
     23  ******************************************************************************/
     24 #include <string.h>
     25 
     26 #include "gki.h"
     27 #include "avrc_api.h"
     28 #include "avrc_int.h"
     29 #include "wcassert.h"
     30 
     31 /*****************************************************************************
     32 **  Global data
     33 *****************************************************************************/
     34 
     35 
     36 #define AVRC_MAX_RCV_CTRL_EVT   AVCT_BROWSE_UNCONG_IND_EVT
     37 
     38 static const UINT8 avrc_ctrl_event_map[] =
     39 {
     40     AVRC_OPEN_IND_EVT,  /* AVCT_CONNECT_CFM_EVT */
     41     AVRC_OPEN_IND_EVT,  /* AVCT_CONNECT_IND_EVT */
     42     AVRC_CLOSE_IND_EVT, /* AVCT_DISCONNECT_CFM_EVT */
     43     AVRC_CLOSE_IND_EVT, /* AVCT_DISCONNECT_IND_EVT */
     44     AVRC_CONG_IND_EVT,  /* AVCT_CONG_IND_EVT */
     45     AVRC_UNCONG_IND_EVT,/* AVCT_UNCONG_IND_EVT */
     46     AVRC_BROWSE_OPEN_IND_EVT,  /* AVCT_BROWSE_CONN_CFM_EVT   */
     47     AVRC_BROWSE_OPEN_IND_EVT,  /* AVCT_BROWSE_CONN_IND_EVT   */
     48     AVRC_BROWSE_CLOSE_IND_EVT, /* AVCT_BROWSE_DISCONN_CFM_EVT */
     49     AVRC_BROWSE_CLOSE_IND_EVT, /* AVCT_BROWSE_DISCONN_IND_EVT */
     50     AVRC_BROWSE_CONG_IND_EVT,  /* AVCT_BROWSE_CONG_IND_EVT    */
     51     AVRC_BROWSE_UNCONG_IND_EVT /* AVCT_BROWSE_UNCONG_IND_EVT  */
     52 };
     53 
     54 #define AVRC_OP_DROP        0xFE    /* use this unused opcode to indication no need to call the callback function */
     55 #define AVRC_OP_DROP_N_FREE 0xFD    /* use this unused opcode to indication no need to call the callback function & free buffer */
     56 
     57 /******************************************************************************
     58 **
     59 ** Function         avrc_ctrl_cback
     60 **
     61 ** Description      This is the callback function used by AVCTP to report
     62 **                  received link events.
     63 **
     64 ** Returns          Nothing.
     65 **
     66 ******************************************************************************/
     67 static void avrc_ctrl_cback(UINT8 handle, UINT8 event, UINT16 result,
     68                                 BD_ADDR peer_addr)
     69 {
     70     UINT8   avrc_event;
     71 
     72     if (event <= AVRC_MAX_RCV_CTRL_EVT && avrc_cb.ccb[handle].p_ctrl_cback)
     73     {
     74         avrc_event = avrc_ctrl_event_map[event];
     75         if (event == AVCT_CONNECT_CFM_EVT)
     76         {
     77             if (result != 0) /* failed */
     78                 avrc_event = AVRC_CLOSE_IND_EVT;
     79         }
     80         (*avrc_cb.ccb[handle].p_ctrl_cback)(handle, avrc_event, result, peer_addr);
     81     }
     82     /* else drop the unknown event*/
     83 }
     84 
     85 /******************************************************************************
     86 **
     87 ** Function         avrc_get_data_ptr
     88 **
     89 ** Description      If the offset in the received buffer is smaller than required
     90 **                  move the portion of data AVRC cares.
     91 **
     92 ** Returns          Nothing.
     93 **
     94 ******************************************************************************/
     95 static UINT8 * avrc_get_data_ptr(BT_HDR *p_pkt)
     96 {
     97     UINT8   *p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset;
     98     int     i, gap;
     99 
    100     if (p_pkt->offset < AVCT_MSG_OFFSET)
    101     {
    102         gap = AVCT_MSG_OFFSET - p_pkt->offset;
    103         for(i=p_pkt->len; i>0; i--)
    104         {
    105             *(p_data + i + gap) = *(p_data + i);
    106         }
    107         p_pkt->offset   += gap;
    108         p_data          = (UINT8 *)(p_pkt+1) + p_pkt->offset;
    109     }
    110     *p_data         = AVRC_RSP_IMPL_STBL;
    111     return p_data;
    112 }
    113 
    114 
    115 /******************************************************************************
    116 **
    117 ** Function         avrc_msg_cback
    118 **
    119 ** Description      This is the callback function used by AVCTP to report
    120 **                  received AV control messages.
    121 **
    122 ** Returns          Nothing.
    123 **
    124 ******************************************************************************/
    125 static void avrc_msg_cback(UINT8 handle, UINT8 label, UINT8 cr,
    126                                BT_HDR *p_pkt)
    127 {
    128     UINT8       opcode;
    129     tAVRC_MSG   msg;
    130     UINT8       *p_data;
    131     UINT8       *p_begin;
    132     BOOLEAN     drop = FALSE;
    133     BOOLEAN     free = TRUE;
    134     BT_HDR      *p_rsp = NULL;
    135     UINT8       *p_rsp_data;
    136     int         xx;
    137     BOOLEAN     reject = FALSE;
    138 #if (BT_USE_TRACES == TRUE)
    139     char        *p_drop_msg = "dropped";
    140 #endif
    141     tAVRC_MSG_VENDOR *p_msg = &msg.vendor;
    142 
    143     if (cr == AVCT_CMD &&
    144         (p_pkt->layer_specific & AVCT_DATA_CTRL && AVRC_PACKET_LEN < sizeof(p_pkt->len)))
    145     {
    146         /* Ignore the invalid AV/C command frame */
    147 #if (BT_USE_TRACES == TRUE)
    148         p_drop_msg = "dropped - too long AV/C cmd frame size";
    149 #endif
    150         GKI_freebuf(p_pkt);
    151         return;
    152     }
    153 
    154     if (cr == AVCT_REJ)
    155     {
    156         /* The peer thinks that this PID is no longer open - remove this handle */
    157         /*  */
    158         GKI_freebuf(p_pkt);
    159         AVCT_RemoveConn(handle);
    160         return;
    161     }
    162 
    163     p_data  = (UINT8 *)(p_pkt+1) + p_pkt->offset;
    164     memset(&msg, 0, sizeof(tAVRC_MSG) );
    165     {
    166         msg.hdr.ctype           = p_data[0] & AVRC_CTYPE_MASK;
    167         AVRC_TRACE_DEBUG4("avrc_msg_cback handle:%d, ctype:%d, offset:%d, len: %d",
    168                 handle, msg.hdr.ctype, p_pkt->offset, p_pkt->len);
    169         msg.hdr.subunit_type    = (p_data[1] & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT;
    170         msg.hdr.subunit_id      = p_data[1] & AVRC_SUBID_MASK;
    171         opcode                  = p_data[2];
    172     }
    173 
    174     if ( ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) ||
    175         ((avrc_cb.ccb[handle].control & AVRC_CT_CONTROL) && (cr == AVCT_RSP)) )
    176     {
    177 
    178         switch(opcode)
    179         {
    180         case AVRC_OP_UNIT_INFO:
    181             if (cr == AVCT_CMD)
    182             {
    183                 /* send the response to the peer */
    184                 p_rsp           = p_pkt; /* this also sets free = FALSE, drop = TRUE */
    185                 /* check & set the offset. set response code, set subunit_type & subunit_id,
    186                    set AVRC_OP_UNIT_INFO */
    187                 p_rsp_data      = avrc_get_data_ptr(p_pkt) + AVRC_AVC_HDR_SIZE; /* 3 bytes: ctype, subunit*, opcode */
    188                 *p_rsp_data++   = 7;
    189                 /* Panel subunit & id=0 */
    190                 *p_rsp_data++   = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT);
    191                 AVRC_CO_ID_TO_BE_STREAM(p_rsp_data, avrc_cb.ccb[handle].company_id);
    192                 p_rsp->len      = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset);
    193                 cr = AVCT_RSP;
    194 #if (BT_USE_TRACES == TRUE)
    195                 p_drop_msg = "auto respond";
    196 #endif
    197             }
    198             else
    199             {
    200                 /* parse response */
    201                 p_data += 4; /* 3 bytes: ctype, subunit*, opcode + octet 3 (is 7)*/
    202                 msg.unit.unit_type  = (*p_data & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT;
    203                 msg.unit.unit       = *p_data & AVRC_SUBID_MASK;
    204                 p_data++;
    205                 AVRC_BE_STREAM_TO_CO_ID(msg.unit.company_id, p_data);
    206             }
    207             break;
    208 
    209         case AVRC_OP_SUB_INFO:
    210             if (cr == AVCT_CMD)
    211             {
    212                 /* send the response to the peer */
    213                 p_rsp           = p_pkt; /* this also sets free = FALSE, drop = TRUE */
    214                 /* check & set the offset. set response code, set (subunit_type & subunit_id),
    215                    set AVRC_OP_SUB_INFO, set (page & extention code) */
    216                 p_rsp_data      = avrc_get_data_ptr(p_pkt) + 4;
    217                 /* Panel subunit & id=0 */
    218                 *p_rsp_data++   = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT);
    219                 memset(p_rsp_data, AVRC_CMD_OPRND_PAD, AVRC_SUBRSP_OPRND_BYTES);
    220                 p_rsp_data      += AVRC_SUBRSP_OPRND_BYTES;
    221                 p_rsp->len      = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset);
    222                 cr = AVCT_RSP;
    223 #if (BT_USE_TRACES == TRUE)
    224                 p_drop_msg = "auto responded";
    225 #endif
    226             }
    227             else
    228             {
    229                 /* parse response */
    230                 p_data += AVRC_AVC_HDR_SIZE; /* 3 bytes: ctype, subunit*, opcode */
    231                 msg.sub.page    = (*p_data++ >> AVRC_SUB_PAGE_SHIFT) & AVRC_SUB_PAGE_MASK;
    232                 xx      = 0;
    233                 while (*p_data != AVRC_CMD_OPRND_PAD && xx<AVRC_SUB_TYPE_LEN)
    234                 {
    235                     msg.sub.subunit_type[xx] = *p_data++ >> AVRC_SUBTYPE_SHIFT;
    236                     if (msg.sub.subunit_type[xx] == AVRC_SUB_PANEL)
    237                         msg.sub.panel   = TRUE;
    238                     xx++;
    239                 }
    240             }
    241             break;
    242 
    243         case AVRC_OP_VENDOR:
    244             p_data  = (UINT8 *)(p_pkt+1) + p_pkt->offset;
    245             p_begin = p_data;
    246             if (p_pkt->len < AVRC_VENDOR_HDR_SIZE) /* 6 = ctype, subunit*, opcode & CO_ID */
    247             {
    248                 if (cr == AVCT_CMD)
    249                     reject = TRUE;
    250                 else
    251                     drop = TRUE;
    252                 break;
    253             }
    254             p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */
    255             AVRC_BE_STREAM_TO_CO_ID(p_msg->company_id, p_data);
    256             p_msg->p_vendor_data   = p_data;
    257             p_msg->vendor_len      = p_pkt->len - (p_data - p_begin);
    258 
    259             break;
    260 
    261         case AVRC_OP_PASS_THRU:
    262             if (p_pkt->len < 5) /* 3 bytes: ctype, subunit*, opcode & op_id & len */
    263             {
    264                 if (cr == AVCT_CMD)
    265                     reject = TRUE;
    266                 else
    267                     drop = TRUE;
    268                 break;
    269             }
    270             p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */
    271             msg.pass.op_id  = (AVRC_PASS_OP_ID_MASK & *p_data);
    272             if (AVRC_PASS_STATE_MASK & *p_data)
    273                 msg.pass.state  = TRUE;
    274             else
    275                 msg.pass.state  = FALSE;
    276             p_data++;
    277             msg.pass.pass_len    = *p_data++;
    278             if (msg.pass.pass_len != p_pkt->len - 5)
    279                 msg.pass.pass_len = p_pkt->len - 5;
    280             if (msg.pass.pass_len)
    281                 msg.pass.p_pass_data = p_data;
    282             else
    283                 msg.pass.p_pass_data = NULL;
    284             break;
    285 
    286 
    287         default:
    288             if ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD))
    289             {
    290                 /* reject unsupported opcode */
    291                 reject = TRUE;
    292             }
    293             drop    = TRUE;
    294             break;
    295         }
    296     }
    297     else /* drop the event */
    298     {
    299             drop    = TRUE;
    300     }
    301 
    302     if (reject)
    303     {
    304         /* reject unsupported opcode */
    305         p_rsp           = p_pkt; /* this also sets free = FALSE, drop = TRUE */
    306         p_rsp_data      = avrc_get_data_ptr(p_pkt);
    307         *p_rsp_data     = AVRC_RSP_REJ;
    308 #if (BT_USE_TRACES == TRUE)
    309         p_drop_msg = "rejected";
    310 #endif
    311         cr      = AVCT_RSP;
    312         drop    = TRUE;
    313     }
    314 
    315     if (p_rsp)
    316     {
    317         /* set to send response right away */
    318         AVCT_MsgReq( handle, label, cr, p_rsp);
    319         free = FALSE;
    320         drop = TRUE;
    321     }
    322 
    323     if (drop == FALSE)
    324     {
    325         msg.hdr.opcode = opcode;
    326         (*avrc_cb.ccb[handle].p_msg_cback)(handle, label, opcode, &msg);
    327     }
    328 #if (BT_USE_TRACES == TRUE)
    329     else
    330     {
    331         AVRC_TRACE_WARNING5("avrc_msg_cback %s msg handle:%d, control:%d, cr:%d, opcode:x%x",
    332                 p_drop_msg,
    333                 handle, avrc_cb.ccb[handle].control, cr, opcode);
    334     }
    335 #endif
    336 
    337 
    338     if (free)
    339         GKI_freebuf(p_pkt);
    340 }
    341 
    342 
    343 
    344 
    345 /******************************************************************************
    346 **
    347 ** Function         avrc_pass_msg
    348 **
    349 ** Description      Compose a PASS THROUGH command according to p_msg
    350 **
    351 **                  Input Parameters:
    352 **                      p_msg: Pointer to PASS THROUGH message structure.
    353 **
    354 **                  Output Parameters:
    355 **                      None.
    356 **
    357 ** Returns          pointer to a valid GKI buffer if successful.
    358 **                  NULL if p_msg is NULL.
    359 **
    360 ******************************************************************************/
    361 static BT_HDR  * avrc_pass_msg(tAVRC_MSG_PASS *p_msg)
    362 {
    363     BT_HDR  *p_cmd = NULL;
    364     UINT8   *p_data;
    365 
    366     WC_ASSERT(p_msg != NULL);
    367     WC_ASSERT(AVRC_CMD_POOL_SIZE > (AVRC_MIN_CMD_LEN+p_msg->pass_len));
    368 
    369     if ((p_cmd = (BT_HDR *) GKI_getpoolbuf(AVRC_CMD_POOL_ID)) != NULL)
    370     {
    371         p_cmd->offset   = AVCT_MSG_OFFSET;
    372         p_cmd->layer_specific   = AVCT_DATA_CTRL;
    373         p_data          = (UINT8 *)(p_cmd + 1) + p_cmd->offset;
    374         *p_data++       = (p_msg->hdr.ctype & AVRC_CTYPE_MASK);
    375         *p_data++       = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); /* Panel subunit & id=0 */
    376         *p_data++       = AVRC_OP_PASS_THRU;
    377         *p_data         = (AVRC_PASS_OP_ID_MASK&p_msg->op_id);
    378         if (p_msg->state)
    379             *p_data     |= AVRC_PASS_STATE_MASK;
    380         p_data++;
    381 
    382         if (p_msg->op_id == AVRC_ID_VENDOR)
    383         {
    384             *p_data++       = p_msg->pass_len;
    385             if (p_msg->pass_len && p_msg->p_pass_data)
    386             {
    387                 memcpy(p_data, p_msg->p_pass_data, p_msg->pass_len);
    388                 p_data += p_msg->pass_len;
    389             }
    390         }
    391         else /* set msg len to 0 for other op_id */
    392         {
    393             /* set msg len to 0 for other op_id */
    394             *p_data++       = 0;
    395         }
    396         p_cmd->len      = (UINT16) (p_data - (UINT8 *)(p_cmd + 1) - p_cmd->offset);
    397     }
    398     return p_cmd;
    399 }
    400 
    401 /******************************************************************************
    402 **
    403 ** Function         AVRC_Open
    404 **
    405 ** Description      This function is called to open a connection to AVCTP.
    406 **                  The connection can be either an initiator or acceptor, as
    407 **                  determined by the p_ccb->stream parameter.
    408 **                  The connection can be a target, a controller or for both role,
    409 **                  as determined by the p_ccb->control parameter.
    410 **                  By definition, a target connection is an acceptor connection
    411 **                  that waits for an incoming AVCTP connection from the peer.
    412 **                  The connection remains available to the application until
    413 **                  the application closes it by calling AVRC_Close().  The
    414 **                  application does not need to reopen the connection after an
    415 **                  AVRC_CLOSE_IND_EVT is received.
    416 **
    417 **                  Input Parameters:
    418 **                      p_ccb->company_id: Company Identifier.
    419 **
    420 **                      p_ccb->p_ctrl_cback:  Pointer to control callback function.
    421 **
    422 **                      p_ccb->p_msg_cback:  Pointer to message callback function.
    423 **
    424 **                      p_ccb->conn: AVCTP connection role.  This is set to
    425 **                      AVCTP_INT for initiator connections and AVCTP_ACP
    426 **                      for acceptor connections.
    427 **
    428 **                      p_ccb->control: Control role.  This is set to
    429 **                      AVRC_CT_TARGET for target connections, AVRC_CT_CONTROL
    430 **                      for control connections or (AVRC_CT_TARGET|AVRC_CT_CONTROL)
    431 **                      for connections that support both roles.
    432 **
    433 **                      peer_addr: BD address of peer device.  This value is
    434 **                      only used for initiator connections; for acceptor
    435 **                      connections it can be set to NULL.
    436 **
    437 **                  Output Parameters:
    438 **                      p_handle: Pointer to handle.  This parameter is only
    439 **                                valid if AVRC_SUCCESS is returned.
    440 **
    441 ** Returns          AVRC_SUCCESS if successful.
    442 **                  AVRC_NO_RESOURCES if there are not enough resources to open
    443 **                  the connection.
    444 **
    445 ******************************************************************************/
    446 UINT16 AVRC_Open(UINT8 *p_handle, tAVRC_CONN_CB *p_ccb, BD_ADDR_PTR peer_addr)
    447 {
    448     UINT16      status;
    449     tAVCT_CC    cc;
    450 
    451     cc.p_ctrl_cback = avrc_ctrl_cback;      /* Control callback */
    452     cc.p_msg_cback  = avrc_msg_cback;       /* Message callback */
    453     cc.pid          = UUID_SERVCLASS_AV_REMOTE_CONTROL;  /* Profile ID */
    454     cc.role         = p_ccb->conn;          /* Initiator/acceptor role */
    455     cc.control      = p_ccb->control;       /* Control role (Control/Target) */
    456 
    457     status = AVCT_CreateConn(p_handle, &cc, peer_addr);
    458     if (status == AVCT_SUCCESS)
    459     {
    460         memcpy(&avrc_cb.ccb[*p_handle], p_ccb, sizeof(tAVRC_CONN_CB));
    461     }
    462     AVRC_TRACE_DEBUG4("AVRC_Open role: %d, control:%d status:%d, handle:%d", cc.role, cc.control, status, *p_handle);
    463 
    464     return status;
    465 }
    466 
    467 /******************************************************************************
    468 **
    469 ** Function         AVRC_Close
    470 **
    471 ** Description      Close a connection opened with AVRC_Open().
    472 **                  This function is called when the
    473 **                  application is no longer using a connection.
    474 **
    475 **                  Input Parameters:
    476 **                      handle: Handle of this connection.
    477 **
    478 **                  Output Parameters:
    479 **                      None.
    480 **
    481 ** Returns          AVRC_SUCCESS if successful.
    482 **                  AVRC_BAD_HANDLE if handle is invalid.
    483 **
    484 ******************************************************************************/
    485 UINT16 AVRC_Close(UINT8 handle)
    486 {
    487     AVRC_TRACE_DEBUG1("AVRC_Close handle:%d", handle);
    488     return AVCT_RemoveConn(handle);
    489 }
    490 
    491 
    492 /******************************************************************************
    493 **
    494 ** Function         AVRC_MsgReq
    495 **
    496 ** Description      This function is used to send the AVRCP byte stream in p_pkt
    497 **                  down to AVCTP.
    498 **
    499 **                  It is expected that p_pkt->offset is at least AVCT_MSG_OFFSET
    500 **                  p_pkt->layer_specific is AVCT_DATA_CTRL or AVCT_DATA_BROWSE
    501 **                  p_pkt->event is AVRC_OP_VENDOR, AVRC_OP_PASS_THRU or AVRC_OP_BROWSE
    502 **                  The above BT_HDR settings are set by the AVRC_Bld* functions.
    503 **
    504 ** Returns          AVRC_SUCCESS if successful.
    505 **                  AVRC_BAD_HANDLE if handle is invalid.
    506 **
    507 ******************************************************************************/
    508 UINT16 AVRC_MsgReq (UINT8 handle, UINT8 label, UINT8 ctype, BT_HDR *p_pkt)
    509 {
    510     return AVRC_NO_RESOURCES;
    511 }
    512 
    513 
    514 /******************************************************************************
    515 **
    516 ** Function         AVRC_PassCmd
    517 **
    518 ** Description      Send a PASS THROUGH command to the peer device.  This
    519 **                  function can only be called for controller role connections.
    520 **                  Any response message from the peer is passed back through
    521 **                  the tAVRC_MSG_CBACK callback function.
    522 **
    523 **                  Input Parameters:
    524 **                      handle: Handle of this connection.
    525 **
    526 **                      label: Transaction label.
    527 **
    528 **                      p_msg: Pointer to PASS THROUGH message structure.
    529 **
    530 **                  Output Parameters:
    531 **                      None.
    532 **
    533 ** Returns          AVRC_SUCCESS if successful.
    534 **                  AVRC_BAD_HANDLE if handle is invalid.
    535 **
    536 ******************************************************************************/
    537 UINT16 AVRC_PassCmd(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg)
    538 {
    539     BT_HDR *p_buf;
    540     WC_ASSERT(p_msg != NULL);
    541     if (p_msg)
    542     {
    543         p_msg->hdr.ctype    = AVRC_CMD_CTRL;
    544         p_buf = avrc_pass_msg(p_msg);
    545         if (p_buf)
    546             return AVCT_MsgReq( handle, label, AVCT_CMD, p_buf);
    547     }
    548     return AVRC_NO_RESOURCES;
    549 }
    550 
    551 /******************************************************************************
    552 **
    553 ** Function         AVRC_PassRsp
    554 **
    555 ** Description      Send a PASS THROUGH response to the peer device.  This
    556 **                  function can only be called for target role connections.
    557 **                  This function must be called when a PASS THROUGH command
    558 **                  message is received from the peer through the
    559 **                  tAVRC_MSG_CBACK callback function.
    560 **
    561 **                  Input Parameters:
    562 **                      handle: Handle of this connection.
    563 **
    564 **                      label: Transaction label.  Must be the same value as
    565 **                      passed with the command message in the callback function.
    566 **
    567 **                      p_msg: Pointer to PASS THROUGH message structure.
    568 **
    569 **                  Output Parameters:
    570 **                      None.
    571 **
    572 ** Returns          AVRC_SUCCESS if successful.
    573 **                  AVRC_BAD_HANDLE if handle is invalid.
    574 **
    575 ******************************************************************************/
    576 UINT16 AVRC_PassRsp(UINT8 handle, UINT8 label, tAVRC_MSG_PASS *p_msg)
    577 {
    578     BT_HDR *p_buf;
    579     WC_ASSERT(p_msg != NULL);
    580     if (p_msg)
    581     {
    582         p_buf = avrc_pass_msg(p_msg);
    583         if (p_buf)
    584             return AVCT_MsgReq( handle, label, AVCT_RSP, p_buf);
    585     }
    586     return AVRC_NO_RESOURCES;
    587 }
    588 
    589