OSDN Git Service

Add support for AbsoluteVolume
authorSatya Calloji <satyac@broadcom.com>
Thu, 1 Aug 2013 09:14:43 +0000 (02:14 -0700)
committerJohn Du <johnldu@google.com>
Fri, 16 Aug 2013 19:38:58 +0000 (12:38 -0700)
Add support for AbsoluteVolume in BTIF
bug 9595622

Change-Id: I11b707f6ab50d31bd11e879b0ebe5ad5bac54ad9

Conflicts:
btif/src/btif_rc.c

btif/src/btif_av.c
btif/src/btif_rc.c
btif/src/btif_util.c
include/bt_target.h
stack/Android.mk
stack/avrc/avrc_api.c
stack/avrc/avrc_bld_ct.c [new file with mode: 0644]
stack/avrc/avrc_pars_ct.c [new file with mode: 0755]
stack/avrc/avrc_pars_tg.c
stack/avrc/avrc_sdp.c

index 6bb6bfb..934c937 100755 (executable)
@@ -960,8 +960,12 @@ bt_status_t btif_av_execute_service(BOOLEAN b_enable)
           * be initiated by the app/audioflinger layers */
 #if (AVRC_METADATA_INCLUDED == TRUE)
          BTA_AvEnable(BTA_SEC_AUTHENTICATE,
-             BTA_AV_FEAT_RCTG|BTA_AV_FEAT_METADATA|BTA_AV_FEAT_VENDOR|BTA_AV_FEAT_NO_SCO_SSPD,
-             bte_av_callback);
+             BTA_AV_FEAT_RCTG|BTA_AV_FEAT_METADATA|BTA_AV_FEAT_VENDOR|BTA_AV_FEAT_NO_SCO_SSPD
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+             |BTA_AV_FEAT_RCCT
+             |BTA_AV_FEAT_ADV_CTRL
+#endif
+             ,bte_av_callback);
 #else
          BTA_AvEnable(BTA_SEC_AUTHENTICATE, (BTA_AV_FEAT_RCTG | BTA_AV_FEAT_NO_SCO_SSPD),
                       bte_av_callback);
index a37dc63..0289692 100755 (executable)
@@ -56,7 +56,9 @@
 #define IDX_GET_APP_ATTR_TXT_RSP  5
 #define IDX_GET_APP_VAL_TXT_RSP   6
 #define IDX_GET_ELEMENT_ATTR_RSP  7
-
+#define MAX_VOLUME 128
+#define MAX_LABEL 16
+#define MAX_TRANSACTIONS_PER_SESSION 16
 #define MAX_CMD_QUEUE_LEN 8
 
 #define CHECK_RC_CONNECTED                                                                  \
@@ -112,8 +114,25 @@ typedef struct {
     UINT16                      rc_pending_play;
     btif_rc_cmd_ctxt_t          rc_pdu_info[MAX_CMD_QUEUE_LEN];
     btif_rc_reg_notifications_t rc_notif[MAX_RC_NOTIFICATIONS];
+    unsigned int                rc_volume;
+    uint8_t                     rc_vol_label;
 } btif_rc_cb_t;
 
+typedef struct {
+    BOOLEAN in_use;
+    UINT8 lbl;
+    UINT8 handle;
+} rc_transaction_t;
+
+typedef struct
+{
+    pthread_mutex_t lbllock;
+    rc_transaction_t transaction[MAX_TRANSACTIONS_PER_SESSION];
+} rc_device_t;
+
+
+rc_device_t device;
+
 #define MAX_UINPUT_PATHS 3
 static const char* uinput_dev_path[] =
                        {"/dev/uinput", "/dev/input/uinput", "/dev/misc/uinput" };
@@ -147,7 +166,16 @@ static void send_reject_response (UINT8 rc_handle, UINT8 label,
 static UINT8 opcode_from_pdu(UINT8 pdu);
 static void send_metamsg_rsp (UINT8 rc_handle, UINT8 label,
     tBTA_AV_CODE code, tAVRC_RESPONSE *pmetamsg_resp);
+static void register_volumechange(UINT8 label);
+static void lbl_init();
+static void lbl_destroy();
+static void init_all_transactions();
+static bt_status_t  get_transaction(rc_transaction_t **ptransaction);
+static void release_transaction(UINT8 label);
+static rc_transaction_t* get_transaction_by_lbl(UINT8 label);
+static void handle_rc_metamsg_rsp(tBTA_AV_META_MSG *pmeta_msg);
 static void btif_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND* p_param, UINT8 ctype, UINT8 label);
+static void btif_rc_upstreams_rsp_evt(UINT16 event, tAVRC_RESPONSE *pavrc_resp, UINT8 ctype, UINT8 label);
 
 /*****************************************************************************
 **  Static variables
@@ -291,7 +319,62 @@ void close_uinput (void)
     }
 }
 
+void handle_rc_features()
+{
+    btrc_remote_features_t rc_features = BTRC_FEAT_NONE;
+    bt_bdaddr_t rc_addr;
+    bdcpy(rc_addr.address, btif_rc_cb.rc_addr);
 
+    if (btif_rc_cb.rc_features & BTA_AV_FEAT_BROWSE)
+    {
+        rc_features |= BTRC_FEAT_BROWSE;
+    }
+    if ( (btif_rc_cb.rc_features & BTA_AV_FEAT_ADV_CTRL) &&
+         (btif_rc_cb.rc_features & BTA_AV_FEAT_RCTG))
+    {
+        rc_features |= BTRC_FEAT_ABSOLUTE_VOLUME;
+    }
+    if (btif_rc_cb.rc_features & BTA_AV_FEAT_METADATA)
+    {
+        rc_features |= BTRC_FEAT_METADATA;
+    }
+    BTIF_TRACE_DEBUG2("%s: rc_features=0x%x", __FUNCTION__, rc_features);
+    HAL_CBACK(bt_rc_callbacks, remote_features_cb, &rc_addr, rc_features)
+
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+     BTIF_TRACE_DEBUG1("Checking for feature flags in btif_rc_handler with label %d",
+                        btif_rc_cb.rc_vol_label);
+     // Register for volume change on connect
+      if(btif_rc_cb.rc_features & BTA_AV_FEAT_ADV_CTRL &&
+         btif_rc_cb.rc_features & BTA_AV_FEAT_RCTG)
+      {
+         rc_transaction_t *p_transaction=NULL;
+         bt_status_t status = BT_STATUS_NOT_READY;
+         if(MAX_LABEL==btif_rc_cb.rc_vol_label)
+         {
+            status=get_transaction(&p_transaction);
+         }
+         else
+         {
+            p_transaction=get_transaction_by_lbl(btif_rc_cb.rc_vol_label);
+            if(NULL!=p_transaction)
+            {
+               BTIF_TRACE_DEBUG1("register_volumechange already in progress for label %d",
+                                  btif_rc_cb.rc_vol_label);
+               return;
+            }
+            else
+              status=get_transaction(&p_transaction);
+         }
+
+         if(BT_STATUS_SUCCESS == status && NULL!=p_transaction)
+         {
+            btif_rc_cb.rc_vol_label=p_transaction->lbl;
+            register_volumechange(btif_rc_cb.rc_vol_label);
+         }
+       }
+#endif
+}
 
 
 /***************************************************************************
@@ -313,10 +396,16 @@ void handle_rc_connect (tBTA_AV_RC_OPEN *p_rc_open)
     {
         memcpy(btif_rc_cb.rc_addr, p_rc_open->peer_addr, sizeof(BD_ADDR));
         btif_rc_cb.rc_features = p_rc_open->peer_features;
+        btif_rc_cb.rc_vol_label=MAX_LABEL;
+        btif_rc_cb.rc_volume=MAX_VOLUME;
 
         btif_rc_cb.rc_connected = TRUE;
         btif_rc_cb.rc_handle = p_rc_open->rc_handle;
 
+        /* on locally initiated connection we will get remote features as part of connect */
+        if (btif_rc_cb.rc_features != 0)
+            handle_rc_features();
+
         result = uinput_driver_check();
         if(result == BT_STATUS_SUCCESS)
         {
@@ -347,6 +436,9 @@ void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close)
     btif_rc_cb.rc_connected = FALSE;
     memset(btif_rc_cb.rc_addr, 0, sizeof(BD_ADDR));
     btif_rc_cb.rc_features = 0;
+    btif_rc_cb.rc_vol_label=MAX_LABEL;
+    btif_rc_cb.rc_volume=MAX_VOLUME;
+    init_all_transactions();
     close_uinput();
 }
 
@@ -499,16 +591,38 @@ void handle_rc_metamsg_cmd (tBTA_AV_META_MSG *pmeta_msg)
 
     if (pmeta_msg->code >= AVRC_RSP_NOT_IMPL)
     {
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+{
+     rc_transaction_t *transaction=NULL;
+     transaction=get_transaction_by_lbl(pmeta_msg->label);
+     if(NULL!=transaction)
+     {
+        handle_rc_metamsg_rsp(pmeta_msg);
+     }
+     else
+     {
+         BTIF_TRACE_DEBUG3("%s:Discard vendor dependent rsp. code: %d label:%d.",
+             __FUNCTION__, pmeta_msg->code, pmeta_msg->label);
+     }
+     return;
+}
+#else
+{
         BTIF_TRACE_DEBUG3("%s:Received vendor dependent rsp. code: %d len: %d. Not processing it.",
             __FUNCTION__, pmeta_msg->code, pmeta_msg->len);
         return;
-    }
-    status = AVRC_ParsCommand(pmeta_msg->p_msg, &avrc_command, scratch_buf, sizeof(scratch_buf));
+}
+#endif
+      }
+
+    status=AVRC_ParsCommand(pmeta_msg->p_msg, &avrc_command, scratch_buf, sizeof(scratch_buf));
+    BTIF_TRACE_DEBUG3("Received vendor command.code,PDU and label: %d, %d,%d",pmeta_msg->code,
+                       avrc_command.cmd.pdu, pmeta_msg->label);
 
     if (status != AVRC_STS_NO_ERROR)
     {
         /* return error */
-        BTIF_TRACE_WARNING2("%s: Error in parsing received  metamsg command. status: 0x%02x",
+        BTIF_TRACE_WARNING2("%s: Error in parsing received metamsg command. status: 0x%02x",
             __FUNCTION__, status);
         send_reject_response(pmeta_msg->rc_handle, pmeta_msg->label, avrc_command.pdu, status);
     }
@@ -520,8 +634,8 @@ void handle_rc_metamsg_cmd (tBTA_AV_META_MSG *pmeta_msg)
         {
             UINT8 event_id = avrc_command.reg_notif.event_id;
             param_len = sizeof(tAVRC_REG_NOTIF_CMD);
-            BTIF_TRACE_EVENT3("%s: New register notification received. event_id:%s, label:0x%x",
-                 __FUNCTION__, dump_rc_notification_event_id(event_id), pmeta_msg->label);
+            BTIF_TRACE_EVENT4("%s:New register notification received.event_id:%s,label:0x%x,code:%x",
+            __FUNCTION__,dump_rc_notification_event_id(event_id), pmeta_msg->label,pmeta_msg->code);
             btif_rc_cb.rc_notif[event_id-1].bNotify = TRUE;
             btif_rc_cb.rc_notif[event_id-1].label = pmeta_msg->label;
 
@@ -540,7 +654,7 @@ void handle_rc_metamsg_cmd (tBTA_AV_META_MSG *pmeta_msg)
             *btif context, no context switching is required. Invoke
             * btif_rc_upstreams_evt directly from here. */
         btif_rc_upstreams_evt((uint16_t)avrc_command.cmd.pdu, &avrc_command, pmeta_msg->code,
-            pmeta_msg->label);
+                               pmeta_msg->label);
     }
 }
 
@@ -578,7 +692,7 @@ void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data)
         {
             BTIF_TRACE_DEBUG1("Peer_features:%x", p_data->rc_feat.peer_features);
             btif_rc_cb.rc_features = p_data->rc_feat.peer_features;
-            /* TODO  Handle RC_FEAT_EVT*/
+            handle_rc_features();
         }
         break;
         case BTA_AV_META_MSG_EVT:
@@ -914,6 +1028,48 @@ static void btif_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8
 
 }
 
+
+/*******************************************************************************
+**
+** Function         btif_rc_upstreams_rsp_evt
+**
+** Description      Executes AVRC UPSTREAMS response events in btif context.
+**
+** Returns          void
+**
+*******************************************************************************/
+static void btif_rc_upstreams_rsp_evt(UINT16 event, tAVRC_RESPONSE *pavrc_resp, UINT8 ctype, UINT8 label)
+{
+    BTIF_TRACE_EVENT5("%s pdu: %s handle: 0x%x ctype:%x label:%x", __FUNCTION__,
+        dump_rc_pdu(pavrc_resp->pdu), btif_rc_cb.rc_handle, ctype, label);
+
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+    switch (event)
+    {
+        case AVRC_PDU_REGISTER_NOTIFICATION:
+        {
+             if(AVRC_RSP_CHANGED==ctype)
+                 btif_rc_cb.rc_volume=pavrc_resp->reg_notif.param.volume;
+             HAL_CBACK(bt_rc_callbacks, volume_change_cb, pavrc_resp->reg_notif.param.volume,ctype)
+        }
+        break;
+
+        case AVRC_PDU_SET_ABSOLUTE_VOLUME:
+        {
+            BTIF_TRACE_DEBUG2("Set absolute volume change event received: volume %d,ctype %d",
+                pavrc_resp->volume.volume,ctype);
+            if(AVRC_RSP_ACCEPT==ctype)
+                btif_rc_cb.rc_volume=pavrc_resp->volume.volume;
+            HAL_CBACK(bt_rc_callbacks,volume_change_cb,pavrc_resp->volume.volume,ctype)
+        }
+        break;
+
+        default:
+            return;
+    }
+#endif
+}
+
 /************************************************************************************
 **  AVRCP API Functions
 ************************************************************************************/
@@ -937,6 +1093,9 @@ static bt_status_t init(btrc_callbacks_t* callbacks )
 
     bt_rc_callbacks = callbacks;
     memset (&btif_rc_cb, 0, sizeof(btif_rc_cb));
+    btif_rc_cb.rc_vol_label=MAX_LABEL;
+    btif_rc_cb.rc_volume=MAX_VOLUME;
+    lbl_init();
 
     return result;
 }
@@ -1064,6 +1223,202 @@ static bt_status_t register_notification_rsp(btrc_event_id_t event_id,
 
 /***************************************************************************
 **
+** Function         set_volume
+**
+** Description      Send current volume setting to remote side.
+**                  Support limited to SetAbsoluteVolume
+**                  This can be enhanced to support Relative Volume (AVRCP 1.0).
+**                  With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN
+**                  as opposed to absolute volume level
+** volume: Should be in the range 0-127. bit7 is reseved and cannot be set
+**
+** Returns          bt_status_t
+**
+***************************************************************************/
+static bt_status_t set_volume(uint8_t volume)
+{
+    BTIF_TRACE_DEBUG1("%s", __FUNCTION__);
+    CHECK_RC_CONNECTED
+    tAVRC_STS status = BT_STATUS_UNSUPPORTED;
+    rc_transaction_t *p_transaction=NULL;
+
+    if(btif_rc_cb.rc_volume==volume)
+    {
+        status=BT_STATUS_DONE;
+        BTIF_TRACE_ERROR2("%s: volume value already set earlier: 0x%02x",__FUNCTION__, volume);
+        return status;
+    }
+
+    if ((btif_rc_cb.rc_features & BTA_AV_FEAT_RCTG) &&
+        (btif_rc_cb.rc_features & BTA_AV_FEAT_ADV_CTRL))
+    {
+        tAVRC_COMMAND avrc_cmd = {0};
+        BT_HDR *p_msg = NULL;
+
+        BTIF_TRACE_DEBUG2("%s: Peer supports absolute volume. newVolume=%d", __FUNCTION__, volume);
+        avrc_cmd.volume.opcode = AVRC_OP_VENDOR;
+        avrc_cmd.volume.pdu = AVRC_PDU_SET_ABSOLUTE_VOLUME;
+        avrc_cmd.volume.status = AVRC_STS_NO_ERROR;
+        avrc_cmd.volume.volume = volume;
+
+        if (AVRC_BldCommand(&avrc_cmd, &p_msg) == AVRC_STS_NO_ERROR)
+        {
+            bt_status_t tran_status=get_transaction(&p_transaction);
+            if(BT_STATUS_SUCCESS == tran_status && NULL!=p_transaction)
+            {
+                BTIF_TRACE_DEBUG2("%s msgreq being sent out with label %d",
+                                   __FUNCTION__,p_transaction->lbl);
+                BTA_AvMetaCmd(btif_rc_cb.rc_handle,p_transaction->lbl, AVRC_CMD_CTRL, p_msg);
+                status =  BT_STATUS_SUCCESS;
+            }
+            else
+            {
+                if(NULL!=p_msg)
+                   GKI_freebuf(p_msg);
+                BTIF_TRACE_ERROR2("%s: failed to obtain transaction details. status: 0x%02x",
+                                    __FUNCTION__, tran_status);
+                status = BT_STATUS_FAIL;
+            }
+        }
+        else
+        {
+            BTIF_TRACE_ERROR2("%s: failed to build absolute volume command. status: 0x%02x",
+                                __FUNCTION__, status);
+            status = BT_STATUS_FAIL;
+        }
+    }
+    else
+        status=BT_STATUS_NOT_READY;
+    return status;
+}
+
+
+/***************************************************************************
+**
+** Function         register_volumechange
+**
+** Description     Register for volume change notification from remote side.
+**
+** Returns          void
+**
+***************************************************************************/
+
+static void register_volumechange (UINT8 lbl)
+{
+    tAVRC_COMMAND avrc_cmd = {0};
+    BT_HDR *p_msg = NULL;
+    tAVRC_STS BldResp=AVRC_STS_BAD_CMD;
+    UINT16 rv = 0;
+    bt_status_t tran_status;
+    rc_transaction_t *p_transaction=NULL;
+
+    BTIF_TRACE_DEBUG2("%s called with label:%d",__FUNCTION__,lbl);
+
+    avrc_cmd.cmd.opcode=0x00;
+    avrc_cmd.pdu = AVRC_PDU_REGISTER_NOTIFICATION;
+    avrc_cmd.reg_notif.event_id = AVRC_EVT_VOLUME_CHANGE;
+    avrc_cmd.reg_notif.status = AVRC_STS_NO_ERROR;
+
+    BldResp=AVRC_BldCommand(&avrc_cmd, &p_msg);
+    if(AVRC_STS_NO_ERROR==BldResp && p_msg)
+    {
+         p_transaction=get_transaction_by_lbl(lbl);
+         if(NULL!=p_transaction)
+         {
+             BTA_AvMetaCmd(btif_rc_cb.rc_handle,p_transaction->lbl, AVRC_CMD_NOTIF, p_msg);
+             BTIF_TRACE_DEBUG1("%s:BTA_AvMetaCmd called",__FUNCTION__);
+         }
+         else
+         {
+            if(NULL!=p_msg)
+               GKI_freebuf(p_msg);
+            BTIF_TRACE_ERROR2("%s transaction not obtained with label: %d",__FUNCTION__,lbl);
+         }
+    }
+    else
+        BTIF_TRACE_ERROR2("%s failed to build command:%d",__FUNCTION__,BldResp);
+}
+
+
+/***************************************************************************
+**
+** Function         handle_rc_metamsg_rsp
+**
+** Description      Handle RC metamessage response
+**
+** Returns          void
+**
+***************************************************************************/
+static void handle_rc_metamsg_rsp(tBTA_AV_META_MSG *pmeta_msg)
+{
+    tAVRC_RESPONSE    avrc_response = {0};
+    UINT8             scratch_buf[512] = {0};
+    tAVRC_STS status = BT_STATUS_UNSUPPORTED;
+
+    if(AVRC_OP_VENDOR==pmeta_msg->p_msg->hdr.opcode &&(AVRC_RSP_CHANGED==pmeta_msg->code
+      || AVRC_RSP_INTERIM==pmeta_msg->code || AVRC_RSP_ACCEPT==pmeta_msg->code
+      || AVRC_RSP_REJ==pmeta_msg->code || AVRC_RSP_NOT_IMPL==pmeta_msg->code))
+    {
+        status=AVRC_ParsResponse(pmeta_msg->p_msg, &avrc_response, scratch_buf, sizeof(scratch_buf));
+        BTIF_TRACE_DEBUG6("%s: code %d,event ID %d,PDU %x,parsing status %d, label:%d",
+          __FUNCTION__,pmeta_msg->code,avrc_response.reg_notif.event_id,avrc_response.reg_notif.pdu,
+          status, pmeta_msg->label);
+
+        if (status != AVRC_STS_NO_ERROR)
+        {
+            if(AVRC_PDU_REGISTER_NOTIFICATION==avrc_response.rsp.pdu
+                && AVRC_EVT_VOLUME_CHANGE==avrc_response.reg_notif.event_id
+                && btif_rc_cb.rc_vol_label==pmeta_msg->label)
+            {
+                btif_rc_cb.rc_vol_label=MAX_LABEL;
+                release_transaction(btif_rc_cb.rc_vol_label);
+            }
+            else if(AVRC_PDU_SET_ABSOLUTE_VOLUME==avrc_response.rsp.pdu)
+            {
+                release_transaction(pmeta_msg->label);
+            }
+            return;
+        }
+        else if(AVRC_PDU_REGISTER_NOTIFICATION==avrc_response.rsp.pdu
+            && AVRC_EVT_VOLUME_CHANGE==avrc_response.reg_notif.event_id
+            && btif_rc_cb.rc_vol_label!=pmeta_msg->label)
+            {
+                // Just discard the message, if the device sends back with an incorrect label
+                BTIF_TRACE_DEBUG3("%s:Discarding register notfn in rsp.code: %d and label %d",
+                __FUNCTION__, pmeta_msg->code, pmeta_msg->label);
+                return;
+            }
+    }
+    else
+    {
+        BTIF_TRACE_DEBUG3("%s:Received vendor dependent in adv ctrl rsp. code: %d len: %d. Not processing it.",
+        __FUNCTION__, pmeta_msg->code, pmeta_msg->len);
+        return;
+    }
+
+    if(AVRC_PDU_REGISTER_NOTIFICATION==avrc_response.rsp.pdu
+        && AVRC_EVT_VOLUME_CHANGE==avrc_response.reg_notif.event_id
+        && AVRC_RSP_CHANGED==pmeta_msg->code)
+     {
+         /* re-register for volume change notification */
+         // Do not re-register for rejected case, as it might get into endless loop
+         register_volumechange(btif_rc_cb.rc_vol_label);
+     }
+     else if(AVRC_PDU_SET_ABSOLUTE_VOLUME==avrc_response.rsp.pdu)
+     {
+          /* free up the label here */
+          release_transaction(pmeta_msg->label);
+     }
+
+     BTIF_TRACE_EVENT2("%s: Passing received metamsg response to app. pdu: %s",
+             __FUNCTION__, dump_rc_pdu(avrc_response.pdu));
+     btif_rc_upstreams_rsp_evt((uint16_t)avrc_response.rsp.pdu, &avrc_response, pmeta_msg->code,
+                                pmeta_msg->label);
+}
+
+
+/***************************************************************************
+**
 ** Function         cleanup
 **
 ** Description      Closes the AVRC interface
@@ -1080,6 +1435,7 @@ static void cleanup()
         bt_rc_callbacks = NULL;
     }
     memset(&btif_rc_cb, 0, sizeof(btif_rc_cb_t));
+    lbl_destroy();
 }
 
 
@@ -1095,6 +1451,7 @@ static const btrc_interface_t bt_rc_interface = {
     get_element_attr_rsp,
     NULL, /* set_player_app_value_rsp */
     register_notification_rsp,
+    set_volume,
     cleanup,
 };
 
@@ -1111,4 +1468,155 @@ const btrc_interface_t *btif_rc_get_interface(void)
 {
     BTIF_TRACE_EVENT1("%s", __FUNCTION__);
     return &bt_rc_interface;
-}
\ No newline at end of file
+}
+
+/*******************************************************************************
+**      Function         initialize_transaction
+**
+**      Description    Initializes fields of the transaction structure
+**
+**      Returns          void
+*******************************************************************************/
+static void initialize_transaction(int lbl)
+{
+    pthread_mutex_lock(&device.lbllock);
+    if(lbl < MAX_TRANSACTIONS_PER_SESSION)
+    {
+       device.transaction[lbl].lbl = lbl;
+       device.transaction[lbl].in_use=FALSE;
+       device.transaction[lbl].handle=0;
+    }
+    pthread_mutex_unlock(&device.lbllock);
+}
+
+/*******************************************************************************
+**      Function         lbl_init
+**
+**      Description    Initializes label structures and mutexes.
+**
+**      Returns         void
+*******************************************************************************/
+void lbl_init()
+{
+    memset(&device,0,sizeof(rc_device_t));
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_init(&attr);
+    pthread_mutex_init(&(device.lbllock), &attr);
+    pthread_mutexattr_destroy(&attr);
+    init_all_transactions();
+}
+
+/*******************************************************************************
+**
+** Function         init_all_transactions
+**
+** Description    Initializes all transactions
+**
+** Returns          void
+*******************************************************************************/
+void init_all_transactions()
+{
+    UINT8 txn_indx=0;
+    for(txn_indx=0; txn_indx < MAX_TRANSACTIONS_PER_SESSION; txn_indx++)
+    {
+        initialize_transaction(txn_indx);
+    }
+}
+
+/*******************************************************************************
+**
+** Function         get_transaction_by_lbl
+**
+** Description    Will return a transaction based on the label. If not inuse
+**                     will return an error.
+**
+** Returns          bt_status_t
+*******************************************************************************/
+rc_transaction_t *get_transaction_by_lbl(UINT8 lbl)
+{
+    rc_transaction_t *transaction = NULL;
+    pthread_mutex_lock(&device.lbllock);
+
+    /* Determine if this is a valid label */
+    if (lbl < MAX_TRANSACTIONS_PER_SESSION)
+    {
+        if (FALSE==device.transaction[lbl].in_use)
+        {
+            transaction = NULL;
+        }
+        else
+        {
+            transaction = &(device.transaction[lbl]);
+            BTIF_TRACE_DEBUG2("%s: Got transaction.label: %d",__FUNCTION__,lbl);
+        }
+    }
+
+    pthread_mutex_unlock(&device.lbllock);
+    return transaction;
+}
+
+/*******************************************************************************
+**
+** Function         get_transaction
+**
+** Description    Obtains the transaction details.
+**
+** Returns          bt_status_t
+*******************************************************************************/
+
+bt_status_t  get_transaction(rc_transaction_t **ptransaction)
+{
+    bt_status_t result = BT_STATUS_NOMEM;
+    UINT8 i=0;
+    pthread_mutex_lock(&device.lbllock);
+
+    // Check for unused transactions
+    for (i=0; i<MAX_TRANSACTIONS_PER_SESSION; i++)
+    {
+        if (FALSE==device.transaction[i].in_use)
+        {
+            BTIF_TRACE_DEBUG2("%s:Got transaction.label: %d",__FUNCTION__,device.transaction[i].lbl);
+            device.transaction[i].in_use = TRUE;
+            *ptransaction = &(device.transaction[i]);
+            result = BT_STATUS_SUCCESS;
+            break;
+        }
+    }
+
+    pthread_mutex_unlock(&device.lbllock);
+    return result;
+}
+
+
+/*******************************************************************************
+**
+** Function         release_transaction
+**
+** Description    Will release a transaction for reuse
+**
+** Returns          bt_status_t
+*******************************************************************************/
+void release_transaction(UINT8 lbl)
+{
+    rc_transaction_t *transaction = get_transaction_by_lbl(lbl);
+
+    /* If the transaction is in use... */
+    if (transaction != NULL)
+    {
+        BTIF_TRACE_DEBUG2("%s: lbl: %d", __FUNCTION__, lbl);
+        initialize_transaction(lbl);
+    }
+}
+
+/*******************************************************************************
+**
+** Function         lbl_destroy
+**
+** Description    Cleanup of the mutex
+**
+** Returns          void
+*******************************************************************************/
+void lbl_destroy()
+{
+    pthread_mutex_destroy(&(device.lbllock));
+}
index 636c1bc..6389ccb 100755 (executable)
@@ -472,6 +472,7 @@ const char *dump_rc_event(UINT8 event)
         CASE_RETURN_STR(BTA_AV_VENDOR_CMD_EVT)
         CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT)
         CASE_RETURN_STR(BTA_AV_META_MSG_EVT)
+        CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT)
         default:
             return "UNKNOWN_EVENT";
    }
@@ -489,6 +490,7 @@ const char * dump_rc_notification_event_id(UINT8 event_id)
         CASE_RETURN_STR(AVRC_EVT_BATTERY_STATUS_CHANGE)
         CASE_RETURN_STR(AVRC_EVT_SYSTEM_STATUS_CHANGE)
         CASE_RETURN_STR(AVRC_EVT_APP_SETTING_CHANGE)
+        CASE_RETURN_STR(AVRC_EVT_VOLUME_CHANGE)
 
         default:
             return "Unhandled Event ID";
@@ -512,7 +514,7 @@ const char*  dump_rc_pdu(UINT8 pdu)
         CASE_RETURN_STR(AVRC_PDU_REGISTER_NOTIFICATION)
         CASE_RETURN_STR(AVRC_PDU_REQUEST_CONTINUATION_RSP)
         CASE_RETURN_STR(AVRC_PDU_ABORT_CONTINUATION_RSP)
-
+        CASE_RETURN_STR(AVRC_PDU_SET_ABSOLUTE_VOLUME)
         default:
             return "Unknown PDU";
     }
index 4484ac7..99db42b 100644 (file)
@@ -3487,6 +3487,10 @@ Range: Minimum 12000 (12 secs) when supporting PBF.
 #define AVRC_METADATA_INCLUDED      TRUE
 #endif
 
+#ifndef AVRC_ADV_CTRL_INCLUDED
+#define AVRC_ADV_CTRL_INCLUDED      TRUE
+#endif
+
 /******************************************************************************
 **
 ** MCAP
index 10e9bef..92ce253 100755 (executable)
@@ -47,7 +47,9 @@ LOCAL_SRC_FILES:= \
     ./avrc/avrc_sdp.c \
     ./avrc/avrc_opt.c \
     ./avrc/avrc_bld_tg.c \
+    ./avrc/avrc_bld_ct.c \
     ./avrc/avrc_pars_tg.c \
+    ./avrc/avrc_pars_ct.c \
     ./avrc/avrc_utils.c \
     ./hid/hidh_api.c \
     ./hid/hidh_conn.c \
index c2e36d7..c572f94 100755 (executable)
@@ -132,7 +132,8 @@ static void avrc_prep_end_frag(UINT8 handle)
     p_fcb = &avrc_cb.fcb[handle];
 
     /* The response type of the end fragment should be the same as the the PDU of "End Fragment
-    ** Respose" Errata: https://www.bluetooth.org/errata/errata_view.cfm?errata_id=4383 */
+    ** Response" Errata: https://www.bluetooth.org/errata/errata_view.cfm?errata_id=4383
+    */
     p_orig_data = ((UINT8 *)(p_fcb->p_fmsg + 1) + p_fcb->p_fmsg->offset);
     rsp_type = ((*p_orig_data) & AVRC_CTYPE_MASK);
 
@@ -146,6 +147,7 @@ static void avrc_prep_end_frag(UINT8 handle)
     AVRC_CO_ID_TO_BE_STREAM(p_data, AVRC_CO_METADATA);
     *p_data++       = p_fcb->frag_pdu;
     *p_data++       = AVRC_PKT_END;
+
     /* 4=pdu, pkt_type & len */
     UINT16_TO_BE_STREAM(p_data, (p_pkt_new->len - AVRC_VENDOR_HDR_SIZE - AVRC_MIN_META_HDR_SIZE));
 }
@@ -337,7 +339,7 @@ static BT_HDR * avrc_proc_vendor_command(UINT8 handle, UINT8 label,
 **
 ** Function         avrc_proc_far_msg
 **
-** Description      This function processes vendor command/response fragmetation
+** Description      This function processes metadata fragmenation
 **                  and reassembly
 **
 ** Returns          0, to report the message with msg_cback .
@@ -359,6 +361,10 @@ static UINT8 avrc_proc_far_msg(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR **pp_
     tAVRC_NEXT_CMD   avrc_cmd;
 
     p_data  = (UINT8 *)(p_pkt+1) + p_pkt->offset;
+
+    /* Skip over vendor header (ctype, subunit*, opcode, CO_ID) */
+    p_data += AVRC_VENDOR_HDR_SIZE;
+
     pkt_type = *(p_data + 1) & AVRC_PKT_TYPE_MASK;
     AVRC_TRACE_DEBUG1 ("pkt_type %d", pkt_type );
     p_rcb = &avrc_cb.rcb[handle];
@@ -381,13 +387,48 @@ static UINT8 avrc_proc_far_msg(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR **pp_
             /* not a single response packet - need to re-assemble metadata messages */
             if (pkt_type == AVRC_PKT_START)
             {
-                p_rcb->rasm_offset = p_pkt->offset;
-                p_rcb->p_rmsg = p_pkt;
+                /* Allocate buffer for re-assembly */
+                p_rcb->rasm_pdu = *p_data;
+                if ((p_rcb->p_rmsg = (BT_HDR *)GKI_getbuf(GKI_MAX_BUF_SIZE)) != NULL)
+                {
+                    /* Copy START packet to buffer for re-assembling fragments*/
+                    memcpy(p_rcb->p_rmsg, p_pkt, sizeof(BT_HDR));   /* Copy bt hdr */
+
+                    /* Copy metadata message */
+                    memcpy((UINT8 *)(p_rcb->p_rmsg + 1),
+                           (UINT8 *)(p_pkt+1) + p_pkt->offset, p_pkt->len);
+
+                    /* offset of start of metadata response in reassembly buffer */
+                    p_rcb->p_rmsg->offset = p_rcb->rasm_offset = 0;
+
+                    /* Free original START packet, replace with pointer to reassembly buffer  */
+                    GKI_freebuf(p_pkt);
+                    *pp_pkt = p_rcb->p_rmsg;
+                }
+                else
+                {
+                    /* Unable to allocate buffer for fragmented avrc message. Reuse START
+                                      buffer for reassembly (re-assembled message may fit into ACL buf) */
+                    AVRC_TRACE_DEBUG0 ("Unable to allocate buffer for fragmented avrc message, \
+                                       reusing START buffer for reassembly");
+                    p_rcb->rasm_offset = p_pkt->offset;
+                    p_rcb->p_rmsg = p_pkt;
+                }
+
                 /* set offset to point to where to copy next - use the same re-asm logic as AVCT */
                 p_rcb->p_rmsg->offset += p_rcb->p_rmsg->len;
-                p_rcb->rasm_pdu = *p_data;
                 req_continue = TRUE;
             }
+            else if (p_rcb->p_rmsg == NULL)
+            {
+                /* Received a CONTINUE/END, but no corresponding START
+                              (or previous fragmented response was dropped) */
+                AVRC_TRACE_DEBUG0 ("Received a CONTINUE/END without no corresponding START \
+                                   (or previous fragmented response was dropped)");
+                drop = 5;
+                GKI_freebuf(p_pkt);
+                *pp_pkt = NULL;
+            }
             else
             {
                 /* get size of buffer holding assembled message */
@@ -453,7 +494,19 @@ static UINT8 avrc_proc_far_msg(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR **pp_
                 drop = 4;
 
         }
+        else if (cr == AVCT_RSP && req_continue == TRUE)
+        {
+            avrc_cmd.pdu    = AVRC_PDU_REQUEST_CONTINUATION_RSP;
+            avrc_cmd.status = AVRC_STS_NO_ERROR;
+            avrc_cmd.target_pdu = p_rcb->rasm_pdu;
+            if (AVRC_BldCommand ((tAVRC_COMMAND *)&avrc_cmd, &p_cmd) == AVRC_STS_NO_ERROR)
+            {
+                drop = 2;
+                AVRC_MsgReq (handle, (UINT8)(label), AVRC_CMD_CTRL, p_cmd);
+            }
+        }
     }
+
     return drop;
 }
 #endif /* (AVRC_METADATA_INCLUDED == TRUE) */
@@ -604,7 +657,21 @@ static void avrc_msg_cback(UINT8 handle, UINT8 label, UINT8 cr,
             p_msg->vendor_len      = p_pkt->len - (p_data - p_begin);
 
 #if (AVRC_METADATA_INCLUDED == TRUE)
-            drop = avrc_proc_far_msg(handle, label, cr, &p_pkt, p_msg);
+            if (p_msg->company_id == AVRC_CO_METADATA)
+            {
+                /* Validate length for metadata message */
+                if (p_pkt->len < (AVRC_VENDOR_HDR_SIZE + AVRC_MIN_META_HDR_SIZE))
+                {
+                    if (cr == AVCT_CMD)
+                        reject = TRUE;
+                    else
+                        drop = TRUE;
+                    break;
+                }
+
+                /* Check+handle fragmented messages */
+                drop = avrc_proc_far_msg(handle, label, cr, &p_pkt, p_msg);
+            }
             if (drop)
             {
                 free = FALSE;
diff --git a/stack/avrc/avrc_bld_ct.c b/stack/avrc/avrc_bld_ct.c
new file mode 100644 (file)
index 0000000..039eab8
--- /dev/null
@@ -0,0 +1,250 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2006-2013 Broadcom Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#include <string.h>
+
+#include "gki.h"
+#include "avrc_api.h"
+#include "avrc_defs.h"
+#include "avrc_int.h"
+
+/*****************************************************************************
+**  Global data
+*****************************************************************************/
+
+
+#if (AVRC_METADATA_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function         avrc_bld_next_cmd
+**
+** Description      This function builds the Request Continue or Abort command.
+**
+** Returns          AVRC_STS_NO_ERROR, if the command is built successfully
+**                  Otherwise, the error code.
+**
+*******************************************************************************/
+static tAVRC_STS avrc_bld_next_cmd (tAVRC_NEXT_CMD *p_cmd, BT_HDR *p_pkt)
+{
+    UINT8   *p_data, *p_start;
+
+    AVRC_TRACE_API0("avrc_bld_next_cmd");
+
+    /* get the existing length, if any, and also the num attributes */
+    p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+    p_data = p_start + 2; /* pdu + rsvd */
+
+    /* add fixed lenth 1 - pdu_id (1) */
+    UINT16_TO_BE_STREAM(p_data, 1);
+    UINT8_TO_BE_STREAM(p_data, p_cmd->target_pdu);
+    p_pkt->len = (p_data - p_start);
+
+    return AVRC_STS_NO_ERROR;
+}
+
+/*****************************************************************************
+**  the following commands are introduced in AVRCP 1.4
+*****************************************************************************/
+
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function         avrc_bld_set_abs_volume_cmd
+**
+** Description      This function builds the Set Absolute Volume command.
+**
+** Returns          AVRC_STS_NO_ERROR, if the command is built successfully
+**                  Otherwise, the error code.
+**
+*******************************************************************************/
+static tAVRC_STS avrc_bld_set_abs_volume_cmd (tAVRC_SET_VOLUME_CMD *p_cmd, BT_HDR *p_pkt)
+{
+    UINT8   *p_data, *p_start;
+
+    AVRC_TRACE_API0("avrc_bld_set_abs_volume_cmd");
+    /* get the existing length, if any, and also the num attributes */
+    p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+    p_data = p_start + 2; /* pdu + rsvd */
+    /* add fixed lenth 1 - volume (1) */
+    UINT16_TO_BE_STREAM(p_data, 1);
+    UINT8_TO_BE_STREAM(p_data, (AVRC_MAX_VOLUME & p_cmd->volume));
+    p_pkt->len = (p_data - p_start);
+    return AVRC_STS_NO_ERROR;
+}
+
+/*******************************************************************************
+**
+** Function         avrc_bld_vol_change_notfn
+**
+** Description      This function builds the register notification for volume change.
+**
+** Returns          AVRC_STS_NO_ERROR, if the command is built successfully
+**                  Otherwise, the error code.
+**
+*******************************************************************************/
+static tAVRC_STS avrc_bld_vol_change_notfn(BT_HDR * p_pkt)
+{
+    UINT8   *p_data, *p_start;
+
+    AVRC_TRACE_API0("avrc_bld_vol_change");
+    /* get the existing length, if any, and also the num attributes */
+    // Set the notify value
+    p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset;
+    p_data = p_start + 2; /* pdu + rsvd */
+    /* add fixed length 5 -*/
+    UINT16_TO_BE_STREAM(p_data, 5);
+    UINT8_TO_BE_STREAM(p_data,AVRC_EVT_VOLUME_CHANGE);
+    UINT32_TO_BE_STREAM(p_data, 0);
+    p_pkt->len = (p_data - p_start);
+    return AVRC_STS_NO_ERROR;
+}
+#endif
+
+/*******************************************************************************
+**
+** Function         avrc_bld_init_cmd_buffer
+**
+** Description      This function initializes the command buffer based on PDU
+**
+** Returns          NULL, if no GKI buffer or failure to build the message.
+**                  Otherwise, the GKI buffer that contains the initialized message.
+**
+*******************************************************************************/
+static BT_HDR *avrc_bld_init_cmd_buffer(tAVRC_COMMAND *p_cmd)
+{
+    UINT16 offset = 0, chnl = AVCT_DATA_CTRL, len=AVRC_META_CMD_POOL_SIZE;
+    BT_HDR *p_pkt=NULL;
+    UINT8  opcode;
+
+    opcode = avrc_opcode_from_pdu(p_cmd->pdu);
+    AVRC_TRACE_API2("avrc_bld_init_cmd_buffer: pdu=%x, opcode=%x", p_cmd->pdu, opcode);
+
+    switch (opcode)
+    {
+    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 */
+    p_pkt = (BT_HDR *)GKI_getbuf(len);
+    if (p_pkt)
+    {
+        UINT8 *p_data, *p_start;
+
+        p_pkt->layer_specific = chnl;
+        p_pkt->event    = opcode;
+        p_pkt->offset   = offset;
+        p_data = (UINT8 *)(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_cmd->pdu;
+
+        switch (opcode)
+        {
+        case AVRC_OP_VENDOR:
+            /* reserved 0, packet_type 0 */
+            UINT8_TO_BE_STREAM(p_data, 0);
+            /* continue to the next "case to add length */
+            /* add fixed lenth - 0 */
+            UINT16_TO_BE_STREAM(p_data, 0);
+            break;
+        }
+
+        p_pkt->len = (p_data - p_start);
+    }
+    p_cmd->cmd.opcode = opcode;
+    return p_pkt;
+}
+
+/*******************************************************************************
+**
+** Function         AVRC_BldCommand
+**
+** Description      This function builds the given AVRCP command to the given
+**                  GKI buffer
+**
+** Returns          AVRC_STS_NO_ERROR, if the command is built successfully
+**                  Otherwise, the error code.
+**
+*******************************************************************************/
+tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt)
+{
+    tAVRC_STS status = AVRC_STS_BAD_PARAM;
+    BT_HDR  *p_pkt;
+    BOOLEAN alloc = FALSE;
+
+    AVRC_TRACE_API2("AVRC_BldCommand: pdu=%x status=%x", p_cmd->cmd.pdu, p_cmd->cmd.status);
+    if (!p_cmd || !pp_pkt)
+    {
+        AVRC_TRACE_API2("AVRC_BldCommand. Invalid parameters passed. p_cmd=%p, pp_pkt=%p",
+            p_cmd, pp_pkt);
+        return AVRC_STS_BAD_PARAM;
+    }
+
+    if (*pp_pkt == NULL)
+    {
+        if ((*pp_pkt = avrc_bld_init_cmd_buffer(p_cmd)) == NULL)
+        {
+            AVRC_TRACE_API0("AVRC_BldCommand: Failed to initialize command buffer");
+            return AVRC_STS_INTERNAL_ERR;
+        }
+        alloc = TRUE;
+    }
+    status = AVRC_STS_NO_ERROR;
+    p_pkt = *pp_pkt;
+
+    switch (p_cmd->pdu)
+    {
+    case AVRC_PDU_REQUEST_CONTINUATION_RSP:     /*        0x40 */
+        status = avrc_bld_next_cmd(&p_cmd->continu, p_pkt);
+        break;
+
+    case AVRC_PDU_ABORT_CONTINUATION_RSP:       /*          0x41 */
+        status = avrc_bld_next_cmd(&p_cmd->abort, p_pkt);
+        break;
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+    case AVRC_PDU_SET_ABSOLUTE_VOLUME:         /* 0x50 */
+        status = avrc_bld_set_abs_volume_cmd(&p_cmd->volume, p_pkt);
+        break;
+#endif
+
+    case AVRC_PDU_REGISTER_NOTIFICATION:      /* 0x31 */
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+        if(AVRC_EVT_VOLUME_CHANGE==p_cmd->reg_notif.event_id)
+           status=avrc_bld_vol_change_notfn(p_pkt);
+#endif
+        break;
+
+    }
+
+    if (alloc && (status != AVRC_STS_NO_ERROR) )
+    {
+        GKI_freebuf(p_pkt);
+        *pp_pkt = NULL;
+    }
+    AVRC_TRACE_API1("AVRC_BldCommand: returning %d", status);
+    return status;
+}
+#endif /* (AVRC_METADATA_INCLUDED == TRUE) */
+
diff --git a/stack/avrc/avrc_pars_ct.c b/stack/avrc/avrc_pars_ct.c
new file mode 100755 (executable)
index 0000000..85a9233
--- /dev/null
@@ -0,0 +1,148 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2006-2013 Broadcom Corporation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+#include <string.h>
+
+#include "gki.h"
+#include "avrc_api.h"
+#include "avrc_defs.h"
+#include "avrc_int.h"
+
+/*****************************************************************************
+**  Global data
+*****************************************************************************/
+
+#if (AVRC_METADATA_INCLUDED == TRUE)
+
+/*******************************************************************************
+**
+** Function         avrc_pars_vendor_rsp
+**
+** 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_rsp(tAVRC_MSG_VENDOR *p_msg, tAVRC_RESPONSE *p_result, UINT8 *p_buf, UINT16 buf_len)
+{
+    tAVRC_STS  status = AVRC_STS_NO_ERROR;
+    UINT8   *p = p_msg->p_vendor_data;
+    UINT16  len;
+    UINT8   xx, yy;
+    tAVRC_NOTIF_RSP_PARAM   *p_param;
+    tAVRC_APP_SETTING       *p_app_set;
+    tAVRC_APP_SETTING_TEXT  *p_app_txt;
+    tAVRC_ATTR_ENTRY        *p_entry;
+    UINT32  *p_u32;
+    UINT8   *p_u8;
+    UINT16  size_needed;
+    UINT8 eventid=0;
+
+    BE_STREAM_TO_UINT8 (p_result->pdu, p);
+    p++; /* skip the reserved/packe_type byte */
+    BE_STREAM_TO_UINT16 (len, p);
+    AVRC_TRACE_DEBUG4("avrc_pars_vendor_rsp() ctype:0x%x pdu:0x%x, len:%d/0x%x", p_msg->hdr.ctype, p_result->pdu, len, len);
+    if (p_msg->hdr.ctype == AVRC_RSP_REJ)
+    {
+        p_result->rsp.status = *p;
+        return p_result->rsp.status;
+    }
+
+    switch (p_result->pdu)
+    {
+    /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */
+    /* case AVRC_PDU_ABORT_CONTINUATION_RSP:   0x41 */
+
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+    case AVRC_PDU_SET_ABSOLUTE_VOLUME:      /* 0x50 */
+        if (len != 1)
+            status = AVRC_STS_INTERNAL_ERR;
+        else
+        {
+            BE_STREAM_TO_UINT8 (p_result->volume.volume, p);
+        }
+        break;
+#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */
+
+    case AVRC_PDU_REGISTER_NOTIFICATION:    /* 0x31 */
+#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
+        BE_STREAM_TO_UINT8 (eventid, p);
+        if(AVRC_EVT_VOLUME_CHANGE==eventid
+            && (AVRC_RSP_CHANGED==p_msg->hdr.ctype || AVRC_RSP_INTERIM==p_msg->hdr.ctype
+            || AVRC_RSP_REJ==p_msg->hdr.ctype || AVRC_RSP_NOT_IMPL==p_msg->hdr.ctype))
+        {
+            p_result->reg_notif.status=p_msg->hdr.ctype;
+            p_result->reg_notif.event_id=eventid;
+            BE_STREAM_TO_UINT8 (p_result->reg_notif.param.volume, p);
+        }
+        AVRC_TRACE_DEBUG2("avrc_pars_vendor_rsp PDU reg notif response:event %x, volume %x",eventid,
+            p_result->reg_notif.param.volume);
+#endif /* (AVRC_ADV_CTRL_INCLUDED == TRUE) */
+        break;
+    default:
+        status = AVRC_STS_BAD_CMD;
+        break;
+    }
+
+    return status;
+}
+
+/*******************************************************************************
+**
+** 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;
+
+    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, 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_ERROR1("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;
+}
+
+
+#endif /* (AVRC_METADATA_INCLUDED == TRUE) */
index 0d9cf34..d3d5262 100755 (executable)
@@ -244,9 +244,19 @@ static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR *p_msg, tAVRC_COMMAND *p_
     case AVRC_PDU_REGISTER_NOTIFICATION:    /* 0x31 */
         if (len != 5)
             status = AVRC_STS_INTERNAL_ERR;
-        BE_STREAM_TO_UINT8 (p_result->reg_notif.event_id, p);
-        BE_STREAM_TO_UINT32 (p_result->reg_notif.param, p);
+        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 */
index e447e48..7ddf0f7 100755 (executable)
@@ -38,13 +38,26 @@ tAVRC_CB avrc_cb;
 const tSDP_PROTOCOL_ELEM  avrc_proto_list [] =
 {
     {UUID_PROTOCOL_L2CAP, 1, {AVCT_PSM, 0} },
+#if AVRC_ADV_CTRL_INCLUDED == TRUE
+    {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0}  }
+#else
 #if AVRC_METADATA_INCLUDED == TRUE
     {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_2, 0}  }
 #else
     {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_0, 0}  }
 #endif
+#endif
 };
 
+#if AVRC_ADV_CTRL_INCLUDED == TRUE
+const tSDP_PROTO_LIST_ELEM  avrc_add_proto_list [] =
+{
+    {AVRC_NUM_PROTO_ELEMS,
+    {
+    {UUID_PROTOCOL_L2CAP, 1, {AVCT_BR_PSM, 0} },
+    {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0}  }}}
+};
+#endif
 
 
 /******************************************************************************
@@ -210,18 +223,31 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name,
 
     /* add service class id list */
     class_list[0] = service_uuid;
+#if AVRC_ADV_CTRL_INCLUDED == TRUE
+    if( service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL )
+    {
+        class_list[1] = UUID_SERVCLASS_AV_REM_CTRL_CONTROL;
+        count = 2;
+    }
+#endif
     result &= SDP_AddServiceClassIdList(sdp_handle, count, class_list);
 
     /* add protocol descriptor list   */
     result &= SDP_AddProtocolList(sdp_handle, AVRC_NUM_PROTO_ELEMS, (tSDP_PROTOCOL_ELEM *)avrc_proto_list);
 
     /* add profile descriptor list   */
+#if AVRC_ADV_CTRL_INCLUDED == TRUE
+    /* additional protocol list to include browsing channel */
+    result &= SDP_AddAdditionProtoLists( sdp_handle, 1, (tSDP_PROTO_LIST_ELEM *)avrc_add_proto_list);
+
+    result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_4);
+#else
 #if AVRC_METADATA_INCLUDED == TRUE
     result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_3);
 #else
     result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_0);
 #endif
-
+#endif
 
     /* add supported categories */
     p = temp;