diff options
author | Ayan Ghosh <abghosh@codeaurora.org> | 2014-07-25 17:51:32 +0530 |
---|---|---|
committer | Linux Build Service Account <lnxbuild@localhost> | 2015-10-06 03:21:46 -0600 |
commit | eceafe97c9a07a5bf4e5f84cadaad3b731c38d21 (patch) | |
tree | daa810c800a3e34ae6c812f51b5d279aeac2fd23 | |
parent | 0887ba37967b83b0264f9f336f5f3d8074daf761 (diff) | |
download | android_system_bt-eceafe97c9a07a5bf4e5f84cadaad3b731c38d21.tar.gz android_system_bt-eceafe97c9a07a5bf4e5f84cadaad3b731c38d21.tar.bz2 android_system_bt-eceafe97c9a07a5bf4e5f84cadaad3b731c38d21.zip |
AVRCP 1.5 Implementation.
AVRCP 1.5 Implementation
Change-Id: Ia35dc4344b05584cf5ab3b463815c9e6297d24ab
Proper check to ensure attributes are populated properly
Incorporated proper check to ensure if memory is allocated properly
then all requested attributes are populated in response from DUT.
CRs-Fixed: 753309
Change-Id: Iead549635cd2059bc8cf33585a5d87ae023149d4
Reset Browsing Bit if carkit is blacklisted
- Reset Browsing bit if carkit is blacklisted for avrcp 1.5.
- Not to send 1.3+ specific supported events if carkit is blacklisted.
CRs-Fixed: 762470
Change-Id: I32ac80c0549b2b89dd2522dbb0dea89d255d33dc
Free Browse packet
Free Borwse packet in AVRC layer once the same is
copied to BTA for further processing.
CRs-Fixed: 785286
Change-Id: I8037a649cff5a1e527c28ba36999a1bed34d315a
BTIF-AV: Use valid memory for AVRCP message
After the context switch to BTIF for META message processing
the buffer allocated for tAVRC_MSG will be freed, hence allocate
the required memory and free that after handling the META
message.
Conflicts:
stack/avrc/avrc_api.c
Crs-Fixed: 651506
Change-Id: Icbf61776a6fb63fac1555bf25d3700beccf2b67a
-rw-r--r-- | bta/av/bta_av_act.c | 93 | ||||
-rw-r--r-- | bta/av/bta_av_cfg.c | 17 | ||||
-rw-r--r-- | bta/include/bta_av_api.h | 18 | ||||
-rw-r--r-- | btif/src/btif_av.c | 43 | ||||
-rw-r--r-- | btif/src/btif_rc.c | 1217 | ||||
-rw-r--r-- | btif/src/btif_util.c | 5 | ||||
-rw-r--r-- | include/bt_target.h | 13 | ||||
-rw-r--r-- | stack/avct/avct_api.c | 3 | ||||
-rw-r--r-- | stack/avct/avct_ccb.c | 39 | ||||
-rw-r--r-- | stack/avct/avct_int.h | 2 | ||||
-rw-r--r-- | stack/avct/avct_l2c.c | 396 | ||||
-rw-r--r-- | stack/avct/avct_lcb.c | 155 | ||||
-rw-r--r-- | stack/avct/avct_lcb_act.c | 564 | ||||
-rwxr-xr-x | stack/avrc/avrc_api.c | 385 | ||||
-rw-r--r-- | stack/avrc/avrc_bld_tg.c | 461 | ||||
-rw-r--r-- | stack/avrc/avrc_int.h | 1 | ||||
-rw-r--r-- | stack/avrc/avrc_pars_tg.c | 24 | ||||
-rw-r--r-- | stack/avrc/avrc_sdp.c | 35 | ||||
-rw-r--r-- | stack/avrc/avrc_utils.c | 2 | ||||
-rw-r--r-- | stack/include/avct_api.h | 16 | ||||
-rw-r--r-- | stack/include/avrc_api.h | 15 | ||||
-rw-r--r-- | stack/include/avrc_defs.h | 14 | ||||
-rw-r--r-- | stack/include/bt_types.h | 5 | ||||
-rw-r--r-- | stack/include/sdp_api.h | 14 | ||||
-rw-r--r-- | stack/sdp/sdp_api.c | 15 | ||||
-rw-r--r-- | stack/sdp/sdp_server.c | 178 | ||||
-rw-r--r-- | stack/sdp/sdpint.h | 2 |
27 files changed, 3500 insertions, 232 deletions
diff --git a/bta/av/bta_av_act.c b/bta/av/bta_av_act.c index 429d00d2a..fdb50a6a2 100644 --- a/bta/av/bta_av_act.c +++ b/bta/av/bta_av_act.c @@ -270,8 +270,13 @@ static void bta_av_rc_msg_cback(UINT8 handle, UINT8 label, UINT8 opcode, tAVRC_M } else if (opcode == AVRC_OP_PASS_THRU && p_msg->pass.p_pass_data != NULL) { p_data_src = p_msg->pass.p_pass_data; data_len = (UINT16) p_msg->pass.pass_len; + } else if (opcode == AVRC_OP_BROWSE && p_msg->browse.p_browse_data != NULL) { + APPL_TRACE_EVENT("bta_av_rc_msg_cback Browse Data"); + p_data_src = p_msg->browse.p_browse_data; + data_len = (UINT16) p_msg->browse.browse_len; } + /* Create a copy of the message */ tBTA_AV_RC_MSG *p_buf = (tBTA_AV_RC_MSG *)GKI_getbuf((UINT16)(sizeof(tBTA_AV_RC_MSG) + data_len)); @@ -289,6 +294,8 @@ static void bta_av_rc_msg_cback(UINT8 handle, UINT8 label, UINT8 opcode, tAVRC_M p_buf->msg.vendor.p_vendor_data = p_data_dst; else if (opcode == AVRC_OP_PASS_THRU) p_buf->msg.pass.p_pass_data = p_data_dst; + else if (opcode == AVRC_OP_BROWSE) + p_buf->msg.browse.p_browse_data = p_data_dst; } bta_sys_sendmsg(p_buf); } @@ -777,9 +784,11 @@ static tAVRC_STS bta_av_chk_notif_evt_id(tAVRC_MSG_VENDOR *p_vendor) tBTA_AV_EVT bta_av_proc_meta_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_msg, UINT8 *p_ctype) { tBTA_AV_EVT evt = BTA_AV_META_MSG_EVT; - UINT8 u8, pdu, *p; + UINT8 u8, pdu, *p, i; UINT16 u16; tAVRC_MSG_VENDOR *p_vendor = &p_msg->msg.vendor; + BD_ADDR addr; + BOOLEAN is_dev_avrcpv_blacklisted = FALSE; #if (AVRC_METADATA_INCLUDED == TRUE) @@ -840,8 +849,31 @@ tBTA_AV_EVT bta_av_proc_meta_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_ms { *p_ctype = AVRC_RSP_IMPL_STBL; p_rc_rsp->get_caps.count = p_bta_av_cfg->num_evt_ids; - memcpy(p_rc_rsp->get_caps.param.event_id, p_bta_av_cfg->p_meta_evt_ids, - p_bta_av_cfg->num_evt_ids); + /* DUT has blacklisted few remote dev for Avrcp Version hence + * respose for event supported should not have AVRCP 1.5/1.4 + * version events + */ + if (avct_get_peer_addr_by_ccb(p_msg->handle, addr) == TRUE) + { + is_dev_avrcpv_blacklisted = SDP_Dev_Blacklisted_For_Avrcp15(addr); + BTIF_TRACE_ERROR("Blacklist for AVRCP1.5 = %d", is_dev_avrcpv_blacklisted); + } + BTIF_TRACE_DEBUG("Blacklist for AVRCP1.5 = %d", is_dev_avrcpv_blacklisted); + if (is_dev_avrcpv_blacklisted == TRUE) + { + for (i = 0; i <= p_bta_av_cfg->num_evt_ids; ++i) + { + if (p_bta_av_cfg->p_meta_evt_ids[i] == AVRC_EVT_AVAL_PLAYERS_CHANGE) + break; + } + p_rc_rsp->get_caps.count = i; + memcpy(p_rc_rsp->get_caps.param.event_id, p_bta_av_cfg->p_meta_evt_ids, i); + } + else + { + memcpy(p_rc_rsp->get_caps.param.event_id, p_bta_av_cfg->p_meta_evt_ids, + p_bta_av_cfg->num_evt_ids); + } } else { @@ -872,6 +904,39 @@ tBTA_AV_EVT bta_av_proc_meta_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_ms return evt; } +/***************************************************************************** +** +** Function bta_av_proc_browse_cmd +** +** Description Process and AVRCP browse command from the peer +** +** Returns status +** +****************************************************************************/ +tBTA_AV_EVT bta_av_proc_browse_cmd(tAVRC_RESPONSE *p_rc_rsp, tBTA_AV_RC_MSG *p_msg) +{ + tBTA_AV_EVT evt = BTA_AV_BROWSE_MSG_EVT; + UINT8 u8, pdu, *p; + UINT16 u16; + tAVRC_MSG_BROWSE *p_browse = &p_msg->msg.browse; + + pdu = p_browse->p_browse_data[0]; + APPL_TRACE_DEBUG("bta_av_proc_browse_cmd browse cmd: %x", pdu); + switch (pdu) + { + case AVRC_PDU_GET_FOLDER_ITEMS: + case AVRC_PDU_SET_BROWSED_PLAYER: + case AVRC_PDU_CHANGE_PATH: + case AVRC_PDU_GET_ITEM_ATTRIBUTES: + break; + default: + evt = 0; + p_rc_rsp->rsp.status = AVRC_STS_BAD_CMD; + APPL_TRACE_ERROR("### Not supported cmd: %x", pdu); + break; + } + return evt; +} /******************************************************************************* ** @@ -895,6 +960,7 @@ void bta_av_rc_msg(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) rc_rsp.rsp.status = BTA_AV_STS_NO_RSP; #endif + APPL_TRACE_DEBUG("bta_av_rc_msg opcode: %x",p_data->rc_msg.opcode); if (p_data->rc_msg.opcode == AVRC_OP_PASS_THRU) { @@ -939,7 +1005,7 @@ void bta_av_rc_msg(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) } } /* else if this is a pass thru response */ - else if (p_data->rc_msg.msg.hdr.ctype >= AVRC_RSP_ACCEPT) + else if (p_data->rc_msg.msg.hdr.ctype >= AVRC_RSP_NOT_IMPL) { /* set up for callback */ evt = BTA_AV_REMOTE_RSP_EVT; @@ -1011,6 +1077,25 @@ void bta_av_rc_msg(tBTA_AV_CB *p_cb, tBTA_AV_DATA *p_data) AVRC_VendorRsp(p_data->rc_msg.handle, p_data->rc_msg.label, &p_data->rc_msg.msg.vendor); } } +#if (AVCT_BROWSE_INCLUDED == TRUE) + else if (p_data->rc_msg.opcode == AVRC_OP_BROWSE ) + { + APPL_TRACE_DEBUG("browse len PDU :%x",p_data->rc_msg.msg.browse.browse_len); + APPL_TRACE_DEBUG("browse data:%x",p_data->rc_msg.msg.browse.p_browse_data[0]); + av.browse_msg.label = p_data->rc_msg.label; + av.browse_msg.p_msg = &p_data->rc_msg.msg; + + evt = bta_av_proc_browse_cmd(&rc_rsp, &p_data->rc_msg); + if (evt == 0) + { + APPL_TRACE_ERROR("Browse PDU not supported"); + rc_rsp.rsp.pdu = AVRC_PDU_GENERAL_REJECT; + rc_rsp.rsp.status = AVRC_STS_BAD_CMD; + ctype = 0; + AVRC_BldBrowseResponse(0, &rc_rsp, &p_pkt); + } + } +#endif #if (AVRC_METADATA_INCLUDED == TRUE) if (evt == 0 && rc_rsp.rsp.status != BTA_AV_STS_NO_RSP) { diff --git a/bta/av/bta_av_cfg.c b/bta/av/bta_av_cfg.c index 8768934b5..6e9a7b4de 100644 --- a/bta/av/bta_av_cfg.c +++ b/bta/av/bta_av_cfg.c @@ -65,21 +65,32 @@ const UINT16 bta_av_audio_flush_to[] = { /* Note: Android doesnt support AVRC_SUPF_TG_GROUP_NAVI */ /* Note: if AVRC_SUPF_TG_GROUP_NAVI is set, bta_av_cfg.avrc_group should be TRUE */ #if AVRC_METADATA_INCLUDED == TRUE -#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT1) /* TODO: | AVRC_SUPF_TG_APP_SETTINGS) */ + +#if AVCT_BROWSE_INCLUDED == TRUE +#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT1 | AVRC_SUPF_TG_BROWSE | AVRC_SUPF_TG_APP_SETTINGS) +#else +#define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT1 | AVRC_SUPF_TG_APP_SETTINGS) +#endif + #else #define BTA_AV_RC_SUPF_TG (AVRC_SUPF_TG_CAT1) #endif /* * If the number of event IDs is changed in this array, BTA_AV_ NUM_RC_EVT_IDS also needs to be changed. + * AVRCP 1.3 specific events to be added before AVCT_BROWSE_INCLUDED. */ const UINT8 bta_av_meta_caps_evt_ids[] = { AVRC_EVT_PLAY_STATUS_CHANGE, AVRC_EVT_TRACK_CHANGE, AVRC_EVT_PLAY_POS_CHANGED, - /* TODO: Add support for these events AVRC_EVT_APP_SETTING_CHANGE, - */ +#if AVCT_BROWSE_INCLUDED == TRUE + AVRC_EVT_NOW_PLAYING_CHANGE, + AVRC_EVT_AVAL_PLAYERS_CHANGE, + AVRC_EVT_ADDR_PLAYER_CHANGE, + AVRC_EVT_NOW_PLAYING_CHANGE, +#endif }; #ifndef BTA_AV_NUM_RC_EVT_IDS #define BTA_AV_NUM_RC_EVT_IDS (sizeof(bta_av_meta_caps_evt_ids) / sizeof(bta_av_meta_caps_evt_ids[0])) diff --git a/bta/include/bta_av_api.h b/bta/include/bta_av_api.h index a78796c81..c57c95844 100644 --- a/bta/include/bta_av_api.h +++ b/bta/include/bta_av_api.h @@ -249,10 +249,11 @@ typedef UINT8 tBTA_AV_ERR; #define BTA_AV_META_MSG_EVT 17 /* metadata messages */ #define BTA_AV_REJECT_EVT 18 /* incoming connection rejected */ #define BTA_AV_RC_FEAT_EVT 19 /* remote control channel peer supported features update */ -#define BTA_AV_MEDIA_SINK_CFG_EVT 20 /* command to configure codec */ -#define BTA_AV_MEDIA_DATA_EVT 21 /* sending data to Media Task */ +#define BTA_AV_BROWSE_MSG_EVT 20 /* Browse MSG EVT */ +#define BTA_AV_MEDIA_SINK_CFG_EVT 21 /* command to configure codec */ +#define BTA_AV_MEDIA_DATA_EVT 22 /* sending data to Media Task */ /* Max BTA event */ -#define BTA_AV_MAX_EVT 22 +#define BTA_AV_MAX_EVT 23 typedef UINT8 tBTA_AV_EVT; @@ -411,6 +412,16 @@ typedef struct tAVRC_MSG *p_msg; } tBTA_AV_META_MSG; +/*data associated with BTA_AV_BROWSE_MSG_EVT */ +typedef struct +{ + UINT8 rc_handle; + UINT16 len; + UINT8 label; + UINT8 *p_data; + tAVRC_MSG *p_msg; +}tBTA_AV_BROWSE_MSG; + /* data associated with BTA_AV_PENDING_EVT */ typedef struct { @@ -446,6 +457,7 @@ typedef union tBTA_AV_SUSPEND suspend; tBTA_AV_PEND pend; tBTA_AV_META_MSG meta_msg; + tBTA_AV_BROWSE_MSG browse_msg; tBTA_AV_REJECT reject; tBTA_AV_RC_FEAT rc_feat; } tBTA_AV; diff --git a/btif/src/btif_av.c b/btif/src/btif_av.c index ad357d878..c671b33c2 100644 --- a/btif/src/btif_av.c +++ b/btif/src/btif_av.c @@ -127,6 +127,7 @@ else\ case BTA_AV_REMOTE_CMD_EVT: \ case BTA_AV_VENDOR_CMD_EVT: \ case BTA_AV_META_MSG_EVT: \ + case BTA_AV_BROWSE_MSG_EVT: \ case BTA_AV_RC_FEAT_EVT: \ case BTA_AV_REMOTE_RSP_EVT: \ { \ @@ -358,6 +359,7 @@ static BOOLEAN btif_av_state_idle_handler(btif_sm_event_t event, void *p_data) case BTA_AV_META_MSG_EVT: case BTA_AV_RC_FEAT_EVT: case BTA_AV_REMOTE_RSP_EVT: + case BTA_AV_BROWSE_MSG_EVT: btif_rc_handler(event, (tBTA_AV*)p_data); break; @@ -1018,6 +1020,33 @@ void btif_av_event_deep_copy(UINT16 event, char *p_dest, char *p_src) } } break; + case BTA_AV_BROWSE_MSG_EVT: + if (av_src->browse_msg.p_data && av_src->browse_msg.len) + { + av_dest->browse_msg.p_data = osi_calloc(av_src->browse_msg.len); + assert(av_dest->browse_msg.p_data); + memcpy(av_dest->browse_msg.p_data, + av_src->browse_msg.p_data, av_src->browse_msg.len); + } + + if (av_src->browse_msg.p_msg) + { + av_dest->browse_msg.p_msg = osi_calloc(sizeof(tAVRC_MSG)); + assert(av_dest->browse_msg.p_msg); + memcpy(av_dest->browse_msg.p_msg, av_src->browse_msg.p_msg, sizeof(tAVRC_MSG)); + + if (av_src->browse_msg.p_msg->browse.p_browse_data && + av_src->browse_msg.p_msg->browse.browse_len) + { + av_dest->browse_msg.p_msg->browse.p_browse_data = osi_calloc( + av_src->browse_msg.p_msg->browse.browse_len); + assert(av_dest->browse_msg.p_msg->browse.p_browse_data); + memcpy(av_dest->browse_msg.p_msg->browse.p_browse_data, + av_src->browse_msg.p_msg->browse.p_browse_data, + av_src->browse_msg.p_msg->browse.browse_len); + } + } + break; default: break; @@ -1042,6 +1071,20 @@ static void btif_av_event_free_data(btif_sm_event_t event, void *p_data) } } break; + case BTA_AV_BROWSE_MSG_EVT: + { + tBTA_AV *av = (tBTA_AV*)p_data; + if (av->browse_msg.p_data) + osi_free(av->browse_msg.p_data); + + if (av->browse_msg.p_msg) + { + if (av->browse_msg.p_msg->browse.p_browse_data) + osi_free(av->browse_msg.p_msg->browse.p_browse_data); + osi_free(av->browse_msg.p_msg); + } + } + break; default: break; diff --git a/btif/src/btif_rc.c b/btif/src/btif_rc.c index 93be30f5a..3fefbbaf9 100644 --- a/btif/src/btif_rc.c +++ b/btif/src/btif_rc.c @@ -47,21 +47,31 @@ /* cod value for Headsets */ #define COD_AV_HEADSETS 0x0404 /* for AVRC 1.4 need to change this */ -#define MAX_RC_NOTIFICATIONS AVRC_EVT_APP_SETTING_CHANGE - -#define IDX_GET_PLAY_STATUS_RSP 0 -#define IDX_LIST_APP_ATTR_RSP 1 -#define IDX_LIST_APP_VALUE_RSP 2 -#define IDX_GET_CURR_APP_VAL_RSP 3 -#define IDX_SET_APP_VAL_RSP 4 -#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_RC_NOTIFICATIONS AVRC_EVT_VOLUME_CHANGE +//#define TEST_BROWSE_RESPONSE +#define MAX_FOLDER_RSP_SUPPORT 3 + +#define IDX_GET_PLAY_STATUS_RSP 0 +#define IDX_LIST_APP_ATTR_RSP 1 +#define IDX_LIST_APP_VALUE_RSP 2 +#define IDX_GET_CURR_APP_VAL_RSP 3 +#define IDX_SET_APP_VAL_RSP 4 +#define IDX_GET_APP_ATTR_TXT_RSP 5 +#define IDX_GET_APP_VAL_TXT_RSP 6 +#define IDX_GET_ELEMENT_ATTR_RSP 7 +#define IDX_GET_FOLDER_ITEMS_RSP 8 +#define IDX_SET_FOLDER_ITEM_RSP 9 +#define IDX_SET_ADDRESS_PLAYER_RSP 10 +#define IDX_SET_BROWSE_PLAYER_RSP 11 +#define IDX_CHANGE_PATH_RSP 12 +#define IDX_PLAY_ITEM_RSP 13 +#define IDX_GET_ITEM_ATTR_RSP 14 #define MAX_VOLUME 128 #define MAX_LABEL 16 #define MAX_TRANSACTIONS_PER_SESSION 16 -#define MAX_CMD_QUEUE_LEN 8 #define PLAY_STATUS_PLAYING 1 +#define MAX_CMD_QUEUE_LEN 15 +#define ERR_PLAYER_NOT_ADDRESED 0x13 #define CHECK_RC_CONNECTED \ BTIF_TRACE_DEBUG("## %s ##", __FUNCTION__); \ @@ -92,6 +102,21 @@ btif_rc_cb.rc_pdu_info[index].is_rsp_pending = FALSE; \ } + +#define SEND_BROWSEMSG_RSP(index , avrc_rsp) \ +{ \ + if(btif_rc_cb.rc_pdu_info[index].is_rsp_pending == FALSE) \ + { \ + BTIF_TRACE_WARNING("%s Not sending response as no PDU was registered", __FUNCTION__); \ + return BT_STATUS_UNHANDLED; \ + } \ + send_browsemsg_rsp(btif_rc_cb.rc_handle, btif_rc_cb.rc_pdu_info[index].label, \ + btif_rc_cb.rc_pdu_info[index].ctype, avrc_rsp); \ + btif_rc_cb.rc_pdu_info[index].ctype = 0; \ + btif_rc_cb.rc_pdu_info[index].label = 0; \ + btif_rc_cb.rc_pdu_info[index].is_rsp_pending = FALSE; \ +} + /***************************************************************************** ** Local type definitions ******************************************************************************/ @@ -206,6 +231,12 @@ 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 bt_status_t set_addrplayer_rsp(btrc_status_t status_code); + +/*Added for Browsing Message Response */ +static void send_browsemsg_rsp (UINT8 rc_handle, UINT8 label, + tBTA_AV_CODE code, tAVRC_RESPONSE *pmetamsg_resp); + /***************************************************************************** ** Static variables @@ -223,7 +254,7 @@ static btrc_ctrl_callbacks_t *bt_rc_ctrl_callbacks = NULL; ******************************************************************************/ extern BOOLEAN btif_hf_call_terminated_recently(); extern BOOLEAN check_cod(const bt_bdaddr_t *remote_bdaddr, uint32_t cod); - +extern BOOLEAN btif_hf_is_call_idle(); /***************************************************************************** ** Functions @@ -801,6 +832,277 @@ void handle_rc_metamsg_cmd (tBTA_AV_META_MSG *pmeta_msg) } } +/************************************************************************************ +* Function handle_get_folder_item_mediaplyerlist_cmd +* +* - Argument: tBTA_AV_BROWSE_MSG structure containing the received +* browse message +* tAVRC_COMMAND structure containing the commands +* to be updated +* UINT8 event, variable having value of event. +* +* - Description: Handler for get media player list command +* +************************************************************************************/ +UINT8 handle_get_folder_item_mediaplyerlist_cmd (tBTA_AV_BROWSE_MSG *pbrowse_msg, + tAVRC_COMMAND *cmd, UINT8 *event) +{ + UINT8 *p_length, *start_item, *end_item; + UINT8 length, xx; + *event = pbrowse_msg->p_msg->browse.p_browse_data[0] ; + cmd->get_items.pdu = *event; + //Check length + p_length = &pbrowse_msg->p_msg->browse.p_browse_data[1]; + BE_STREAM_TO_UINT16(length, p_length); + if (length != 10) //Refer to spec + { + BTIF_TRACE_ERROR("GET_FOLDER_ITEMS: length error: =%d",length); + return TRUE; + } + else + { + start_item = &pbrowse_msg->p_msg->browse.p_browse_data[4]; + BE_STREAM_TO_UINT32(cmd->get_items.start_item ,start_item); + BTIF_TRACE_EVENT("pbrowse_msg start_item :%x",cmd->get_items.start_item); + end_item = &pbrowse_msg->p_msg->browse.p_browse_data[8]; + BE_STREAM_TO_UINT32(cmd->get_items.end_item,end_item); + BTIF_TRACE_EVENT("pbrowse_msg start_item :%x",cmd->get_items.end_item); + cmd->get_items.attr_count = 0xff; /* in MediaPlayerList we don't have attr_id */ + //Update OPCODE + cmd->get_items.opcode = AVRC_OP_BROWSE; + cmd->get_items.scope = pbrowse_msg->p_msg->browse.p_browse_data[3] ; + cmd->get_items.status = AVRC_STS_NO_ERROR ; + for (xx = 0; xx < BTRC_MAX_ELEM_ATTR_SIZE ; xx++) + { + cmd->get_items.attrs[xx] = 0; + } + return FALSE; + } +} + +/************************************************************************************ +* Function handle_get_folder_item_filesystem_cmd +* +* - Argument: tBTA_AV_BROWSE_MSG structure containing the received +* browse message +* tAVRC_COMMAND structure containing the commands +* to be updated +* UINT8 event, variable having value of event. +* +* - Description: Handler for get folder item command +* +************************************************************************************/ +UINT8 handle_get_folder_item_filesystem_cmd (tBTA_AV_BROWSE_MSG *pbrowse_msg, tAVRC_COMMAND *cmd, + UINT8 *event) +{ + UINT8 *p_length, *start_item, *end_item, *p_data; + UINT8 length, attr_count = 0, xx; + *event = pbrowse_msg->p_msg->browse.p_browse_data[0] ; + cmd->get_items.pdu = *event; + //Check length + p_length = &pbrowse_msg->p_msg->browse.p_browse_data[1]; + BE_STREAM_TO_UINT16(length, p_length); + attr_count = pbrowse_msg->p_msg->browse.p_browse_data[12]; + BTIF_TRACE_ERROR("GET_FOLDER_ITEMS: attr_count: =%d", attr_count); + switch (attr_count) + { + case 0xff: + if (length != 10) + { + BTIF_TRACE_ERROR("GET_FOLDER_ITEMS: length error: =%d", length); + return TRUE; + } + break; + default: + if (length != ((attr_count * 4) + 10)) + { + BTIF_TRACE_ERROR("GET_FOLDER_ITEMS: length error: =%d", length); + return TRUE; + } + } + + start_item = &pbrowse_msg->p_msg->browse.p_browse_data[4]; + BE_STREAM_TO_UINT32(cmd->get_items.start_item ,start_item); + BTIF_TRACE_EVENT("pbrowse_msg start_item :%x",cmd->get_items.start_item); + end_item = &pbrowse_msg->p_msg->browse.p_browse_data[8]; + BE_STREAM_TO_UINT32(cmd->get_items.end_item,end_item); + BTIF_TRACE_EVENT("pbrowse_msg start_item :%x",cmd->get_items.end_item); + cmd->get_items.attr_count = attr_count; + if (attr_count == 0) + { + for (xx = 0; xx < BTRC_MAX_ELEM_ATTR_SIZE; xx++) + { + cmd->get_items.attrs[xx] = xx + 1; + } + } + else if (attr_count == 0xff) /* no attribute requested */ + { + BTIF_TRACE_DEBUG("No attribute requested"); + } + else + { + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[13]; + for (xx = 0; xx < attr_count; xx++) + BE_STREAM_TO_UINT32(cmd->get_items.attrs[xx], p_data) + } + //Update OPCODE + cmd->get_items.opcode = AVRC_OP_BROWSE; + cmd->get_items.scope = pbrowse_msg->p_msg->browse.p_browse_data[3] ; + cmd->get_items.status = AVRC_STS_NO_ERROR ; + return FALSE; +} + +/************************************************************************************ +* Function handle_rc_browsemsg_cmd +* +* - Argument: tBTA_AV_BROWSE_MSG structure containing the recieved +* browse message +* +* - Description: Remote Control browse message handler +* +************************************************************************************/ +void handle_rc_browsemsg_cmd (tBTA_AV_BROWSE_MSG *pbrowse_msg) +{ + UINT8 event; + UINT16 length; + tAVRC_COMMAND cmd; + UINT8 *start_item, *p_length, *p_data; + UINT8 *end_item; + tAVRC_RESPONSE avrc_rsp; + UINT8 dropmsg = TRUE; + + BTIF_TRACE_EVENT("+ %s", __FUNCTION__); + BTIF_TRACE_EVENT("pbrowse_msg PDU_ID :%x",pbrowse_msg->p_msg->browse.p_browse_data[0]); + BTIF_TRACE_EVENT("pbrowse_msg length :%x",pbrowse_msg->p_msg->browse.browse_len); + switch(pbrowse_msg->p_msg->browse.p_browse_data[0]) + { + case AVRC_PDU_GET_FOLDER_ITEMS: + { + UINT8 scope = pbrowse_msg->p_msg->browse.p_browse_data[3]; + switch (scope) + { + case AVRC_SCOPE_PLAYER_LIST: + dropmsg = handle_get_folder_item_mediaplyerlist_cmd(pbrowse_msg, &cmd, &event); + break; + case AVRC_SCOPE_FILE_SYSTEM: + case AVRC_SCOPE_NOW_PLAYING: + dropmsg = handle_get_folder_item_filesystem_cmd(pbrowse_msg, &cmd, &event); + break; + } + if (dropmsg == FALSE) + { + btif_rc_upstreams_evt(event,&cmd,0,pbrowse_msg->label); + } + } + break; + + case AVRC_PDU_SET_BROWSED_PLAYER: + event = pbrowse_msg->p_msg->browse.p_browse_data[0] ; + cmd.br_player.pdu = event; + //Check for length + p_length = &pbrowse_msg->p_msg->browse.p_browse_data[1]; + BE_STREAM_TO_UINT16(length, p_length); + if (length != 0x0002) + { + BTIF_TRACE_ERROR("SET_BROWSED_PLAYERlength error: = %d", length); + } + else + { + p_length = &pbrowse_msg->p_msg->browse.p_browse_data[3]; + BE_STREAM_TO_UINT16(cmd.br_player.player_id, p_length); + cmd.br_player.opcode = AVRC_OP_BROWSE; + btif_rc_upstreams_evt(event, &cmd, 0, pbrowse_msg->label); + dropmsg = FALSE; + } + break; + + case AVRC_PDU_CHANGE_PATH: + event = pbrowse_msg->p_msg->browse.p_browse_data[0] ; + cmd.chg_path.pdu = event; + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[1]; + BE_STREAM_TO_UINT16(length, p_data); + if (length != 11) + { + BTIF_TRACE_ERROR("CHANGE_PATH length error: = %d",length); + } + else + { + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[3]; + BE_STREAM_TO_UINT16(cmd.chg_path.uid_counter, p_data); + cmd.chg_path.direction = pbrowse_msg->p_msg->browse.p_browse_data[5]; + cmd.chg_path.opcode = AVRC_OP_BROWSE; + cmd.chg_path.status = AVRC_STS_NO_ERROR; + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[6]; + BE_STREAM_TO_UINT64(cmd.chg_path.folder_uid, p_data); + btif_rc_upstreams_evt(event, &cmd, 0, pbrowse_msg->label); + dropmsg = FALSE; + } + break; + + case AVRC_PDU_GET_ITEM_ATTRIBUTES: + { + UINT16 packet_len; + UINT8 num_attr, index; + event = pbrowse_msg->p_msg->browse.p_browse_data[0] ; + cmd.get_attrs.pdu = event; + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[1]; + BE_STREAM_TO_UINT16(packet_len, p_data); + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[14]; + BE_STREAM_TO_UINT8(num_attr, p_data); + if (packet_len != ((num_attr * 4) + 12)) + { + BTIF_TRACE_ERROR("Get Item Attributes length error: = %d",packet_len); + } + else + { + cmd.get_attrs.status = AVRC_STS_NO_ERROR; + cmd.get_attrs.opcode = AVRC_OP_BROWSE; + cmd.get_attrs.scope = pbrowse_msg->p_msg->browse.p_browse_data[3]; + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[4]; + BE_STREAM_TO_UINT64(cmd.get_attrs.uid, p_data); + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[12]; + BE_STREAM_TO_UINT16(cmd.get_attrs.uid_counter, p_data); + cmd.get_attrs.attr_count = num_attr; + if (num_attr == 0) + { + /* remote requested all Attribute ID*/ + for (index = 0; index < BTRC_MAX_ELEM_ATTR_SIZE; index++) + { + cmd.get_attrs.attrs[index] = index + 1; + } + } + else + { + p_data = &pbrowse_msg->p_msg->browse.p_browse_data[15]; + BTIF_TRACE_ERROR("GetItemAttr num_attr: = %d", cmd.get_attrs.attr_count); + for (index = 0; index < num_attr ; index++) + { + BE_STREAM_TO_UINT32(cmd.get_attrs.attrs[index], p_data); + BTIF_TRACE_ERROR("GetItemAttr attrid: = %d", cmd.get_attrs.attrs[index]); + } + } + btif_rc_upstreams_evt(event, &cmd, 0, pbrowse_msg->label); + dropmsg = FALSE; + } + } + break; + + default: + BTIF_TRACE_ERROR("pbrowse_msg ERROR"); + break; + } + if (dropmsg == TRUE) + { + avrc_rsp.rsp.pdu = pbrowse_msg->p_msg->browse.p_browse_data[0]; + avrc_rsp.rsp.status = AVRC_STS_BAD_CMD; + avrc_rsp.rsp.opcode = AVRC_OP_BROWSE; + BTIF_TRACE_ERROR("handle_rc_browsemsg_cmd: pbrowse_msg ERROR: %x", avrc_rsp.rsp.pdu); + send_browsemsg_rsp(btif_rc_cb.rc_handle, pbrowse_msg->label, 0, &avrc_rsp); + } + +} + + /*************************************************************************** ** ** Function btif_rc_handler @@ -886,6 +1188,13 @@ void btif_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data) } } break; + case BTA_AV_BROWSE_MSG_EVT: + { + BTIF_TRACE_DEBUG("BTA_AV_BROWSE_MSG_EVT label:%d handle:%d", p_data->browse_msg.label, + p_data->browse_msg.rc_handle); + handle_rc_browsemsg_cmd(&(p_data->browse_msg)); + } + break; default: BTIF_TRACE_DEBUG("Unhandled RC event : 0x%x", event); } @@ -1116,6 +1425,68 @@ static UINT8 opcode_from_pdu(UINT8 pdu) return opcode; } +/**************************************************************************** +* Function send_browsemsg_rsp +* +* Arguments - rc_handle RC handle corresponding to the connected RC +* label Label of the RC response +* code Response type---->Not needed for Browsing +* pmetamsg_resp Vendor response +* +* Description - Remote control browse Message Rsp +* +*******************************************************************************/ +static void send_browsemsg_rsp (UINT8 rc_handle, UINT8 label, tBTA_AV_CODE code, + tAVRC_RESPONSE *pbrowsemsg_resp) +{ + tAVRC_STS status; + BT_HDR *p_msg = NULL; + + if (!pbrowsemsg_resp) + { + BTIF_TRACE_WARNING("%s: Invalid response received from application", __FUNCTION__); + return; + } + + BTIF_TRACE_EVENT("+%s:rc_handle: %d, label: %d, code: 0x%02x, pdu: %s", __FUNCTION__,\ + rc_handle, label, code, dump_rc_pdu(pbrowsemsg_resp->rsp.pdu)); + if (pbrowsemsg_resp->rsp.status != AVRC_STS_NO_ERROR) + { + BTIF_TRACE_ERROR("send_browsemsg_rsp **Error**"); + } + /*Browse Command and Response structure are different + *as comapared to Meta data response ,opcode and c-type + *not part of browse response hence handling browse response + *in seprate function + */ + status = AVRC_BldBrowseResponse(rc_handle, pbrowsemsg_resp, &p_msg); + if (status == AVRC_STS_NO_ERROR) + { + BTA_AvMetaRsp(rc_handle, label, 0, p_msg); + } + else + { + BTIF_TRACE_ERROR("%s: failed to build metamsg response. status: 0x%02x", + __FUNCTION__, status); + } +} + +/**************************************************************************** +* Function app_sendbrowsemsg +* +* Arguments - index Of array stored while recieving command +* avrc_rsp Avrcp response from application +* +* Description - Send Browse message +* +*******************************************************************************/ +int app_sendbrowsemsg(UINT8 index ,tAVRC_RESPONSE *avrc_rsp) +{ + SEND_BROWSEMSG_RSP(index ,avrc_rsp); + return 0; +} + + /******************************************************************************* ** ** Function btif_rc_upstreams_evt @@ -1134,19 +1505,117 @@ static void btif_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 { case AVRC_PDU_GET_PLAY_STATUS: { + BTIF_TRACE_DEBUG("AVRC_PDU_GET_PLAY_STATUS "); FILL_PDU_QUEUE(IDX_GET_PLAY_STATUS_RSP, ctype, label, TRUE) HAL_CBACK(bt_rc_callbacks, get_play_status_cb); } break; case AVRC_PDU_LIST_PLAYER_APP_ATTR: + { + BTIF_TRACE_DEBUG("AVRC_PDU_LIST_PLAYER_APP_ATTR "); + FILL_PDU_QUEUE(IDX_LIST_APP_ATTR_RSP, ctype, label, TRUE) + HAL_CBACK(bt_rc_callbacks, list_player_app_attr_cb); + } + break; case AVRC_PDU_LIST_PLAYER_APP_VALUES: + { + BTIF_TRACE_DEBUG("AVRC_PDU_LIST_PLAYER_APP_VALUES =%d" ,pavrc_cmd->list_app_values.attr_id); + if (pavrc_cmd->list_app_values.attr_id == 0) + { + send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, AVRC_STS_BAD_PARAM); + break; + } + FILL_PDU_QUEUE(IDX_LIST_APP_VALUE_RSP, ctype, label, TRUE) + HAL_CBACK(bt_rc_callbacks, list_player_app_values_cb ,pavrc_cmd->list_app_values.attr_id); + } + break; case AVRC_PDU_GET_CUR_PLAYER_APP_VALUE: + { + btrc_player_attr_t player_attr[BTRC_MAX_ELEM_ATTR_SIZE]; + UINT8 player_attr_num; + BTIF_TRACE_DEBUG("PLAYER_APP_VALUE PDU 0x13 = %d",pavrc_cmd->get_cur_app_val.num_attr); + if ((pavrc_cmd->get_cur_app_val.num_attr == 0) || + (pavrc_cmd->get_cur_app_val.num_attr > BTRC_MAX_ELEM_ATTR_SIZE)) + { + send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, AVRC_STS_BAD_PARAM); + break; + } + memset( player_attr, 0, sizeof(player_attr)); + for (player_attr_num = 0 ; player_attr_num < pavrc_cmd->get_cur_app_val.num_attr; + ++player_attr_num) + { + player_attr[player_attr_num] = pavrc_cmd->get_cur_app_val.attrs[player_attr_num]; + } + FILL_PDU_QUEUE(IDX_GET_CURR_APP_VAL_RSP, ctype, label, TRUE) + HAL_CBACK(bt_rc_callbacks, get_player_app_value_cb , + pavrc_cmd->get_cur_app_val.num_attr, player_attr ); + } + break; case AVRC_PDU_SET_PLAYER_APP_VALUE: + { + btrc_player_settings_t attr; + UINT8 count; + tAVRC_RESPONSE avrc_rsp; + if ((pavrc_cmd->set_app_val.num_val== 0) || + (pavrc_cmd->set_app_val.num_val > BTRC_MAX_ELEM_ATTR_SIZE)) + { + send_reject_response (btif_rc_cb.rc_handle, label, + pavrc_cmd->pdu, AVRC_STS_BAD_PARAM); + break; + } + else + { + for(count = 0; count < pavrc_cmd->set_app_val.num_val ; ++count) + { + attr.attr_ids[count] = pavrc_cmd->set_app_val.p_vals[count].attr_id ; + attr.attr_values[count]= pavrc_cmd->set_app_val.p_vals[count].attr_val; + } + attr.num_attr = pavrc_cmd->set_app_val.num_val ; + FILL_PDU_QUEUE(IDX_SET_APP_VAL_RSP, ctype, label, TRUE) + HAL_CBACK(bt_rc_callbacks, set_player_app_value_cb, &attr ); + } + } + break; case AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT: + { + btrc_player_attr_t player_attr_txt [BTRC_MAX_ELEM_ATTR_SIZE]; + UINT8 count_txt = 0 ; + if ((pavrc_cmd->get_app_attr_txt.num_attr == 0) || + (pavrc_cmd->get_app_attr_txt.num_attr > BTRC_MAX_ELEM_ATTR_SIZE)) + { + send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, AVRC_STS_BAD_PARAM); + } + else + { + for (count_txt = 0;count_txt < pavrc_cmd->get_app_attr_txt.num_attr ; ++count_txt) + { + player_attr_txt[count_txt] = pavrc_cmd->get_app_attr_txt.attrs[count_txt]; + } + FILL_PDU_QUEUE(IDX_GET_APP_ATTR_TXT_RSP, ctype, label, TRUE) + HAL_CBACK(bt_rc_callbacks, get_player_app_attrs_text_cb, + pavrc_cmd->get_app_attr_txt.num_attr, player_attr_txt ); + } + } + break; case AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT: { - /* TODO: Add support for Application Settings */ - send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, AVRC_STS_BAD_CMD); + if (pavrc_cmd->get_app_val_txt.attr_id == 0 || + pavrc_cmd->get_app_val_txt.attr_id > AVRC_PLAYER_VAL_GROUP_REPEAT) + { + send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, AVRC_STS_BAD_PARAM); + break; + } + if (pavrc_cmd->get_app_val_txt.num_val == 0) + { + send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, AVRC_STS_BAD_PARAM); + } + else + { + FILL_PDU_QUEUE(IDX_GET_APP_VAL_TXT_RSP, ctype, label, TRUE) + HAL_CBACK(bt_rc_callbacks, get_player_app_values_text_cb, + pavrc_cmd->get_app_val_txt.attr_id, pavrc_cmd->get_app_val_txt.num_val, + pavrc_cmd->get_app_val_txt.vals); + } } break; case AVRC_PDU_GET_ELEMENT_ATTR: @@ -1207,7 +1676,7 @@ static void btif_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 break; case AVRC_PDU_REGISTER_NOTIFICATION: { - if(pavrc_cmd->reg_notif.event_id == BTRC_EVT_PLAY_POS_CHANGED && + if (pavrc_cmd->reg_notif.event_id == BTRC_EVT_PLAY_POS_CHANGED && pavrc_cmd->reg_notif.param == 0) { BTIF_TRACE_WARNING("%s Device registering position changed with illegal param 0.", @@ -1235,11 +1704,158 @@ static void btif_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 } } break; + case AVRC_PDU_SET_ADDRESSED_PLAYER: + { + btrc_status_t status_code = AVRC_STS_NO_ERROR; + BTIF_TRACE_EVENT("%s() AVRC_PDU_SET_ADDRESSED_PLAYER", __FUNCTION__); + if (!btif_hf_is_call_idle()) + { + set_addrplayer_rsp(ERR_PLAYER_NOT_ADDRESED); // send reject if call is in progress + return; + } + if (btif_rc_cb.rc_connected == TRUE) + { + FILL_PDU_QUEUE(IDX_SET_ADDRESS_PLAYER_RSP, ctype, label, TRUE); + HAL_CBACK(bt_rc_callbacks, set_addrplayer_cb, pavrc_cmd->addr_player.player_id); + } + } + break; + case AVRC_PDU_GET_FOLDER_ITEMS: + { + tAVRC_RESPONSE avrc_rsp; + btrc_getfolderitem_t getfolder; + btrc_browse_folderitem_t scope; + UINT8 player[] = "MusicPlayer1"; + tAVRC_ITEM tem[1]; + UINT8 index, numAttr; + BTIF_TRACE_EVENT("%s()AVRC_PDU_GET_FOLDER_ITEMS", __FUNCTION__); + FILL_PDU_QUEUE(IDX_GET_FOLDER_ITEMS_RSP,ctype, label, TRUE); + BTIF_TRACE_EVENT("rc_connected: %d",btif_rc_cb.rc_connected); + if (btif_rc_cb.rc_connected == TRUE) + { + getfolder.start_item = pavrc_cmd->get_items.start_item; + getfolder.end_item = pavrc_cmd->get_items.end_item; + getfolder.size = AVCT_GetBrowseMtu(btif_rc_cb.rc_handle); + getfolder.attr_count = pavrc_cmd->get_items.attr_count; + scope = (btrc_browse_folderitem_t)pavrc_cmd->get_items.scope; + if (getfolder.attr_count == 255) + { + numAttr = 0; + } + else + { + if (getfolder.attr_count == 0) + { + numAttr = 7; + for (index = 0; index < BTRC_MAX_ELEM_ATTR_SIZE; index++) + { + getfolder.attrs[index] = index + 1; + } + } + else + { + numAttr = getfolder.attr_count; + for (index = 0; index < numAttr; index++) + { + getfolder.attrs[index] = pavrc_cmd->get_items.attrs[index]; + BTIF_TRACE_ERROR("getfolder[%d] = %d", index, getfolder.\ + attrs[index]); + BTIF_TRACE_ERROR("pavrc_cmd->get_items.attrs[%d] = %d",\ + index, pavrc_cmd->get_items.attrs[index]); + } + } + } + HAL_CBACK(bt_rc_callbacks, get_folderitems_cb, scope, &getfolder); + } + } + break; + case AVRC_PDU_SET_BROWSED_PLAYER: + { + BTIF_TRACE_EVENT("%s() AVRC_PDU_SET_BROWSED_PLAYER", __FUNCTION__); + if (btif_rc_cb.rc_connected == TRUE) + { + FILL_PDU_QUEUE(IDX_SET_BROWSE_PLAYER_RSP, ctype, label, TRUE); + HAL_CBACK(bt_rc_callbacks, set_browsed_player_cb, pavrc_cmd->br_player.player_id); + } + } + break; + case AVRC_PDU_CHANGE_PATH: + { + BTIF_TRACE_EVENT("%s() AVRC_PDU_CHANGE_PATH", __FUNCTION__); + if (btif_rc_cb.rc_connected == TRUE) + { + FILL_PDU_QUEUE(IDX_CHANGE_PATH_RSP, ctype, label, TRUE); + HAL_CBACK(bt_rc_callbacks, change_path_cb, pavrc_cmd->chg_path.direction, \ + pavrc_cmd->chg_path.folder_uid); + } + } + break; + case AVRC_PDU_GET_ITEM_ATTRIBUTES: + { + UINT8 num_attr = pavrc_cmd->get_attrs.attr_count; + UINT8 index, num_attr_requested = 0; + BTIF_TRACE_EVENT("%s() AVRC_PDU_GET_ITEM_ATTRIBUTES", __FUNCTION__); + btrc_media_attr_t element_attrs[BTRC_MAX_ELEM_ATTR_SIZE]; + memset(&element_attrs, 0, sizeof(element_attrs)); + if (num_attr == 0) + { + /* CT requests for all attributes */ + for (index = 0; index < BTRC_MAX_ELEM_ATTR_SIZE; index++) + { + element_attrs[index] = index + 1; + } + num_attr_requested = 7; /* get all seven */ + } + else if (num_attr == 0xFF) + { + /* 0xff indicates, no attributes requested - reject */ + send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, + AVRC_STS_BAD_PARAM); + return; + } + else + { + /* Attribute IDs from 1 to BTRC_MAX_ELEM_ATTR_SIZE are only valid, + * hence HAL definition limits the attributes to BTRC_MAX_ELEM_ATTR_SIZE. + * Fill only valid entries. + */ + for (index = 0; (index < num_attr) && (num_attr <= BTRC_MAX_ELEM_ATTR_SIZE); index++) + { + if ((pavrc_cmd->get_attrs.attrs[index] > 0) && + (pavrc_cmd->get_attrs.attrs[index] <= BTRC_MAX_ELEM_ATTR_SIZE)) + { + element_attrs[index] = pavrc_cmd->get_attrs.attrs[index]; + BTIF_TRACE_ERROR("element_attrs[%d]: %d", index, element_attrs[index]); + } + } + num_attr_requested = index; + BTIF_TRACE_ERROR("num_attr_requested: %d", num_attr_requested); + } + + if (btif_rc_cb.rc_connected == TRUE) + { + FILL_PDU_QUEUE(IDX_GET_ITEM_ATTR_RSP, ctype, label, TRUE); + HAL_CBACK(bt_rc_callbacks, get_item_attr_cb, pavrc_cmd->get_attrs.scope, + pavrc_cmd->get_attrs.uid, num_attr_requested, element_attrs); + } + } + break; + case AVRC_PDU_PLAY_ITEM: + { + BTIF_TRACE_EVENT("%s() AVRC_PDU_PLAY_ITEM", __FUNCTION__); + if (btif_rc_cb.rc_connected == TRUE) + { + FILL_PDU_QUEUE(IDX_PLAY_ITEM_RSP, ctype, label, TRUE); + HAL_CBACK(bt_rc_callbacks, play_item_cb, pavrc_cmd->play_item.scope, + pavrc_cmd->play_item.uid); + } + } + break; default: { - send_reject_response (btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, - (pavrc_cmd->pdu == AVRC_PDU_SEARCH)?AVRC_STS_SEARCH_NOT_SUP:AVRC_STS_BAD_CMD); - return; + send_reject_response(btif_rc_cb.rc_handle, label, pavrc_cmd->pdu, + (pavrc_cmd->pdu == AVRC_PDU_SEARCH)? AVRC_STS_SEARCH_NOT_SUP: + AVRC_STS_BAD_CMD); } break; } @@ -1373,12 +1989,239 @@ static bt_status_t get_play_status_rsp(btrc_play_status_t play_status, uint32_t return BT_STATUS_SUCCESS; } + +/************************************************************************** +** +** Function list_player_app_attr_rsp +** +** Description ListPlayerApplicationSettingAttributes (PDU ID: 0x11) +** This method is callled in response to PDU 0x11 +** +** Returns bt_status_t +** +****************************************************************************/ +static bt_status_t list_player_app_attr_rsp( uint8_t num_attr, btrc_player_attr_t *p_attrs) +{ + tAVRC_RESPONSE avrc_rsp; + UINT32 i; + + CHECK_RC_CONNECTED + memset(&(avrc_rsp.list_app_attr), 0, sizeof(tAVRC_LIST_APP_ATTR_RSP)); + if (num_attr == 0) + { + avrc_rsp.list_app_attr.status = AVRC_STS_BAD_PARAM; + } + else + { + avrc_rsp.list_app_attr.num_attr = num_attr; + for (i = 0 ; i < num_attr ; ++i) + { + avrc_rsp.list_app_attr.attrs[i] = p_attrs[i]; + } + avrc_rsp.list_app_attr.status = AVRC_STS_NO_ERROR; + } + avrc_rsp.list_app_attr.pdu = AVRC_PDU_LIST_PLAYER_APP_ATTR ; + avrc_rsp.list_app_attr.opcode = opcode_from_pdu(AVRC_PDU_LIST_PLAYER_APP_ATTR); + /* Send the response */ + SEND_METAMSG_RSP(IDX_LIST_APP_ATTR_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/********************************************************************** +** +** Function list_player_app_value_rsp +** +** Description ListPlayerApplicationSettingValues (PDU ID: 0x12) + This method is called in response to PDU 0x12 +************************************************************************/ +static bt_status_t list_player_app_value_rsp( uint8_t num_val, uint8_t *value) +{ + tAVRC_RESPONSE avrc_rsp; + UINT32 i; + + CHECK_RC_CONNECTED + memset(&(avrc_rsp.list_app_values), 0, sizeof(tAVRC_LIST_APP_VALUES_RSP)); + if ((num_val == 0) || (num_val > AVRC_MAX_APP_ATTR_SIZE)) + { + avrc_rsp.list_app_values.status = AVRC_STS_BAD_PARAM; + } + else + { + avrc_rsp.list_app_values.num_val = num_val; + for (i = 0; i < num_val; ++i) + { + avrc_rsp.list_app_values.vals[i] = value[i]; + } + avrc_rsp.list_app_values.status = AVRC_STS_NO_ERROR; + } + avrc_rsp.list_app_values.pdu = AVRC_PDU_LIST_PLAYER_APP_VALUES; + avrc_rsp.list_app_attr.opcode = opcode_from_pdu(AVRC_PDU_LIST_PLAYER_APP_VALUES); + /* Send the response */ + SEND_METAMSG_RSP(IDX_LIST_APP_VALUE_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + + +/********************************************************************** +** +** Function get_player_app_value_rsp +** +** Description This methos is called in response to PDU ID 0x13 +** +***********************************************************************/ +static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals) +{ + tAVRC_RESPONSE avrc_rsp; + UINT32 i; + tAVRC_APP_SETTING app_sett[AVRC_MAX_APP_ATTR_SIZE]; + + CHECK_RC_CONNECTED + memset(&(avrc_rsp.get_cur_app_val) ,0 , sizeof(tAVRC_GET_CUR_APP_VALUE_RSP)); + avrc_rsp.get_cur_app_val.p_vals = app_sett ; + //Check for Error Condition + if ((p_vals == NULL) || (p_vals->num_attr== 0) || (p_vals->num_attr > AVRC_MAX_APP_ATTR_SIZE)) + { + avrc_rsp.get_cur_app_val.status = AVRC_STS_BAD_PARAM; + } + else + { + memset(app_sett, 0, sizeof(tAVRC_APP_SETTING)*p_vals->num_attr ); + //update num_val + avrc_rsp.get_cur_app_val.num_val = p_vals->num_attr ; + avrc_rsp.get_cur_app_val.p_vals = app_sett ; + for (i = 0; i < p_vals->num_attr; ++i) + { + app_sett[i].attr_id = p_vals->attr_ids[i] ; + app_sett[i].attr_val = p_vals->attr_values[i]; + BTIF_TRACE_DEBUG("%s attr_id:0x%x, charset_id:0x%x, num_element:%d", + __FUNCTION__, (unsigned int)app_sett[i].attr_id, + app_sett[i].attr_val ,p_vals->num_attr ); + } + //Update PDU , status aind + avrc_rsp.get_cur_app_val.status = AVRC_STS_NO_ERROR; + } + avrc_rsp.get_cur_app_val.pdu = AVRC_PDU_GET_CUR_PLAYER_APP_VALUE; + avrc_rsp.get_cur_app_val.opcode = opcode_from_pdu(AVRC_PDU_GET_CUR_PLAYER_APP_VALUE); + SEND_METAMSG_RSP(IDX_GET_CURR_APP_VAL_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/******************************************************************** +** +** Function set_player_app_value_rsp +** +** Description This method is called in response to +** application value +** +** Return bt_staus_t +** +*******************************************************************/ +static bt_status_t set_player_app_value_rsp (btrc_status_t rsp_status ) +{ + tAVRC_RESPONSE avrc_rsp; + tAVRC_RSP set_app_val; + + CHECK_RC_CONNECTED + avrc_rsp.set_app_val.opcode = opcode_from_pdu(AVRC_PDU_SET_PLAYER_APP_VALUE); + avrc_rsp.set_app_val.pdu = AVRC_PDU_SET_PLAYER_APP_VALUE ; + avrc_rsp.set_app_val.status = rsp_status ; + SEND_METAMSG_RSP(IDX_SET_APP_VAL_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/******************************************************************** +** +** Function get_player_app_attr_text_rsp +** +** Description This method is called in response to get player +** applicaton attribute text response +** +** +*******************************************************************/ +static bt_status_t get_player_app_attr_text_rsp(int num_attr, btrc_player_setting_text_t *p_attrs) +{ + tAVRC_RESPONSE avrc_rsp; + tAVRC_APP_SETTING_TEXT attr_txt[AVRC_MAX_APP_ATTR_SIZE]; + int i; + + CHECK_RC_CONNECTED + if (num_attr == 0) + { + avrc_rsp.get_app_attr_txt.status = AVRC_STS_BAD_PARAM; + } + else + { + for (i =0; i< num_attr; ++i) + { + attr_txt[i].charset_id = AVRC_CHARSET_ID_UTF8; + attr_txt[i].attr_id = p_attrs[i].id ; + attr_txt[i].str_len = (UINT8)strnlen((char *)p_attrs[i].text, BTRC_MAX_ATTR_STR_LEN); + attr_txt[i].p_str = p_attrs[i].text ; + BTIF_TRACE_DEBUG("%s attr_id:0x%x, charset_id:0x%x, str_len:%d, str:%s", + __FUNCTION__, (unsigned int)attr_txt[i].attr_id, + attr_txt[i].charset_id , attr_txt[i].str_len, attr_txt[i].p_str); + } + avrc_rsp.get_app_attr_txt.status = AVRC_STS_NO_ERROR; + } + avrc_rsp.get_app_attr_txt.p_attrs = attr_txt ; + avrc_rsp.get_app_attr_txt.num_attr = (UINT8)num_attr; + avrc_rsp.get_app_attr_txt.pdu = AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT; + avrc_rsp.get_app_attr_txt.opcode = opcode_from_pdu(AVRC_PDU_GET_PLAYER_APP_ATTR_TEXT); + /* Send the response */ + SEND_METAMSG_RSP(IDX_GET_APP_ATTR_TXT_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/******************************************************************** +** +** Function get_player_app_value_text_rsp +** +** Description This method is called in response to Player application +** value text +** +** Return bt_status_t +** +*******************************************************************/ +static bt_status_t get_player_app_value_text_rsp(int num_attr, btrc_player_setting_text_t *p_attrs) +{ + tAVRC_RESPONSE avrc_rsp; + tAVRC_APP_SETTING_TEXT attr_txt[AVRC_MAX_APP_ATTR_SIZE]; + int i; + + CHECK_RC_CONNECTED + if (num_attr == 0) + { + avrc_rsp.get_app_val_txt.status = AVRC_STS_BAD_PARAM; + } + else + { + for (i =0; i< num_attr; ++i) + { + attr_txt[i].charset_id = AVRC_CHARSET_ID_UTF8; + attr_txt[i].attr_id = p_attrs[i].id ; + attr_txt[i].str_len = (UINT8)strnlen((char *)p_attrs[i].text ,BTRC_MAX_ATTR_STR_LEN ); + attr_txt[i].p_str = p_attrs[i].text ; + BTIF_TRACE_DEBUG("%s attr_id:0x%x, charset_id:0x%x, str_len:%d, str:%s", + __FUNCTION__, (unsigned int)attr_txt[i].attr_id, + attr_txt[i].charset_id , attr_txt[i].str_len,attr_txt[i].p_str); + } + avrc_rsp.get_app_val_txt.status = AVRC_STS_NO_ERROR; + } + avrc_rsp.get_app_val_txt.p_attrs = attr_txt; + avrc_rsp.get_app_val_txt.num_attr = (UINT8)num_attr; + avrc_rsp.get_app_val_txt.pdu = AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT; + avrc_rsp.get_app_val_txt.opcode = opcode_from_pdu(AVRC_PDU_GET_PLAYER_APP_VALUE_TEXT); + /* Send the response */ + SEND_METAMSG_RSP(IDX_GET_APP_VAL_TXT_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + /*************************************************************************** ** ** Function get_element_attr_rsp ** ** Description Returns the current songs' element attributes -** in text. +** in text. ** ** Returns bt_status_t ** @@ -1397,7 +2240,8 @@ static bt_status_t get_element_attr_rsp(uint8_t num_attr, btrc_element_attr_val_ } else { - for (i=0; i<num_attr; i++) { + for (i=0; i<num_attr; i++) + { element_attrs[i].attr_id = p_attrs[i].attr_id; element_attrs[i].name.charset_id = AVRC_CHARSET_ID_UTF8; element_attrs[i].name.str_len = (UINT16)strlen((char *)p_attrs[i].text); @@ -1455,6 +2299,23 @@ static bt_status_t register_notification_rsp(btrc_event_id_t event_id, case BTRC_EVT_PLAY_POS_CHANGED: avrc_rsp.reg_notif.param.play_pos = p_param->song_pos; break; + case BTRC_EVT_APP_SETTINGS_CHANGED: + avrc_rsp.reg_notif.param.player_setting.num_attr = p_param->player_setting.num_attr; + memcpy(&avrc_rsp.reg_notif.param.player_setting.attr_id, + p_param->player_setting.attr_ids, 2); + memcpy(&avrc_rsp.reg_notif.param.player_setting.attr_value, + p_param->player_setting.attr_values, 2); + break; + case BTRC_EVT_ADDRESSED_PLAYER_CHANGED: + avrc_rsp.reg_notif.param.addr_player.player_id = p_param->player_id; + avrc_rsp.reg_notif.param.addr_player.uid_counter = 0; + break; + case BTRC_EVT_AVAILABLE_PLAYERS_CHANGED: + avrc_rsp.reg_notif.param.evt = 0x0a; + break; + case BTRC_EVT_NOW_PLAYING_CONTENT_CHANGED: + avrc_rsp.reg_notif.param.evt = 0x09; + break; default: BTIF_TRACE_WARNING("%s : Unhandled event ID : 0x%x", __FUNCTION__, event_id); return BT_STATUS_UNHANDLED; @@ -1462,7 +2323,21 @@ static bt_status_t register_notification_rsp(btrc_event_id_t event_id, avrc_rsp.reg_notif.pdu = AVRC_PDU_REGISTER_NOTIFICATION; avrc_rsp.reg_notif.opcode = opcode_from_pdu(AVRC_PDU_REGISTER_NOTIFICATION); - avrc_rsp.get_play_status.status = AVRC_STS_NO_ERROR; + if (type == BTRC_NOTIFICATION_TYPE_REJECT) + { + /* Spec AVRCP 1.5 ,section 6.9.2.2, on completion + * of the addressed player changed notificatons the TG shall + * complete all player specific notification with AV/C C-type + * Rejected with error code Addressed Player changed. + * This will happen in case when music player has changed + * Application should take care of sending reject response. + */ + avrc_rsp.get_play_status.status = AVRC_STS_ADDR_PLAYER_CHG; + } + else + { + avrc_rsp.get_play_status.status = AVRC_STS_NO_ERROR; + } /* Send the response. */ send_metamsg_rsp(btif_rc_cb.rc_handle, btif_rc_cb.rc_notif[event_id-1].label, @@ -1470,6 +2345,288 @@ static bt_status_t register_notification_rsp(btrc_event_id_t event_id, return BT_STATUS_SUCCESS; } + +/*************************************************************************** +** +** Function get_folderitem_rsp +** +** Description Response to Get Folder Items , PDU 0x71 +** +** Returns bt_status_t +** +***************************************************************************/ +static bt_status_t get_folderitem_rsp(btrc_folder_list_entries_t *rsp) +{ + tAVRC_RESPONSE avrc_rsp; + CHECK_RC_CONNECTED + tAVRC_ITEM item[MAX_FOLDER_RSP_SUPPORT]; //Number of players that could be supported + UINT8 index, i, xx, media_attr_cnt; + UINT8 *p_conversion; + + BTIF_TRACE_EVENT("%s() AVRC_PDU_GET_FOLDER_ITEMS", __FUNCTION__); + index = IDX_GET_FOLDER_ITEMS_RSP ; + avrc_rsp.get_items.pdu = AVRC_PDU_GET_FOLDER_ITEMS; + avrc_rsp.get_items.opcode = AVRC_OP_BROWSE; + avrc_rsp.get_items.uid_counter = rsp->uid_counter; + avrc_rsp.get_items.status = rsp->status ;//4 means SUCCESS + avrc_rsp.get_items.item_count = 0; + BTIF_TRACE_EVENT("status =%d, item_count =%d",rsp->status, rsp->item_count); + + for (i=0; (i < rsp->item_count && i < MAX_FOLDER_RSP_SUPPORT) ; ++i) + { + item[i].item_type = rsp->p_item_list[i].item_type; + BTIF_TRACE_EVENT("item_type = %d", rsp->p_item_list[i].item_type); + switch (item[i].item_type) + { + case AVRC_ITEM_PLAYER: + memcpy(item[i].u.player.features, rsp->p_item_list[i].u.player.features, + AVRC_FEATURE_MASK_SIZE); + item[i].u.player.major_type = rsp->p_item_list[i].u.player.major_type; + item[i].u.player.sub_type = rsp->p_item_list[i].u.player.sub_type; + item[i].u.player.play_status = rsp->p_item_list[i].u.player.play_status; + item[i].u.player.player_id = rsp->p_item_list[i].u.player.player_id; + item[i].u.player.name.charset_id = rsp->p_item_list[i].u.player.name.charset_id; + item[i].u.player.name.str_len = rsp->p_item_list[i].u.player.name.str_len; + item[i].u.player.name.p_str = rsp->p_item_list[i].u.player.name.p_str; + ++avrc_rsp.get_items.item_count; + break; + + case AVRC_ITEM_FOLDER: + item[i].u.folder.type = rsp->p_item_list[i].u.folder.type; + item[i].u.folder.playable = rsp->p_item_list[i].u.folder.playable; + { + p_conversion = (UINT8*)&(rsp->p_item_list[i].u.folder.uid); + for (xx = 0; xx < AVRC_UID_SIZE; xx++) + { + ((UINT8 *) item[i].u.folder.uid)[AVRC_UID_SIZE - (xx + 1)] = \ + *p_conversion++; + } + } + + item[i].u.folder.name.charset_id = rsp->p_item_list[i].u.folder.name.charset_id; + item[i].u.folder.name.str_len = rsp->p_item_list[i].u.folder.name.str_len; + item[i].u.folder.name.p_str = rsp->p_item_list[i].u.folder.name.p_str; + ++avrc_rsp.get_items.item_count; + break; + + case AVRC_ITEM_MEDIA: + item[i].u.media.type = rsp->p_item_list[i].u.media.type; + { + p_conversion = (UINT8*)&(rsp->p_item_list[i].u.media.uid); + //BE_STREAM_TO_ARRAY(p_conversion, item[i].u.folder.uid, AVRC_UID_SIZE); + for (xx = 0; xx < AVRC_UID_SIZE; xx++) + { + ((UINT8 *) item[i].u.folder.uid)[AVRC_UID_SIZE - (xx + 1)] = \ + *p_conversion++; + } + } + item[i].u.media.name.charset_id = rsp->p_item_list[i].u.media.name.charset_id; + item[i].u.media.name.str_len = rsp->p_item_list[i].u.media.name.str_len; + item[i].u.media.name.p_str = rsp->p_item_list[i].u.media.name.p_str; + media_attr_cnt = rsp->p_item_list[i].u.media.attr_count; + item[i].u.media.attr_count = rsp->p_item_list[i].u.media.attr_count; + BTIF_TRACE_ERROR("attr count = %d", media_attr_cnt); + if (media_attr_cnt > 0) + { + if ((item[i].u.media.p_attr_list = \ + (tAVRC_ATTR_ENTRY *)GKI_getbuf((UINT16)(media_attr_cnt * \ + sizeof(tAVRC_ATTR_ENTRY)))) != NULL) + { + for (xx = 0; xx < media_attr_cnt; xx++) + { + item[i].u.media.p_attr_list[xx].attr_id = \ + rsp->p_item_list[i].u.media.p_attr_list[xx].attr_id; + item[i].u.media.p_attr_list[xx].name.charset_id = \ + rsp->p_item_list[i].u.media.p_attr_list[xx].name.charset_id; + item[i].u.media.p_attr_list[xx].name.str_len = \ + rsp->p_item_list[i].u.media.p_attr_list[xx].name.str_len; + item[i].u.media.p_attr_list[xx].name.p_str = \ + rsp->p_item_list[i].u.media.p_attr_list[xx].name.p_str; + BTIF_TRACE_ERROR("attr_id = %d", item[i].u.media.p_attr_list[xx].\ + attr_id); + BTIF_TRACE_ERROR("str_len = %d", item[i].u.media.p_attr_list[xx].\ + name.str_len); + } + } + else + { + BTIF_TRACE_ERROR("Not enough buffer allocated to accomodate attributes"); + item[i].u.media.attr_count = 0; + } + } + ++avrc_rsp.get_items.item_count; + break; + + default: + return BT_STATUS_UNHANDLED; + break; + } + } + if (avrc_rsp.get_items.item_count == 0) { + /*As per spec Send proper Error if no Music App is registered.*/ + avrc_rsp.get_items.status = AVRC_STS_BAD_RANGE; + } + avrc_rsp.get_items.p_item_list = item; + app_sendbrowsemsg(IDX_GET_FOLDER_ITEMS_RSP ,&avrc_rsp); + BTIF_TRACE_ERROR("free attr list"); + for (i=0; (i < rsp->item_count && i < MAX_FOLDER_RSP_SUPPORT) ; ++i) + { + if (item[i].item_type == AVRC_ITEM_MEDIA) + { + if (rsp->p_item_list[i].u.media.attr_count > 0) + { + GKI_freebuf(item[i].u.media.p_attr_list); + } + } + } + return BT_STATUS_SUCCESS; +} + +/********************************************************************** +** +** Function set_addrplayer_rsp +** +** Description Response to Set Addressed Player , PDU 0x60 +** +** Return status +** +*********************************************************************/ + +static bt_status_t set_addrplayer_rsp(btrc_status_t status_code) +{ + tAVRC_RESPONSE avrc_rsp; + CHECK_RC_CONNECTED + avrc_rsp.addr_player.status = status_code; + avrc_rsp.addr_player.opcode = opcode_from_pdu(AVRC_PDU_SET_ADDRESSED_PLAYER); + avrc_rsp.addr_player.pdu = AVRC_PDU_SET_ADDRESSED_PLAYER; + /* Send the response */ + SEND_METAMSG_RSP(IDX_SET_ADDRESS_PLAYER_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/********************************************************************** +** +** Function set_browseplayer_rsp +** +** Description Response to Set Browsed Player , PDU 0x70 +** +** Return status +** +*********************************************************************/ + +static bt_status_t set_browseplayer_rsp(btrc_set_browsed_player_rsp_t *p_param) +{ + tAVRC_RESPONSE avrc_rsp; + + CHECK_RC_CONNECTED + avrc_rsp.br_player.pdu = AVRC_PDU_SET_BROWSED_PLAYER; + avrc_rsp.br_player.folder_depth = p_param->folder_depth; + avrc_rsp.br_player.charset_id = p_param->charset_id; + avrc_rsp.br_player.num_items = p_param->num_items; + avrc_rsp.br_player.opcode = opcode_from_pdu(AVRC_PDU_SET_BROWSED_PLAYER); + avrc_rsp.br_player.status = p_param->status; + avrc_rsp.br_player.uid_counter = p_param->uid_counter; + avrc_rsp.br_player.p_folders = (tAVRC_NAME*)p_param->p_folders; + /* Send the response */ + SEND_BROWSEMSG_RSP(IDX_SET_BROWSE_PLAYER_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/********************************************************************** +** +** Function changepath_rsp +** +** Description Response to Change Path , PDU 0x60 +** +** Return status +** +*********************************************************************/ + +static bt_status_t changepath_rsp(uint8_t status_code, uint32_t item_count) +{ + tAVRC_RESPONSE avrc_rsp; + + CHECK_RC_CONNECTED + avrc_rsp.chg_path.num_items = item_count; + avrc_rsp.chg_path.opcode = opcode_from_pdu(AVRC_PDU_CHANGE_PATH); + avrc_rsp.chg_path.pdu = AVRC_PDU_CHANGE_PATH; + avrc_rsp.chg_path.status = status_code; + /* Send the response */ + SEND_BROWSEMSG_RSP(IDX_CHANGE_PATH_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/********************************************************************** +** +** Function playitem_rsp +** +** Description Response to Play Item , PDU 0x60 +** +** Return status +** +*********************************************************************/ + +static bt_status_t playitem_rsp(uint8_t status_code) +{ + tAVRC_RESPONSE avrc_rsp; + + CHECK_RC_CONNECTED + avrc_rsp.play_item.status = status_code; + avrc_rsp.play_item.opcode = opcode_from_pdu(AVRC_PDU_PLAY_ITEM); + avrc_rsp.play_item.pdu = AVRC_PDU_PLAY_ITEM; + /* Send the response */ + SEND_METAMSG_RSP(IDX_PLAY_ITEM_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + +/********************************************************************** +** +** Function get_itemattr_rsp +** +** Description Response to Get Item , PDU 0x60 +** +** Return status +** +*********************************************************************/ + +static bt_status_t get_itemattr_rsp(uint8_t num_attr, btrc_element_attr_val_t *p_attrs) +{ + tAVRC_RESPONSE avrc_rsp; + UINT32 i; + uint8_t j; + tAVRC_ATTR_ENTRY element_attrs[BTRC_MAX_ELEM_ATTR_SIZE]; + + CHECK_RC_CONNECTED + memset(element_attrs, 0, sizeof(tAVRC_ATTR_ENTRY) * num_attr); + + if (num_attr == 0) + { + avrc_rsp.get_attrs.status = AVRC_STS_INTERNAL_ERR; + } + else + { + for (i=0; i<num_attr; i++) + { + element_attrs[i].attr_id = p_attrs[i].attr_id; + element_attrs[i].name.charset_id = AVRC_CHARSET_ID_UTF8; + element_attrs[i].name.str_len = (UINT16)strlen((char *)p_attrs[i].text); + element_attrs[i].name.p_str = p_attrs[i].text; + BTIF_TRACE_DEBUG("%s attr_id:0x%x, charset_id:0x%x, str_len:%d, str:%s", + __FUNCTION__, (unsigned int)element_attrs[i].attr_id, + element_attrs[i].name.charset_id, element_attrs[i].name.str_len, + element_attrs[i].name.p_str); + } + avrc_rsp.get_attrs.status = AVRC_STS_NO_ERROR; + } + avrc_rsp.get_attrs.attr_count = num_attr; + avrc_rsp.get_attrs.p_attr_list = element_attrs; + avrc_rsp.get_attrs.pdu = AVRC_PDU_GET_ITEM_ATTRIBUTES; + avrc_rsp.get_attrs.opcode = opcode_from_pdu(AVRC_PDU_GET_ITEM_ATTRIBUTES); + /* Send the response */ + SEND_BROWSEMSG_RSP(IDX_GET_ITEM_ATTR_RSP, &avrc_rsp); + return BT_STATUS_SUCCESS; +} + /*************************************************************************** ** ** Function set_volume @@ -1747,15 +2904,21 @@ static const btrc_interface_t bt_rc_interface = { sizeof(bt_rc_interface), init, get_play_status_rsp, - NULL, /* list_player_app_attr_rsp */ - NULL, /* list_player_app_value_rsp */ - NULL, /* get_player_app_value_rsp */ - NULL, /* get_player_app_attr_text_rsp */ - NULL, /* get_player_app_value_text_rsp */ + list_player_app_attr_rsp, /* list_player_app_attr_rsp */ + list_player_app_value_rsp, /* list_player_app_value_rsp */ + get_player_app_value_rsp, /* get_player_app_value_rsp PDU 0x13*/ + get_player_app_attr_text_rsp, /* get_player_app_attr_text_rsp */ + get_player_app_value_text_rsp,/* get_player_app_value_text_rsp */ get_element_attr_rsp, - NULL, /* set_player_app_value_rsp */ + set_player_app_value_rsp, /* set_player_app_value_rsp */ register_notification_rsp, set_volume, + get_folderitem_rsp, + set_addrplayer_rsp, + set_browseplayer_rsp, + changepath_rsp, + playitem_rsp, + get_itemattr_rsp, cleanup, }; diff --git a/btif/src/btif_util.c b/btif/src/btif_util.c index bc6f99d93..1e7fc7bfd 100644 --- a/btif/src/btif_util.c +++ b/btif/src/btif_util.c @@ -512,6 +512,7 @@ const char *dump_rc_event(UINT8 event) CASE_RETURN_STR(BTA_AV_VENDOR_RSP_EVT) CASE_RETURN_STR(BTA_AV_META_MSG_EVT) CASE_RETURN_STR(BTA_AV_RC_FEAT_EVT) + CASE_RETURN_STR(BTA_AV_BROWSE_MSG_EVT) default: return "UNKNOWN_EVENT"; } @@ -530,7 +531,7 @@ const char * dump_rc_notification_event_id(UINT8 event_id) CASE_RETURN_STR(AVRC_EVT_SYSTEM_STATUS_CHANGE) CASE_RETURN_STR(AVRC_EVT_APP_SETTING_CHANGE) CASE_RETURN_STR(AVRC_EVT_VOLUME_CHANGE) - + CASE_RETURN_STR(AVRC_EVT_NOW_PLAYING_CHANGE) default: return "Unhandled Event ID"; } @@ -554,6 +555,8 @@ const char* dump_rc_pdu(UINT8 pdu) CASE_RETURN_STR(AVRC_PDU_REQUEST_CONTINUATION_RSP) CASE_RETURN_STR(AVRC_PDU_ABORT_CONTINUATION_RSP) CASE_RETURN_STR(AVRC_PDU_SET_ABSOLUTE_VOLUME) + CASE_RETURN_STR(AVRC_PDU_SET_ADDRESSED_PLAYER) + CASE_RETURN_STR(AVRC_PDU_CHANGE_PATH) default: return "Unknown PDU"; } diff --git a/include/bt_target.h b/include/bt_target.h index 7cc4f9e37..47236db2a 100644 --- a/include/bt_target.h +++ b/include/bt_target.h @@ -1581,6 +1581,19 @@ Range: 2 octets #define AVRC_CTLR_INCLUDED TRUE #endif +#ifndef SDP_AVRCP_1_5 +#define SDP_AVRCP_1_5 TRUE + +#if SDP_AVRCP_1_5 == TRUE +#ifndef AVCT_BROWSE_INCLUDED +#define AVCT_BROWSE_INCLUDED TRUE +#else +#ifndef AVCT_BROWSE_INCLUDED +#define AVCT_BROWSE_INCLUDED FALSE +#endif +#endif +#endif +#endif /****************************************************************************** ** ** MCAP diff --git a/stack/avct/avct_api.c b/stack/avct/avct_api.c index bf3dd1854..797e07cb2 100644 --- a/stack/avct/avct_api.c +++ b/stack/avct/avct_api.c @@ -112,6 +112,9 @@ void AVCT_Deregister(void) /* deregister PSM with L2CAP */ L2CA_Deregister(AVCT_PSM); +#if (AVCT_BROWSE_INCLUDED == TRUE) + L2CA_Deregister(AVCT_BR_PSM); +#endif } /******************************************************************************* diff --git a/stack/avct/avct_ccb.c b/stack/avct/avct_ccb.c index e0f87de6a..11c45db62 100644 --- a/stack/avct/avct_ccb.c +++ b/stack/avct/avct_ccb.c @@ -88,6 +88,7 @@ void avct_ccb_dealloc(tAVCT_CCB *p_ccb, UINT8 event, UINT16 result, BD_ADDR bd_a /* control channel is down, but the browsing channel is still connected 0 disconnect it now */ avct_bcb_event(p_ccb->p_bcb, AVCT_LCB_UL_UNBIND_EVT, (tAVCT_LCB_EVT *) &p_ccb); p_ccb->p_lcb = NULL; + memset(p_ccb, 0, sizeof(tAVCT_CCB)); } #else memset(p_ccb, 0, sizeof(tAVCT_CCB)); @@ -148,3 +149,41 @@ tAVCT_CCB *avct_ccb_by_idx(UINT8 idx) } return p_ccb; } + + +/******************************************************************************* +** +** Function avct_get_peer_addr_by_ccb +** +** Description Return peer BD address on ccb index (or handle). +** +** +** Returns BD Address. +** +*******************************************************************************/ +BOOLEAN avct_get_peer_addr_by_ccb (UINT8 idx, BD_ADDR addr) +{ + tAVCT_CCB *p_ccb; + BOOLEAN value = FALSE; + p_ccb = avct_ccb_by_idx(idx); + if (p_ccb == NULL) + { + AVCT_TRACE_WARNING("No ccb for idx %d", idx); + } + else + { + if (p_ccb->p_lcb != NULL) + { + memcpy(addr, p_ccb->p_lcb->peer_addr, BD_ADDR_LEN); + value = TRUE; + } + else + { + AVCT_TRACE_WARNING("No lcb for idx %d", idx); + } + } + return value; +} + + + diff --git a/stack/avct/avct_int.h b/stack/avct/avct_int.h index 29300a5cd..1c059ee17 100644 --- a/stack/avct/avct_int.h +++ b/stack/avct/avct_int.h @@ -96,6 +96,8 @@ typedef struct { UINT8 ch_flags; /* L2CAP configuration flags */ BT_HDR *p_tx_msg; /* Message to be sent - in case the browsing channel is not open when MsgReg is called */ UINT8 ch_close; /* CCB index+1, if CCB initiated channel close */ + BUFFER_Q tx_q; /* Transmit data buffer queue */ + BOOLEAN cong; /* TRUE, if congested */ } tAVCT_BCB; #define AVCT_ALOC_LCB 0x01 diff --git a/stack/avct/avct_l2c.c b/stack/avct/avct_l2c.c index 815fa4b33..39f7147b3 100644 --- a/stack/avct/avct_l2c.c +++ b/stack/avct/avct_l2c.c @@ -35,6 +35,13 @@ #define AVCT_L2C_CFG_IND_DONE (1<<0) #define AVCT_L2C_CFG_CFM_DONE (1<<1) +/*L2CAP Browsing Parameter */ +#define AVCT_L2C_TX_WINDOW_SIZE 1 +#define AVCT_L2C_MAX_TRANSMISSION 20 +#define AVCT_L2C_RETRANS_TIMEOUT 2000 +#define AVCT_L2C_MONITOR_TIMEOUT 12000 +#define AVCT_L2C_MPS_SEGMENT_SIZE 1000 + /* callback function declarations */ void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); @@ -45,6 +52,19 @@ void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); +#if (AVCT_BROWSE_INCLUDED == TRUE) +const tL2CAP_FCR_OPTS avct_l2c_br_fcr_opts_def = +{ + L2CAP_FCR_ERTM_MODE, /* Mandatory for Browsing */ + AVCT_L2C_TX_WINDOW_SIZE, /* Tx window size to be #define in bt_target.h*/ + AVCT_L2C_MAX_TRANSMISSION, /* Maximum transmissions before disconnecting */ + AVCT_L2C_RETRANS_TIMEOUT, /* Retransmission timeout (2 secs) */ + AVCT_L2C_MONITOR_TIMEOUT, /* Monitor timeout (12 secs) */ + AVCT_L2C_MPS_SEGMENT_SIZE /* MPS segment size */ +}; +#endif + + /* L2CAP callback function structure */ const tL2CAP_APPL_INFO avct_l2c_appl = { avct_l2c_connect_ind_cback, @@ -60,6 +80,36 @@ const tL2CAP_APPL_INFO avct_l2c_appl = { NULL /* tL2CA_TX_COMPLETE_CB */ }; +#if (AVCT_BROWSE_INCLUDED == TRUE) +/* L2CAP Callback function for Browsing AVRCP 1.4 structure */ + +/* callback function declarations */ +/* callback function declarations */ +void avct_l2c_br_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void avct_l2c_br_connect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_br_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_br_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void avct_l2c_br_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void avct_l2c_br_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void avct_l2c_br_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +void avct_l2c_br_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); + + + +const tL2CAP_APPL_INFO avct_l2c_br_appl = { + avct_l2c_br_connect_ind_cback, + avct_l2c_br_connect_cfm_cback, + NULL, + avct_l2c_br_config_ind_cback, + avct_l2c_br_config_cfm_cback, + avct_l2c_br_disconnect_ind_cback, + avct_l2c_br_disconnect_cfm_cback, + NULL, + avct_l2c_br_data_ind_cback, + avct_l2c_br_congestion_ind_cback, + NULL /* tL2CA_TX_COMPLETE_CB */ +}; +#endif /******************************************************************************* ** ** Function avct_l2c_is_passive @@ -165,6 +215,100 @@ void avct_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 #endif } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_l2c_br_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** Return void +** +*******************************************************************************/ +void avct_l2c_br_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tAVCT_BCB *p_bcb = &avct_cb.bcb[0] ; + tAVCT_LCB *p_lcb = NULL; + UINT16 result = L2CAP_CONN_OK; + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + tL2CAP_ERTM_INFO ertm_info; + tL2CAP_ERTM_INFO *p_ertm_info = NULL; + tL2CAP_CFG_INFO cfg; + UINT8 index; + + AVCT_TRACE_DEBUG("avct_l2c_br_connect_ind_cback lcid:%x, psm:%x, id:%x", + lcid , psm , id); + + /*Check for associated lcb*/ + if ((p_lcb = avct_lcb_by_bd(bd_addr)) == NULL) + { + AVCT_TRACE_ERROR("### LCB not found"); + result = L2CAP_CONN_NO_RESOURCES; + L2CA_ErtmConnectRsp (bd_addr, id, lcid, result, L2CAP_CONN_OK, p_ertm_info); + } + else + { + index = (UINT8) (p_lcb - &avct_cb.lcb[0]); //calculate offset. + AVCT_TRACE_DEBUG("index value = %d",index); + p_bcb = &avct_cb.bcb[index]; + if (p_bcb == NULL) + { + /*Disconnect as browsing channel = NULL */ + AVCT_TRACE_ERROR("### BCB NULL"); + result = L2CAP_CONN_NO_RESOURCES; + L2CA_ErtmConnectRsp (bd_addr, id, lcid, result, L2CAP_CONN_OK, &ertm_info); + } + else + { + p_bcb->allocated = index +1 ; + for (index = 0; index < AVCT_NUM_CONN; ++index) + { + if (avct_cb.ccb[index].p_lcb == p_lcb) + { + AVCT_TRACE_ERROR("index value = %d",index); + avct_cb.ccb[index].p_bcb = p_bcb ; + avct_cb.ccb[index].allocated = AVCT_ALOC_BCB ; + } + + } + AVCT_TRACE_DEBUG("Channel RSP"); + p_bcb->ch_lcid = lcid; /*Updadate LCID so that on config associated bcb could be found*/ + ertm_info.preferred_mode = L2CAP_FCR_ERTM_MODE; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = HCI_ACL_POOL_ID; + ertm_info.user_tx_pool_id = HCI_ACL_POOL_ID; + ertm_info.fcr_rx_pool_id = HCI_ACL_POOL_ID; + ertm_info.fcr_tx_pool_id = HCI_ACL_POOL_ID; + p_ertm_info = &ertm_info; + L2CA_ErtmConnectRsp (bd_addr, id, lcid, result, L2CAP_CONN_OK, p_ertm_info); + } + /*Send L2CAP Channel Rsp if result = ok + * proceed with connection + */ + if (result == L2CAP_CONN_OK) + { + /* transition to configuration state */ + p_bcb->ch_state = AVCT_CH_CFG; + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + cfg.mtu = avct_cb.mtu_br; //As per initial config it's 1008 + cfg.fcr_present = TRUE; + memcpy(&cfg.fcr, &avct_l2c_br_fcr_opts_def, sizeof (tL2CAP_FCR_OPTS)); + /*Send Configue Request*/ + L2CA_ConfigReq(lcid, &cfg); + AVCT_TRACE_DEBUG("Browse Channel mtu size = %d",cfg.mtu); + } + // Send L2CAP Config Rsp + } + +#if (BT_USE_TRACES == TRUE) + if (p_bcb) + AVCT_TRACE_DEBUG("ch_state cni: %d ", p_bcb->ch_state); +#endif +} + +#endif /******************************************************************************* ** ** Function avct_l2c_connect_cfm_cback @@ -226,6 +370,21 @@ void avct_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************** +** Function avct_l2c_br_connect_cfm_cback +** Description Function is not expected to be called +** as browsing is initiated by remote device. +** +*********************************************************************************/ + +void avct_l2c_br_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + AVCT_TRACE_ERROR("avct_l2c_br_connect_cfm_cback %x: lcid , %x: result", lcid, result ); +} +#endif + + /******************************************************************************* ** ** Function avct_l2c_config_cfm_cback @@ -264,7 +423,7 @@ void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) /* else failure */ else { - AVCT_TRACE_DEBUG("ERROR avct_l2c_config_cfm_cback L2CA_DisconnectReq %d ", p_lcb->ch_state); + AVCT_TRACE_ERROR("ERROR avct_l2c_config_cfm_cback L2CA_DisconnectReq = %d ", p_lcb->ch_state); /* store result value */ p_lcb->ch_result = p_cfg->result; @@ -276,6 +435,66 @@ void avct_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/************************************************************************* +** Function : avct_l2c_br_config_cfm_cback +** +** Description: This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*************************************************************************/ + +void avct_l2c_br_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO * p_cfg) +{ + tAVCT_BCB *p_bcb; + AVCT_TRACE_DEBUG("avct_l2c_br_config_cfm_cback lcid : %x",lcid); + + /*lookup for bcb channel, for given lcid there + * should be a bcb associated + */ + if ((p_bcb = avct_bcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG("avct_l2c_br_config_cfm_cback: 0x%x, ch_state: %d, res: %d, ch_flags", + lcid, p_bcb->ch_state, p_cfg->result ,p_bcb->ch_flags ); + AVCT_TRACE_DEBUG("MTU :%x ",p_cfg->mtu); + /*Check for the state , it should be in config*/ + if (p_bcb->ch_state == AVCT_CH_CFG) + { + /*Check for remote l2cap response */ + if (p_cfg->result == L2CAP_CFG_OK) + { + /*Update BCB ch_flags ,either IND_DONE , CFM_DONE */ + p_bcb->ch_flags |= AVCT_L2C_CFG_CFM_DONE; + /* Check for Configuraiton Flag , if configuration complete*/ + if (p_bcb->ch_flags & AVCT_L2C_CFG_IND_DONE) + { + p_bcb->ch_state = AVCT_CH_OPEN; + p_bcb->state = 0; + avct_bcb_event(p_bcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + /*IF L2CAP response result is != L2CAP_CFG_OK*/ + else + { + AVCT_TRACE_ERROR("###config_cfm_cback error, ch_state:%d ", p_bcb->ch_state); + /* store result value */ + p_bcb->ch_result = p_cfg->result; + /* Send L2CAP disconnect req */ + L2CA_DisconnectReq(lcid); + } + } + AVCT_TRACE_DEBUG("ch_state cfc: %d ", p_bcb->ch_state); + } + else + { + AVCT_TRACE_ERROR("### BCB NULL"); + L2CA_DisconnectReq(lcid); + } +} +#endif + /******************************************************************************* ** ** Function avct_l2c_config_ind_cback @@ -326,6 +545,64 @@ void avct_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/************************************************************************** +** Function : avct_l2c_config_ind_cback +** +** Description: This is the L2CAP config indication callback function. +** +** +** Returns: void +** +****************************************************************************/ +void avct_l2c_br_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tAVCT_BCB *p_bcb; + + AVCT_TRACE_DEBUG("config_ind_cback tx_win_sz:%x, max_transmit:%x, rtrans:%x,mon_tout : %x",p_cfg->fcr.tx_win_sz ,\ + p_cfg->fcr.max_transmit , p_cfg->fcr.rtrans_tout , p_cfg->fcr.mon_tout); + + if ((p_bcb = avct_bcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG("avct_l2c_br_config_ind_cback: 0x%x, ch_state: %d", lcid, p_bcb->ch_state); + /* store the mtu in tbl Start from here */ + AVCT_TRACE_DEBUG("avct_l2c_br_config_ind_cback mtu_present: 0x%x, peer mtu: %d", p_cfg->mtu_present, p_cfg->mtu); + if (p_cfg->mtu_present) + { + AVCT_TRACE_DEBUG("PEER MTU: 0x%x", p_bcb->peer_mtu); + p_bcb->peer_mtu = p_cfg->mtu; + } + else + { + p_bcb->peer_mtu = L2CAP_DEFAULT_MTU; + } + /* send L2CAP configure response */ + memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + p_cfg->result = L2CAP_CFG_OK; + L2CA_ConfigRsp(lcid, p_cfg); + /* if first config ind */ + if ((p_bcb->ch_flags & AVCT_L2C_CFG_IND_DONE) == 0) + { + /* update flags */ + p_bcb->ch_flags |= AVCT_L2C_CFG_IND_DONE; + + /* if configuration complete */ + if (p_bcb->ch_flags & AVCT_L2C_CFG_CFM_DONE) + { + p_bcb->ch_state = AVCT_CH_OPEN; + avct_bcb_event(p_bcb, AVCT_LCB_LL_OPEN_EVT, NULL); + } + } + AVCT_TRACE_DEBUG("ch_state cfi: %d ", p_bcb->ch_state); + } + else + { + AVCT_TRACE_ERROR("### BCB= NULL"); + L2CA_DisconnectReq(lcid); + } +} +#endif + /******************************************************************************* ** ** Function avct_l2c_disconnect_ind_cback @@ -356,6 +633,40 @@ void avct_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************************** +** Function: avct_l2c_br_disconnect_ind_cback +** +** Description: This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +********************************************************************************************/ +void avct_l2c_br_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tAVCT_BCB *p_bcb; + UINT16 result = AVCT_RESULT_FAIL; + AVCT_TRACE_DEBUG("avct_l2c_br_disconnect_ind_cback lcid : %d ", lcid); + //Lookup BCB for this event + if ((p_bcb = avct_bcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG("avct_l2c_disconnect_ind_cback: 0x%x, ch_state: %d",\ + lcid, p_bcb->ch_state); + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DisconnectRsp(lcid); + } + avct_bcb_event(p_bcb, AVCT_LCB_LL_CLOSE_EVT, (tAVCT_LCB_EVT *) &result); + } + else + { + AVCT_TRACE_ERROR("###BCB NULL"); + } +} +#endif + /******************************************************************************* ** ** Function avct_l2c_disconnect_cfm_cback @@ -385,6 +696,32 @@ void avct_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) } } + +#if (AVCT_BROWSE_INCLUDED == TRUE) +/********************************************************************************* +** +** Function avct_l2c_br_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect Confirmation callback function +** +** +** Returns Void +** +***********************************************************************************/ +void avct_l2c_br_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tAVCT_BCB *p_bcb; + AVCT_TRACE_DEBUG("avct_l2c_br_disconnect_cfm_cback lcid : %d ", lcid); + + if ((p_bcb = avct_bcb_by_lcid(lcid)) != NULL) + { + AVCT_TRACE_DEBUG("avct_l2c_disconnect_cfm_cback: 0x%x, ch_state: %d, res: %d", + lcid, p_bcb->ch_state, result); + p_bcb->ch_result = 0; + } +} + +#endif /******************************************************************************* ** ** Function avct_l2c_congestion_ind_cback @@ -407,6 +744,34 @@ void avct_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) + +/********************************************************************************* +** Function avct_l2c_br_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** Returns void +****************************************************************************/ +void avct_l2c_br_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tAVCT_BCB *p_bcb = NULL; + AVCT_TRACE_DEBUG("avct_l2c_br_congestion_ind_cback lcid:%d, congestion:%d", + lcid,is_congested); + + /* look up for Browse Control Block for this channel */ + p_bcb = avct_bcb_by_lcid(lcid); + if (p_bcb != NULL) + { + avct_bcb_event(p_bcb, AVCT_LCB_LL_CONG_EVT, (tAVCT_LCB_EVT *) &is_congested); + } + else + { + AVCT_TRACE_ERROR("### No BCB associated with lcid"); + } +} +#endif + /******************************************************************************* ** ** Function avct_l2c_data_ind_cback @@ -434,3 +799,32 @@ void avct_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/***************************************************************************** +** +** Function avct_l2c_br_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** Returns void +** +*****************************************************************************/ +void avct_l2c_br_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tAVCT_BCB *p_bcb; + + AVCT_TRACE_DEBUG("avct_l2c_br_data_ind_cback lcid : %d ", lcid); + AVCT_TRACE_DEBUG("BT_HDR event: %x , len: %x , offset: %x, layer_spec: %x ", p_buf->event, + p_buf->len,p_buf->offset , p_buf->layer_specific ); + if ((p_bcb = avct_bcb_by_lcid(lcid)) != NULL) + { + avct_bcb_event(p_bcb, AVCT_LCB_LL_MSG_EVT, (tAVCT_LCB_EVT *) &p_buf); + } + else /* prevent buffer leak */ + { + AVCT_TRACE_WARNING("avct_l2c_br_data_ind_cback drop buffer"); + GKI_freebuf(p_buf); + } + +} +#endif diff --git a/stack/avct/avct_lcb.c b/stack/avct/avct_lcb.c index 26c03c72e..d61a4f6ab 100644 --- a/stack/avct/avct_lcb.c +++ b/stack/avct/avct_lcb.c @@ -37,6 +37,7 @@ #if BT_TRACE_VERBOSE == TRUE + /* verbose state strings for trace */ const char * const avct_lcb_st_str[] = { "LCB_IDLE_ST", @@ -113,6 +114,27 @@ const tAVCT_LCB_ACTION avct_lcb_action[] = { avct_lcb_free_msg_ind }; +#if (AVCT_BROWSE_INCLUDED == TRUE) +const tAVCT_BCB_ACTION avct_bcb_action[] = { + avct_bcb_chnl_open, + avct_bcb_chnl_disc, + avct_bcb_send_msg, + avct_bcb_open_ind, + avct_bcb_open_fail, + avct_bcb_close_ind, + avct_bcb_close_cfm, + avct_bcb_msg_ind, + avct_bcb_cong_ind, + avct_bcb_bind_conn, + avct_bcb_bind_fail, + avct_bcb_unbind_disc, + avct_bcb_chk_disc, + avct_bcb_discard_msg, + avct_bcb_dealloc, + avct_bcb_free_msg_ind +}; +#endif + /* state table information */ #define AVCT_LCB_ACTIONS 2 /* number of actions */ #define AVCT_LCB_NEXT_STATE 2 /* position of next state */ @@ -302,6 +324,55 @@ tAVCT_LCB *avct_lcb_by_bd(BD_ADDR bd_addr) return p_lcb; } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/***************************************************************************** +** +** Function avct_bcb_by_lcb +** +** Description This function finds bcb associated to a lcb +** +** Returns pointer to the bcb , or NULL if none found. +** +*******************************************************************************/ +tAVCT_BCB *avct_bcb_by_lcb(tAVCT_LCB *p_lcb) +{ + int i; + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + AVCT_TRACE_DEBUG("avct_bcb_by_lcb %d , ccb ", p_ccb->allocated); + if ( p_ccb->p_lcb == p_lcb ) + { + return avct_cb.ccb[i].p_bcb; + } + } + AVCT_TRACE_ERROR("###avct_bcb_by_lcb ERROR"); + return NULL ; +} + +/***************************************************************************** +** +** Function avct_lcb_by_bcb +** +** Description This +** +** Returns pointer to the lcb, or NULL if none found. +** +*******************************************************************************/ +tAVCT_LCB *avct_lcb_by_bcb(tAVCT_BCB *p_bcb) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + for (i = 0; i < AVCT_NUM_CONN; i++) + { + AVCT_TRACE_DEBUG("avct_lcb_alloc= %d", p_bcb->allocated); + if(p_ccb[i].allocated && p_ccb[i].p_bcb == p_bcb) + return avct_cb.ccb[i].p_lcb; + } + AVCT_TRACE_ERROR("###avct_lcb_by_bcb ERROR"); + return NULL ; +} +#endif /******************************************************************************* ** ** Function avct_lcb_alloc @@ -383,6 +454,55 @@ void avct_lcb_dealloc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } +#if AVCT_BROWSE_INCLUDED + +/******************************************************************************* +** +** Function avct_bcb_dealloc +** +** Description Deallocate a browse control block. +** +** +** Returns void. +** +*******************************************************************************/ +void avct_bcb_dealloc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + BOOLEAN found = FALSE; + int i; + + if (p_bcb != NULL) + { + AVCT_TRACE_DEBUG("avct_bcb_dealloc %d", p_bcb->allocated); + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + /* if ccb allocated and */ + if (p_ccb->allocated) + { + if (p_ccb->p_bcb == p_bcb) + { + AVCT_TRACE_DEBUG("avct_lcb_dealloc used by ccb: %d", i); + found = TRUE; + break; + } + } + } + + if (!found) + { + AVCT_TRACE_DEBUG("avct_bcb_dealloc now"); + memset(p_bcb, 0, sizeof(tAVCT_BCB)); + } + } + else + { + AVCT_TRACE_ERROR("### dealloc Null bcb"); + } + +} + +#endif /******************************************************************************* ** ** Function avct_lcb_by_lcid @@ -416,6 +536,41 @@ tAVCT_LCB *avct_lcb_by_lcid(UINT16 lcid) return p_lcb; } +#if AVCT_BROWSE_INCLUDED +/****************************************************************************** +** +** Function avct_bcb_by_lcid(UINT16 lcid); +** +** Description Find the BCB associated with the L2CAP LCID +** +** Return pointer to browse control block +** +*******************************************************************************/ + +tAVCT_BCB *avct_bcb_by_lcid(UINT16 lcid) +{ + AVCT_TRACE_DEBUG("avct_bcb_by_lcid :=%x",lcid); + tAVCT_BCB *p_bcb = &avct_cb.bcb[0]; + int i; + + for (i = 0; i < AVCT_NUM_LINKS; i++, p_bcb++) + { + if (p_bcb->allocated && ((p_bcb->ch_lcid == lcid))) + { + AVCT_TRACE_DEBUG("avct_bcb_by_lcid :=%x",p_bcb->ch_lcid); + break; + } + } + if (i == AVCT_NUM_LINKS) + { + /*out of bcbs */ + p_bcb = NULL; + AVCT_TRACE_WARNING("###No bcb for lcid %x", lcid); + } + return p_bcb; +} + +#endif /******************************************************************************* ** ** Function avct_lcb_has_pid diff --git a/stack/avct/avct_lcb_act.c b/stack/avct/avct_lcb_act.c index 589af0bdf..3ba1d1d41 100644 --- a/stack/avct/avct_lcb_act.c +++ b/stack/avct/avct_lcb_act.c @@ -175,6 +175,61 @@ static BT_HDR *avct_lcb_msg_asmbl(tAVCT_LCB *p_lcb, BT_HDR *p_buf) return p_ret; } +#if (AVCT_BROWSE_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function avct_bcb_msg_asmbl +** +** Description Reassemble incoming message. +** +** +** Returns Pointer to reassembled message; NULL if no message +** available. +** +*******************************************************************************/ +static BT_HDR *avct_bcb_msg_asmbl(tAVCT_BCB *p_bcb, BT_HDR *p_buf) +{ + UINT8 *p; + UINT8 pkt_type; + BT_HDR *p_ret; + UINT16 buf_len; + + /* parse the message header */ + AVCT_TRACE_DEBUG("bcb_msg_asmbl peer_mtu:%x, ch_lcid:%x",p_bcb->peer_mtu, \ + p_bcb->ch_lcid); + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_PRS_PKT_TYPE(p, pkt_type); + AVCT_TRACE_DEBUG("bcb_msg_asmbl pkt_type:%d, p_buf->offset:%x",pkt_type, \ + p_buf->offset); + /* quick sanity check on length */ + if (p_buf->len < avct_lcb_pkt_type_len[pkt_type]) + { + GKI_freebuf(p_buf); + AVCT_TRACE_WARNING("### Bad length during reassembly"); + p_ret = NULL; + } + + /* As Per AVRCP 1.5 Spec,Fragmentation is not allowed + * Section 7.2 AVCTP fragmentation shall not be used on the AVCTP Browsing Channel. + * packet type wil be single else return error + */ + else if (pkt_type == AVCT_PKT_TYPE_SINGLE) + { + AVCT_TRACE_DEBUG("Got single during reassembly"); + p_ret = p_buf; + } + else + { + GKI_freebuf(p_buf); + p_ret =NULL; + AVCT_TRACE_WARNING("### Got Fragmented packet"); + } + return p_ret; +} +#endif + + /******************************************************************************* ** @@ -201,6 +256,50 @@ void avct_lcb_chnl_open(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_chnl_open +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_chnl_open(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + /*DUT does not initiate Browsing channel connect */ + AVCT_TRACE_ERROR("###avct_bcb_chnl_open"); +} + + +/******************************************************************************** +** +** Function avct_close_bcb +** +** Description Clear BCB data structure +** +** +** Returns void. +** +******************************************************************************/ +void avct_close_bcb(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) +{ + int i; + tAVCT_BCB *p_bcb = NULL; + + AVCT_TRACE_DEBUG("avct_close_bcb"); + p_bcb = avct_bcb_by_lcb(p_lcb); + if (p_bcb != NULL) + { + AVCT_TRACE_DEBUG("Send Disconnect Event"); + p_bcb->allocated = 0; + avct_bcb_event( p_bcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } +} + +#endif + /******************************************************************************* ** ** Function avct_lcb_unbind_disc @@ -218,6 +317,24 @@ void avct_lcb_unbind_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) avct_ccb_dealloc(p_data->p_ccb, AVCT_DISCONNECT_CFM_EVT, 0, NULL); } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_unbind_disc +** +** Description Deallocate ccb and call callback with disconnect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_unbind_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_ERROR("### avct_bcb_unbind_disc"); +} +#endif + + /******************************************************************************* ** ** Function avct_lcb_open_ind @@ -271,6 +388,58 @@ void avct_lcb_open_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_open_ind +** +** Description Handle an LL_OPEN event. For each allocated ccb already +** bound to this lcb, send a connect event. For each +** unbound ccb with a new PID, bind that ccb to this lcb and +** send a connect event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_open_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + tAVCT_LCB *p_lcb =NULL; + int i; + BOOLEAN bind = FALSE; + + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + AVCT_TRACE_DEBUG("avct_bcb_open_ind: %d",p_ccb->allocated); + /*if ccb allocated */ + if (p_ccb->allocated & AVCT_ALOC_BCB) + { + /* if bound to this bcb then send connect confirm event */ + if (p_ccb->p_bcb == p_bcb) + { + AVCT_TRACE_DEBUG("open_ind success"); + p_lcb = avct_cb.ccb[i].p_lcb; + if (p_lcb != NULL) + { + bind = TRUE; + p_ccb->cc.p_ctrl_cback(avct_ccb_to_idx(p_ccb), AVCT_BROWSE_CONN_CFM_EVT, + 0, p_lcb->peer_addr); + break; + } + } + } + + } + if (bind == FALSE) + { + AVCT_TRACE_ERROR("### open_ind error"); + avct_bcb_event(p_bcb, AVCT_LCB_INT_CLOSE_EVT, p_data); + } +} +#endif + + /******************************************************************************* ** ** Function avct_lcb_open_fail @@ -296,7 +465,25 @@ void avct_lcb_open_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_open_fail +** +** Description L2CAP channel open attempt failed. Deallocate any ccbs +** on this lcb and send connect confirm event with failure. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_open_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + /* DUT does not initiate browsing channel open */ + AVCT_TRACE_ERROR("###avct_bcb_open_fail"); +} +#endif /******************************************************************************* ** ** Function avct_lcb_close_ind @@ -333,6 +520,37 @@ void avct_lcb_close_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_lcb_close_ind +** +** Description L2CAP channel closed by peer. Deallocate any initiator +** ccbs on this lcb and send disconnect ind event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_close_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + tAVCT_CCB *p_ccb = &avct_cb.ccb[0]; + int i; + AVCT_TRACE_DEBUG("avct_bcb_close_ind"); + for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) + { + if (p_ccb->allocated && (p_ccb->p_bcb == p_bcb)) + { + //set avct_cb.bcb to 0 + memset(p_ccb->p_bcb, 0 ,sizeof(tAVCT_BCB)); + p_ccb->p_bcb == NULL; + AVCT_TRACE_DEBUG("**close_ind"); + } + } +} +#endif + + /******************************************************************************* ** ** Function avct_lcb_close_cfm @@ -379,6 +597,32 @@ void avct_lcb_close_cfm(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_close_cfm +** +** Description +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_close_cfm(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_DEBUG("avct_bcb_close_cfm"); + if (p_bcb != NULL) + { + memset(p_bcb, 0, sizeof(tAVCT_BCB)); + } + else + { + AVCT_TRACE_ERROR("### avct_bcb_close_cfm"); + } +} +#endif + + /******************************************************************************* ** ** Function avct_lcb_bind_conn @@ -396,6 +640,24 @@ void avct_lcb_bind_conn(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) AVCT_CONNECT_CFM_EVT, 0, p_lcb->peer_addr); } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_lcb_bind_conn +** +** Description Bind ccb to lcb and send connect cfm event. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_bind_conn(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + /* DUT does not initiate browse connect*/ + AVCT_TRACE_ERROR("###avct_bcb_bind_conn"); +} + +#endif /******************************************************************************* ** ** Function avct_lcb_chk_disc @@ -427,6 +689,23 @@ void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_chk_disc +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_chk_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + /* BCB disconnecton done during lcb_chk_disc */ + AVCT_TRACE_ERROR("### avct_bcb_chk_disc"); +} +#endif + /******************************************************************************* ** ** Function avct_lcb_chnl_disc @@ -440,10 +719,29 @@ void avct_lcb_chk_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) void avct_lcb_chnl_disc(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) { UNUSED(p_data); - + AVCT_TRACE_DEBUG("avct_lcb_chnl_disc"); L2CA_DisconnectReq(p_lcb->ch_lcid); } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_chnl_disc +** +** Description Disconnect L2CAP channel. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_chnl_disc(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_ERROR("avct_bcb_chnl_disc"); + /* Disconnect L2CAP Browsing channel */ + L2CA_DisconnectReq(p_bcb->ch_lcid); +} +#endif + /******************************************************************************* ** ** Function avct_lcb_bind_fail @@ -462,6 +760,23 @@ void avct_lcb_bind_fail(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) avct_ccb_dealloc(p_data->p_ccb, AVCT_CONNECT_CFM_EVT, AVCT_RESULT_FAIL, NULL); } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_bind_fail +** +** Description +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_bind_fail(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + /* Not expected to be called */ + AVCT_TRACE_ERROR("###avct_bcb_bind_fail"); +} +#endif + /******************************************************************************* ** ** Function avct_lcb_cong_ind @@ -503,6 +818,48 @@ void avct_lcb_cong_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_cong_ind +** +** Description Handle congestion indication from L2CAP. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_cong_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + int i; + UINT8 event; + BT_HDR *p_buf; + + AVCT_TRACE_DEBUG("avct_bcb_cong_ind"); + if (p_bcb != NULL) + { + AVCT_TRACE_DEBUG("avct_bcb_cong_ind = %d",p_data->cong); + /* set event */ + event = (p_data->cong) ? AVCT_CONG_IND_EVT : AVCT_UNCONG_IND_EVT; + p_bcb->cong = p_data->cong; + if (p_bcb->cong == FALSE && GKI_getfirst(&p_bcb->tx_q)) + { + while (!p_bcb->cong && (p_buf = (BT_HDR *)GKI_dequeue(&p_bcb->tx_q)) != NULL) + { + if (L2CA_DataWrite(p_bcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) + { + p_bcb->cong = TRUE; + } + } + } + } + else + { + AVCT_TRACE_ERROR("### bcb NULL"); + } +} +#endif + /******************************************************************************* ** ** Function avct_lcb_discard_msg @@ -522,6 +879,24 @@ void avct_lcb_discard_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) GKI_freebuf(p_data->ul_msg.p_buf); } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_discard_msg +** +** Description Discard a message sent in from the API. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_discard_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_ERROR("### avct_bcb_discard_msg"); + GKI_freebuf(p_data->ul_msg.p_buf); +} +#endif + /******************************************************************************* ** ** Function avct_lcb_send_msg @@ -574,7 +949,7 @@ void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) if ((p_buf = (BT_HDR *) GKI_getbuf(buf_size)) == NULL) { /* whoops; free original msg buf and bail */ - AVCT_TRACE_ERROR ("avct_lcb_send_msg cannot alloc buffer!!"); + AVCT_TRACE_ERROR("avct_lcb_send_msg cannot alloc buffer!!"); GKI_freebuf(p_data->ul_msg.p_buf); break; } @@ -639,7 +1014,68 @@ void avct_lcb_send_msg(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) AVCT_TRACE_DEBUG ("avct_lcb_send_msg tx_q_count:%d", GKI_queue_length(&p_lcb->tx_q)); return; } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_lcb_send_msg +** +** Description Build and send an AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_send_msg(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + UINT16 curr_msg_len; + UINT8 pkt_type; + UINT8 *p; + BT_HDR *p_buf; + UINT8 nosp = 0; /* number of subsequent packets */ + UINT16 buf_size = p_bcb->peer_mtu + L2CAP_MIN_OFFSET + BT_HDR_SIZE; + /* store msg len */ + curr_msg_len = p_data->ul_msg.p_buf->len; + AVCT_TRACE_DEBUG("avct_bcb_send_msg length: %x",curr_msg_len); + AVCT_TRACE_DEBUG("Remote PEER MTU: %x",p_bcb->peer_mtu); + /* initialize packet type and other stuff */ + if (curr_msg_len <= (p_bcb->peer_mtu - AVCT_HDR_LEN_SINGLE)) + { + pkt_type = AVCT_PKT_TYPE_SINGLE; + //No need to do segmentation need to send data as a single packet + p_buf = p_data->ul_msg.p_buf; + + /* set up to build header */ + p_buf->len += AVCT_HDR_LEN_SINGLE; + p_buf->offset -= AVCT_HDR_LEN_SINGLE; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + /* build header */ + p_data->ul_msg.cr = AVCT_RSP ; + AVCT_BLD_HDR(p, p_data->ul_msg.label, pkt_type, p_data->ul_msg.cr); + //UINT8_TO_STREAM(p, nosp); + p_data->ul_msg.p_ccb->cc.pid = 0x110E; + UINT16_TO_BE_STREAM(p, p_data->ul_msg.p_ccb->cc.pid); + if (p_bcb->cong == TRUE) + { + AVCT_TRACE_ERROR("L2CAP congestion"); + GKI_enqueue (&p_bcb->tx_q, p_buf); + } + else + { + if (L2CA_DataWrite(p_bcb->ch_lcid, p_buf) == L2CAP_DW_CONGESTED) + { + AVCT_TRACE_DEBUG("L2CAP Data Write"); + p_bcb->cong = TRUE; + //Flag to be cleared + } + } + } + else + { + AVCT_TRACE_ERROR("### bcb_send_msg, length incorrect"); + } +} +#endif /******************************************************************************* ** ** Function avct_lcb_free_msg_ind @@ -659,6 +1095,26 @@ void avct_lcb_free_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) return; } +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_free_msg_ind +** +** Description Discard an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_free_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + AVCT_TRACE_DEBUG("avct_bcb_free_msg_ind"); + if (p_data) + GKI_freebuf(p_data->p_buf); + +} +#endif + /******************************************************************************* ** ** Function avct_lcb_msg_ind @@ -732,3 +1188,107 @@ void avct_lcb_msg_ind(tAVCT_LCB *p_lcb, tAVCT_LCB_EVT *p_data) } } } + +#if (AVCT_BROWSE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function avct_bcb_msg_ind +** +** Description Handle an incoming AVCTP message. +** +** +** Returns Nothing. +** +*******************************************************************************/ +void avct_bcb_msg_ind(tAVCT_BCB *p_bcb, tAVCT_LCB_EVT *p_data) +{ + UINT8 *p; + UINT8 label, type, cr_ipid; + UINT16 pid; + tAVCT_LCB *p_lcb; + tAVCT_CCB *p_ccb; + BT_HDR *p_buf; + + AVCT_TRACE_DEBUG("avct_bcb_msg_ind"); + /* Update layer specific information so that while + * responding AVCT_MsgReq, AVCT layer knows to respond to + * Browsing channel + */ + p_data->p_buf->layer_specific = AVCT_DATA_BROWSE; + + /* + AVCTP fragmentation shall not be used on the AVCTP Browsing Channel + */ + + if ((p_data->p_buf = avct_bcb_msg_asmbl(p_bcb, p_data->p_buf)) == NULL) + { + AVCT_TRACE_ERROR("### Error bcb_msg_asmbl"); + return; + } + + /* Data passed is HCI+L2CAP+AVCT_AVRCP + * Point to AVCT start + */ + + p = (UINT8 *)(p_data->p_buf + 1) + p_data->p_buf->offset; + + /* parse AVCT header byte */ + AVCT_PRS_HDR(p, label, type, cr_ipid); + + /* check for invalid cr_ipid */ + if (cr_ipid == AVCT_CR_IPID_INVALID) + { + AVCT_TRACE_WARNING("### Invalid cr_ipid", cr_ipid); + GKI_freebuf(p_data->p_buf); + return; + } + /* parse and lookup PID */ + BE_STREAM_TO_UINT16(pid, p); + p_lcb = avct_lcb_by_bcb(p_bcb); + if (p_lcb == NULL) + { + AVCT_TRACE_ERROR("### Error lcb is NULL"); + GKI_freebuf(p_data->p_buf); + } + else + { + AVCT_TRACE_DEBUG("p_lcb param: p[0]: %x, p[1]: %x,\ + p[2] : %x, p[3] : %x ",p_lcb->peer_addr[0], p_lcb->peer_addr[1],p_lcb->peer_addr[2],\ + p_lcb->peer_addr[3]); + /* Irrespective of browsing or control + * message recieved is passed on to above layer + */ + + /* Check if p_lcb is correct */ + if ((p_ccb = avct_lcb_has_pid(p_lcb, pid)) != NULL) + { + /* PID found, send msg to upper laer, adjust + * bt hdr and call msg callback + */ + AVCT_TRACE_DEBUG("label: %x ,cr_ipid : %d ",label, cr_ipid ); + p_data->p_buf->offset += AVCT_HDR_LEN_SINGLE; + p_data->p_buf->len -= AVCT_HDR_LEN_SINGLE; + (*p_ccb->cc.p_msg_cback)(avct_ccb_to_idx(p_ccb), label, cr_ipid, p_data->p_buf); + } + else + { + /* PID not found; drop message */ + AVCT_TRACE_WARNING("### No ccb for PID=%x", pid); + GKI_freebuf(p_data->p_buf); + /* if command send reject */ + if (cr_ipid == AVCT_CMD) + { + if ((p_buf = (BT_HDR *) GKI_getpoolbuf(AVCT_CMD_POOL_ID)) != NULL) + { + p_buf->len = AVCT_HDR_LEN_SINGLE; + p_buf->offset = AVCT_MSG_OFFSET - AVCT_HDR_LEN_SINGLE; + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + AVCT_BLD_HDR(p, label, AVCT_PKT_TYPE_SINGLE, AVCT_REJ); + UINT16_TO_BE_STREAM(p, pid); + L2CA_DataWrite(p_lcb->ch_lcid, p_buf); + } + } + } + } +} +#endif diff --git a/stack/avrc/avrc_api.c b/stack/avrc/avrc_api.c index 1b874dc8c..359d910ec 100755 --- a/stack/avrc/avrc_api.c +++ b/stack/avrc/avrc_api.c @@ -150,7 +150,7 @@ static void avrc_prep_end_frag(UINT8 handle) UINT8 *p_data, *p_orig_data; UINT8 rsp_type; - AVRC_TRACE_DEBUG ("avrc_prep_end_frag" ); + AVRC_TRACE_DEBUG("avrc_prep_end_frag" ); p_fcb = &avrc_cb.fcb[handle]; /* The response type of the end fragment should be the same as the the PDU of "End Fragment @@ -226,7 +226,7 @@ static void avrc_send_continue_frag(UINT8 handle, UINT8 label) p_pkt = p_fcb->p_fmsg; p_fcb->p_fmsg = NULL; p_fcb->frag_enabled = FALSE; - AVRC_TRACE_ERROR ("AVRC_MsgReq no buffers for fragmentation - send internal error" ); + AVRC_TRACE_ERROR("AVRC_MsgReq no buffers for fragmentation - send internal error" ); p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; *p_data++ = AVRC_PDU_REQUEST_CONTINUATION_RSP; *p_data++ = 0; @@ -274,7 +274,7 @@ static BT_HDR * avrc_proc_vendor_command(UINT8 handle, UINT8 label, if (pkt_type != AVRC_PKT_SINGLE) { /* reject - commands can only be in single packets at AVRCP level */ - AVRC_TRACE_ERROR ("commands must be in single packet pdu:0x%x", *p_data ); + AVRC_TRACE_ERROR("commands must be in single packet pdu:0x%x", *p_data ); /* use the current GKI buffer to send the reject */ status = AVRC_STS_BAD_CMD; } @@ -390,7 +390,7 @@ static UINT8 avrc_proc_far_msg(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR **pp_ p_data += AVRC_VENDOR_HDR_SIZE; pkt_type = *(p_data + 1) & AVRC_PKT_TYPE_MASK; - AVRC_TRACE_DEBUG ("pkt_type %d", pkt_type ); + AVRC_TRACE_DEBUG("pkt_type %d", pkt_type ); p_rcb = &avrc_cb.rcb[handle]; if (p_msg->company_id == AVRC_CO_METADATA) { @@ -433,7 +433,7 @@ static UINT8 avrc_proc_far_msg(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR **pp_ { /* Unable to allocate buffer for fragmented avrc message. Reuse START buffer for reassembly (re-assembled message may fit into ACL buf) */ - AVRC_TRACE_DEBUG ("Unable to allocate buffer for fragmented avrc message, \ + AVRC_TRACE_DEBUG("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; @@ -447,7 +447,7 @@ static UINT8 avrc_proc_far_msg(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR **pp_ { /* Received a CONTINUE/END, but no corresponding START (or previous fragmented response was dropped) */ - AVRC_TRACE_DEBUG ("Received a CONTINUE/END without no corresponding START \ + AVRC_TRACE_DEBUG("Received a CONTINUE/END without no corresponding START \ (or previous fragmented response was dropped)"); drop_code = 5; GKI_freebuf(p_pkt); @@ -551,6 +551,7 @@ static void avrc_msg_cback(UINT8 handle, UINT8 label, UINT8 cr, UINT8 opcode; tAVRC_MSG msg; UINT8 *p_data; + UINT8 *browse_length; UINT8 *p_begin; BOOLEAN drop = FALSE; BOOLEAN do_free = TRUE; @@ -585,111 +586,100 @@ static void avrc_msg_cback(UINT8 handle, UINT8 label, UINT8 cr, p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; memset(&msg, 0, sizeof(tAVRC_MSG) ); + /* layer_specific value use to distinguish + * Browsing and control channel PDU ID. + * AVCT_DATA_BROWSE to be used for browsing + * channel + */ + AVRC_TRACE_DEBUG("layer_specific %x",p_pkt->layer_specific); + if (p_pkt->layer_specific != AVCT_DATA_BROWSE) { - msg.hdr.ctype = p_data[0] & AVRC_CTYPE_MASK; - AVRC_TRACE_DEBUG("avrc_msg_cback handle:%d, ctype:%d, offset:%d, len: %d", - handle, msg.hdr.ctype, p_pkt->offset, p_pkt->len); - msg.hdr.subunit_type = (p_data[1] & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; - msg.hdr.subunit_id = p_data[1] & AVRC_SUBID_MASK; - opcode = p_data[2]; - } - - if ( ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) || - ((avrc_cb.ccb[handle].control & AVRC_CT_CONTROL) && (cr == AVCT_RSP)) ) - { - - switch(opcode) { - case AVRC_OP_UNIT_INFO: - if (cr == AVCT_CMD) - { - /* send the response to the peer */ - p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_UNIT_INFO_RSP_LEN); - p_rsp_data = avrc_get_data_ptr(p_rsp); - *p_rsp_data = AVRC_RSP_IMPL_STBL; - /* check & set the offset. set response code, set subunit_type & subunit_id, - set AVRC_OP_UNIT_INFO */ - /* 3 bytes: ctype, subunit*, opcode */ - p_rsp_data += AVRC_AVC_HDR_SIZE; - *p_rsp_data++ = 7; - /* Panel subunit & id=0 */ - *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); - AVRC_CO_ID_TO_BE_STREAM(p_rsp_data, avrc_cb.ccb[handle].company_id); - p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); - cr = AVCT_RSP; -#if (BT_USE_TRACES == TRUE) - p_drop_msg = "auto respond"; -#endif - } - else - { - /* parse response */ - p_data += 4; /* 3 bytes: ctype, subunit*, opcode + octet 3 (is 7)*/ - msg.unit.unit_type = (*p_data & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; - msg.unit.unit = *p_data & AVRC_SUBID_MASK; - p_data++; - AVRC_BE_STREAM_TO_CO_ID(msg.unit.company_id, p_data); - } - break; + msg.hdr.ctype = p_data[0] & AVRC_CTYPE_MASK; + AVRC_TRACE_DEBUG("avrc_msg_cback handle:%d, ctype:%d, offset:%d, len: %d", + handle, msg.hdr.ctype, p_pkt->offset, p_pkt->len); + msg.hdr.subunit_type = (p_data[1] & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; + msg.hdr.subunit_id = p_data[1] & AVRC_SUBID_MASK; + opcode = p_data[2]; + } - case AVRC_OP_SUB_INFO: - if (cr == AVCT_CMD) + if ( ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) || + ((avrc_cb.ccb[handle].control & AVRC_CT_CONTROL) && (cr == AVCT_RSP)) ) + { + switch(opcode) { - /* send the response to the peer */ - p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_SUB_UNIT_INFO_RSP_LEN); - p_rsp_data = avrc_get_data_ptr(p_rsp); - *p_rsp_data = AVRC_RSP_IMPL_STBL; - /* check & set the offset. set response code, set (subunit_type & subunit_id), - set AVRC_OP_SUB_INFO, set (page & extention code) */ - p_rsp_data += 4; - /* Panel subunit & id=0 */ - *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); - memset(p_rsp_data, AVRC_CMD_OPRND_PAD, AVRC_SUBRSP_OPRND_BYTES); - p_rsp_data += AVRC_SUBRSP_OPRND_BYTES; - p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); - cr = AVCT_RSP; + case AVRC_OP_UNIT_INFO: + if (cr == AVCT_CMD) + { + /* send the response to the peer */ + p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_UNIT_INFO_RSP_LEN); + p_rsp_data = avrc_get_data_ptr(p_rsp); + *p_rsp_data = AVRC_RSP_IMPL_STBL; + /* check & set the offset. set response code, set subunit_type & subunit_id, + set AVRC_OP_UNIT_INFO */ + /* 3 bytes: ctype, subunit*, opcode */ + p_rsp_data += AVRC_AVC_HDR_SIZE; + *p_rsp_data++ = 7; + /* Panel subunit & id=0 */ + *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + AVRC_CO_ID_TO_BE_STREAM(p_rsp_data, avrc_cb.ccb[handle].company_id); + p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); + cr = AVCT_RSP; #if (BT_USE_TRACES == TRUE) - p_drop_msg = "auto responded"; + p_drop_msg = "auto respond"; #endif - } - else - { - /* parse response */ - p_data += AVRC_AVC_HDR_SIZE; /* 3 bytes: ctype, subunit*, opcode */ - msg.sub.page = (*p_data++ >> AVRC_SUB_PAGE_SHIFT) & AVRC_SUB_PAGE_MASK; - xx = 0; - while (*p_data != AVRC_CMD_OPRND_PAD && xx<AVRC_SUB_TYPE_LEN) + } + else { - msg.sub.subunit_type[xx] = *p_data++ >> AVRC_SUBTYPE_SHIFT; - if (msg.sub.subunit_type[xx] == AVRC_SUB_PANEL) - msg.sub.panel = TRUE; - xx++; + /* parse response */ + p_data += 4; /* 3 bytes: ctype, subunit*, opcode + octet 3 (is 7)*/ + msg.unit.unit_type = (*p_data & AVRC_SUBTYPE_MASK) >> AVRC_SUBTYPE_SHIFT; + msg.unit.unit = *p_data & AVRC_SUBID_MASK; + p_data++; + AVRC_BE_STREAM_TO_CO_ID(msg.unit.company_id, p_data); } - } - break; + break; - case AVRC_OP_VENDOR: - p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; - p_begin = p_data; - if (p_pkt->len < AVRC_VENDOR_HDR_SIZE) /* 6 = ctype, subunit*, opcode & CO_ID */ - { + case AVRC_OP_SUB_INFO: if (cr == AVCT_CMD) - reject = TRUE; + { + /* send the response to the peer */ + p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_SUB_UNIT_INFO_RSP_LEN); + p_rsp_data = avrc_get_data_ptr(p_rsp); + *p_rsp_data = AVRC_RSP_IMPL_STBL; + /* check & set the offset. set response code, set (subunit_type & subunit_id), + set AVRC_OP_SUB_INFO, set (page & extention code) */ + p_rsp_data += 4; + /* Panel subunit & id=0 */ + *p_rsp_data++ = (AVRC_SUB_PANEL << AVRC_SUBTYPE_SHIFT); + memset(p_rsp_data, AVRC_CMD_OPRND_PAD, AVRC_SUBRSP_OPRND_BYTES); + p_rsp_data += AVRC_SUBRSP_OPRND_BYTES; + p_rsp->len = (UINT16) (p_rsp_data - (UINT8 *)(p_rsp + 1) - p_rsp->offset); + cr = AVCT_RSP; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "auto responded"; +#endif + } else - drop = TRUE; + { + /* parse response */ + p_data += AVRC_AVC_HDR_SIZE; /* 3 bytes: ctype, subunit*, opcode */ + msg.sub.page = (*p_data++ >> AVRC_SUB_PAGE_SHIFT) & AVRC_SUB_PAGE_MASK; + xx = 0; + while (*p_data != AVRC_CMD_OPRND_PAD && xx<AVRC_SUB_TYPE_LEN) + { + msg.sub.subunit_type[xx] = *p_data++ >> AVRC_SUBTYPE_SHIFT; + if (msg.sub.subunit_type[xx] == AVRC_SUB_PANEL) + msg.sub.panel = TRUE; + xx++; + } + } break; - } - p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ - AVRC_BE_STREAM_TO_CO_ID(p_msg->company_id, p_data); - p_msg->p_vendor_data = p_data; - p_msg->vendor_len = p_pkt->len - (p_data - p_begin); -#if (AVRC_METADATA_INCLUDED == TRUE) - UINT8 drop_code = 0; - 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)) + case AVRC_OP_VENDOR: + p_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; + p_begin = p_data; + if (p_pkt->len < AVRC_VENDOR_HDR_SIZE) /* 6 = ctype, subunit*, opcode & CO_ID */ { if (cr == AVCT_CMD) reject = TRUE; @@ -697,117 +687,144 @@ static void avrc_msg_cback(UINT8 handle, UINT8 label, UINT8 cr, drop = TRUE; break; } + p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ + AVRC_BE_STREAM_TO_CO_ID(p_msg->company_id, p_data); + p_msg->p_vendor_data = p_data; + p_msg->vendor_len = p_pkt->len - (p_data - p_begin); - /* Check+handle fragmented messages */ +#if (AVRC_METADATA_INCLUDED == TRUE) + UINT8 drop_code = 0; drop_code = avrc_proc_far_msg(handle, label, cr, &p_pkt, p_msg); if (drop_code > 0) drop = TRUE; - } - if (drop_code > 0) - { - if (drop_code != 4) - do_free = FALSE; -#if (BT_USE_TRACES == TRUE) - switch (drop_code) + if (drop_code > 0) { - case 1: - p_drop_msg = "sent_frag"; - break; - case 2: - p_drop_msg = "req_cont"; - break; - case 3: - p_drop_msg = "sent_frag3"; - break; - case 4: - p_drop_msg = "sent_frag_free"; - break; - default: - p_drop_msg = "sent_fragd"; - } + if (drop_code != 4) + do_free = FALSE; +#if (BT_USE_TRACES == TRUE) + switch (drop_code) + { + case 1: + p_drop_msg = "sent_frag"; + break; + case 2: + p_drop_msg = "req_cont"; + break; + case 3: + p_drop_msg = "sent_frag3"; + break; + case 4: + p_drop_msg = "sent_frag_free"; + break; + default: + p_drop_msg = "sent_fragd"; + } #endif - } + } #endif /* (AVRC_METADATA_INCLUDED == TRUE) */ - break; + break; - case AVRC_OP_PASS_THRU: - if (p_pkt->len < 5) /* 3 bytes: ctype, subunit*, opcode & op_id & len */ - { - if (cr == AVCT_CMD) - reject = TRUE; + case AVRC_OP_PASS_THRU: + if (p_pkt->len < 5) /* 3 bytes: ctype, subunit*, opcode & op_id & len */ + { + if (cr == AVCT_CMD) + reject = TRUE; + else + drop = TRUE; + break; + } + p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ + msg.pass.op_id = (AVRC_PASS_OP_ID_MASK & *p_data); + if (AVRC_PASS_STATE_MASK & *p_data) + msg.pass.state = TRUE; else - drop = TRUE; + msg.pass.state = FALSE; + p_data++; + msg.pass.pass_len = *p_data++; + if (msg.pass.pass_len != p_pkt->len - 5) + msg.pass.pass_len = p_pkt->len - 5; + if (msg.pass.pass_len) + msg.pass.p_pass_data = p_data; + else + msg.pass.p_pass_data = NULL; break; - } - p_data += AVRC_AVC_HDR_SIZE; /* skip the first 3 bytes: ctype, subunit*, opcode */ - msg.pass.op_id = (AVRC_PASS_OP_ID_MASK & *p_data); - if (AVRC_PASS_STATE_MASK & *p_data) - msg.pass.state = TRUE; - else - msg.pass.state = FALSE; - p_data++; - msg.pass.pass_len = *p_data++; - if (msg.pass.pass_len != p_pkt->len - 5) - msg.pass.pass_len = p_pkt->len - 5; - if (msg.pass.pass_len) - msg.pass.p_pass_data = p_data; - else - msg.pass.p_pass_data = NULL; - break; - default: - if ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) - { - /* reject unsupported opcode */ - reject = TRUE; + default: + if ((avrc_cb.ccb[handle].control & AVRC_CT_TARGET) && (cr == AVCT_CMD)) + { + /* reject unsupported opcode */ + reject = TRUE; + } + drop = TRUE; + break; } + } + else /* drop the event */ + { drop = TRUE; - break; } - } - else /* drop the event */ - { + + if (reject) + { + /* reject unsupported opcode */ + p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_REJ_MSG_LEN); + p_rsp_data = avrc_get_data_ptr(p_rsp); + *p_rsp_data = AVRC_RSP_REJ; +#if (BT_USE_TRACES == TRUE) + p_drop_msg = "rejected"; +#endif + cr = AVCT_RSP; drop = TRUE; - } + } - if (reject) - { - /* reject unsupported opcode */ - p_rsp = avrc_copy_packet(p_pkt, AVRC_OP_REJ_MSG_LEN); - p_rsp_data = avrc_get_data_ptr(p_rsp); - *p_rsp_data = AVRC_RSP_REJ; + if (p_rsp) + { + /* set to send response right away */ + AVCT_MsgReq( handle, label, cr, p_rsp); + drop = TRUE; + } + + if (drop == FALSE) + { + msg.hdr.opcode = opcode; + (*avrc_cb.ccb[handle].p_msg_cback)(handle, label, opcode, &msg); + } #if (BT_USE_TRACES == TRUE) - p_drop_msg = "rejected"; + else + { + AVRC_TRACE_WARNING("avrc_msg_cback %s msg handle:%d, control:%d, cr:%d, opcode:x%x", + p_drop_msg, + handle, avrc_cb.ccb[handle].control, cr, opcode); + } #endif - cr = AVCT_RSP; - drop = TRUE; - } - if (p_rsp) - { - /* set to send response right away */ - AVCT_MsgReq( handle, label, cr, p_rsp); - drop = TRUE; - } - if (drop == FALSE) - { - msg.hdr.opcode = opcode; - (*avrc_cb.ccb[handle].p_msg_cback)(handle, label, opcode, &msg); + if (do_free) + GKI_freebuf(p_pkt); } -#if (BT_USE_TRACES == TRUE) else { - AVRC_TRACE_WARNING("avrc_msg_cback %s msg handle:%d, control:%d, cr:%d, opcode:x%x", - p_drop_msg, - handle, avrc_cb.ccb[handle].control, cr, opcode); + opcode = p_data[0]; + AVRC_TRACE_DEBUG("opcode:%x, length:%x",opcode, p_pkt->len); + /*Do sanity Check here*/ + if (cr == AVCT_CMD) + { + opcode = AVRC_OP_BROWSE; + msg.browse.browse_len = p_pkt->len; + AVRC_TRACE_DEBUG("Browsing length %x",msg.browse.browse_len); + /* Browse data remains same */ + msg.browse.p_browse_data = (UINT8 *)(p_pkt+1) + p_pkt->offset; + (*avrc_cb.ccb[handle].p_msg_cback)(handle, label, opcode, &msg); + } + else + { + AVRC_TRACE_ERROR("### expect AVCT_CMD"); + } + /*Free the packet as the same already got copied in BTA*/ + GKI_freebuf(p_pkt); } -#endif - - if (do_free) - GKI_freebuf(p_pkt); } @@ -1076,7 +1093,7 @@ UINT16 AVRC_MsgReq (UINT8 handle, UINT8 label, UINT8 ctype, BT_HDR *p_pkt) } else { - AVRC_TRACE_ERROR ("AVRC_MsgReq no buffers for fragmentation" ); + AVRC_TRACE_ERROR("AVRC_MsgReq no buffers for fragmentation" ); GKI_freebuf(p_pkt); return AVRC_NO_RESOURCES; } diff --git a/stack/avrc/avrc_bld_tg.c b/stack/avrc/avrc_bld_tg.c index a9b1a2adf..c4dfcf81e 100644 --- a/stack/avrc/avrc_bld_tg.c +++ b/stack/avrc/avrc_bld_tg.c @@ -28,6 +28,10 @@ *****************************************************************************/ #if (AVRC_METADATA_INCLUDED == TRUE) +#define EVT_AVAIL_PLAYER_CHANGE_RSP_LENGTH 1 +#define EVT_ADDR_PLAYER_CHANGE_RSP_LENGTH 5 +#define EVT_NOW_PLAYING_CHANGE_RSP_LENGTH 1 + /******************************************************************************* ** ** Function avrc_bld_get_capability_rsp @@ -639,6 +643,20 @@ static tAVRC_STS avrc_bld_notify_rsp (tAVRC_REG_NOTIF_RSP *p_rsp, BT_HDR *p_pkt) status = AVRC_STS_BAD_PARAM; break; + case AVRC_EVT_AVAL_PLAYERS_CHANGE: + len = EVT_AVAIL_PLAYER_CHANGE_RSP_LENGTH; + break; + + case AVRC_EVT_ADDR_PLAYER_CHANGE: + UINT16_TO_BE_STREAM(p_data,p_rsp->param.addr_player.player_id); + UINT16_TO_BE_STREAM(p_data,p_rsp->param.addr_player.uid_counter); + len = EVT_ADDR_PLAYER_CHANGE_RSP_LENGTH; + break; + + case AVRC_EVT_NOW_PLAYING_CHANGE: + len = EVT_NOW_PLAYING_CHANGE_RSP_LENGTH; + break; + default: status = AVRC_STS_BAD_PARAM; AVRC_TRACE_ERROR("unknown event_id"); @@ -671,6 +689,58 @@ static tAVRC_STS avrc_bld_next_rsp (tAVRC_RSP *p_rsp, BT_HDR *p_pkt) return AVRC_STS_NO_ERROR; } +/***************************************************************************** +** +** Function avrc_bld_set_address_player_rsp +** +** Description This function builds the set address player response +** +** Returns AVRC_STS_NO_ERROR, if the response is build successfully +** Otherwise, the error code. +** +******************************************************************************/ +static tAVRC_STS avrc_bld_set_address_player_rsp(tAVRC_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + tAVRC_STS status = AVRC_STS_NO_ERROR; + + AVRC_TRACE_API(" avrc_bld_set_address_player_rsp"); + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + /* To calculate length */ + p_data = p_start + 2; + /* add fixed lenth status(1) */ + UINT16_TO_BE_STREAM(p_data, 1); + UINT8_TO_BE_STREAM(p_data, p_rsp->status); + p_pkt->len = (p_data - p_start); + return status; +} + +/***************************************************************************** +** +** Function avrc_bld_play_item_rsp +** +** Description This function builds the play item response +** +** Returns AVRC_STS_NO_ERROR, if the response is build successfully +** Otherwise, the error code. +** +******************************************************************************/ +static tAVRC_STS avrc_bld_play_item_rsp(tAVRC_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + tAVRC_STS status = AVRC_STS_NO_ERROR; + + AVRC_TRACE_API(" avrc_bld_play_item_rsp"); + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + /* To calculate length */ + p_data = p_start + 2; + /* add fixed lenth status(1) */ + UINT16_TO_BE_STREAM(p_data, 1); + UINT8_TO_BE_STREAM(p_data, p_rsp->status); + p_pkt->len = (p_data - p_start); + return status; +} + /******************************************************************************* ** ** Function avrc_bld_group_navigation_rsp @@ -699,6 +769,256 @@ tAVRC_STS avrc_bld_group_navigation_rsp (UINT16 navi_id, BT_HDR *p_pkt) return AVRC_STS_NO_ERROR; } +/***************************************************************************** +** +** Function avrc_bld_folder_item_values_rsp +** +** Description This function builds the folder item response. +** +** Returns AVRC_STS_NO_ERROR,if the response is built successfully +** otherwise error code +** +******************************************************************************/ +static tAVRC_STS avrc_bld_folder_item_values_rsp(tAVRC_GET_ITEMS_RSP *p_rsp, BT_HDR *p_pkt ) +{ + UINT8 *p_data, *p_start, *p_length, *p_media_element_len; + UINT8 *item_length; + UINT16 itemlength, param_length; + UINT16 length = 0, item_numb = 0, i, xx, media_attr_count; + + AVRC_TRACE_DEBUG(" avrc_bld_folder_item_values_rsp offset :x%x", p_pkt->offset); + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + /* As per AVRCP spec, first byte of response is PDU ID + * and Response does not have any opcode + */ + p_data = p_start; + /*First OCT carry PDU information */ + *p_data++ = p_rsp->pdu; + + /* Refer Media Play list AVRCP 1.5 22.19 (Get Folder Items) + * Mark a pointer to be filled once length is calculated at last + */ + p_length = p_data; + + /*increment to adjust length*/ + p_data = p_data + 2; + /*Status is checked in Btif layer*/ + *p_data++ = p_rsp->status; + if(p_rsp->status != AVRC_STS_NO_ERROR) + { + //TODO Response + AVRC_TRACE_ERROR(" ### Folder_item_values response error"); + return p_rsp->status; + } + /*UID Counter OCT 4 and 5*/ + UINT16_TO_BE_STREAM(p_data, p_rsp->uid_counter); + /*Number of Items OCT 6 and 7*/ + item_numb = p_rsp->item_count; + UINT16_TO_BE_STREAM(p_data, p_rsp->item_count); + param_length = 0; + for (i = 0; i < item_numb; i++) + { + itemlength = 0; + switch(p_rsp->p_item_list[i].item_type) + { + case AVRC_ITEM_PLAYER: + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].item_type); + itemlength = 28 + p_rsp->p_item_list[i].u.player.name.str_len; + UINT16_TO_BE_STREAM(p_data, itemlength); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.player_id); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.major_type); + UINT32_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.sub_type); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.play_status); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.features,\ + AVRC_FEATURE_MASK_SIZE); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.name.charset_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.name.str_len); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.player.name.p_str,\ + p_rsp->p_item_list[i].u.player.name.str_len); + break; + case AVRC_ITEM_FOLDER: + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].item_type); + itemlength = 14 + p_rsp->p_item_list[i].u.folder.name.str_len; + UINT16_TO_BE_STREAM(p_data, itemlength); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.folder.uid ,AVRC_UID_SIZE); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.folder.type); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.folder.playable); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.folder.name.charset_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.folder.name.str_len); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.folder.name.p_str,\ + p_rsp->p_item_list[i].u.folder.name.str_len); + break; + case AVRC_ITEM_MEDIA: + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].item_type); + p_media_element_len = p_data; + itemlength = 13 + p_rsp->p_item_list[i].u.media.name.str_len; + p_data = p_data + 2; /* for length */ + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.media.uid ,AVRC_UID_SIZE); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.media.type); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.media.name.charset_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.media.name.str_len); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.media.name.p_str,\ + p_rsp->p_item_list[i].u.media.name.str_len); + UINT8_TO_BE_STREAM(p_data, p_rsp->p_item_list[i].u.media.attr_count); + media_attr_count = p_rsp->p_item_list[i].u.media.attr_count; + itemlength += 1; /* for attribute count */ + for (xx = 0; xx < media_attr_count; xx++) + { + itemlength += 8 + p_rsp->p_item_list[i].u.media.p_attr_list[xx].name.str_len; + UINT32_TO_BE_STREAM(p_data,\ + p_rsp->p_item_list[i].u.media.p_attr_list[xx].attr_id); + UINT16_TO_BE_STREAM(p_data,\ + p_rsp->p_item_list[i].u.media.p_attr_list[xx].name.charset_id); + UINT16_TO_BE_STREAM(p_data,\ + p_rsp->p_item_list[i].u.media.p_attr_list[xx].name.str_len); + ARRAY_TO_BE_STREAM(p_data,\ + p_rsp->p_item_list[i].u.media.p_attr_list[xx].name.p_str,\ + p_rsp->p_item_list[i].u.media.p_attr_list[xx].name.str_len); + } + UINT16_TO_BE_STREAM(p_media_element_len, itemlength); + break; + } + param_length += itemlength + 3; /* 3 to accommodate item_len and item_type */ + } + param_length = param_length + 5; //Add explicit 5, 2 num items+ 2UID Counter + 1 status counter + UINT16_TO_BE_STREAM(p_length, param_length); + p_pkt->len = p_data - p_start; + return AVRC_STS_NO_ERROR; +} + +/************************************************************************************** +** +** Function avrc_bld_change_path_rsp +** +** Description +** +** Returns +** +************************************************************************************/ +static tAVRC_STS avrc_bld_change_path_rsp (tAVRC_CHG_PATH_RSP *p_rsp, BT_HDR *p_pkt ) +{ + UINT8 *p_data, *p_start; + UINT16 param_len; /* parameter length feild of Rsp */ + UINT8 folder_index = 0; + + + AVRC_TRACE_DEBUG("avrc_bld_change_path_rsp offset :x%x", p_pkt->offset); + + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start; + UINT8_TO_BE_STREAM(p_data, p_rsp->pdu); + param_len = 5; /* refer spec */ + UINT16_TO_BE_STREAM(p_data, param_len); + UINT8_TO_BE_STREAM(p_data, p_rsp->status); + UINT32_TO_BE_STREAM(p_data, p_rsp->num_items); + p_pkt->len = p_data - p_start; + AVRC_TRACE_DEBUG("length = %d",p_pkt->len); + return AVRC_STS_NO_ERROR ; +} + +/************************************************************************************** +** +** Function avrc_bld_set_browse_player_rsp +** +** Description +** +** Returns +** +************************************************************************************/ +static tAVRC_STS avrc_bld_set_browse_player_rsp (tAVRC_SET_BR_PLAYER_RSP *p_rsp, BT_HDR *p_pkt ) +{ + UINT8 *p_data, *p_start; + UINT16 param_len_folder_name = 0; + UINT16 param_len; /* parameter length feild of Rsp */ + UINT8 folder_index = 0; + + AVRC_TRACE_DEBUG("avrc_bld_set_browse_player_rsp offset :x%x", p_pkt->offset); + + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start; + UINT8_TO_BE_STREAM(p_data, p_rsp->pdu); + + for(folder_index = 0; folder_index < p_rsp->folder_depth; folder_index++) + { + param_len_folder_name += p_rsp->p_folders[folder_index].str_len; + } + param_len = 10 + (p_rsp->folder_depth * 2) + param_len_folder_name; /* refer spec */ + + UINT16_TO_BE_STREAM(p_data, param_len); + UINT8_TO_BE_STREAM(p_data, p_rsp->status); + UINT16_TO_BE_STREAM(p_data, p_rsp->uid_counter); + UINT32_TO_BE_STREAM(p_data, p_rsp->num_items); + UINT16_TO_BE_STREAM(p_data, p_rsp->charset_id); + UINT8_TO_BE_STREAM(p_data, p_rsp->folder_depth); + + for(folder_index = 0; folder_index < p_rsp->folder_depth; folder_index++) + { + UINT16_TO_BE_STREAM(p_data, p_rsp->p_folders[folder_index].str_len); + ARRAY_TO_BE_STREAM(p_data,p_rsp->p_folders[folder_index].p_str, \ + p_rsp->p_folders[folder_index].str_len); + } + p_pkt->len = p_data - p_start; + AVRC_TRACE_DEBUG("length = %d",p_pkt->len); + return AVRC_STS_NO_ERROR ; +} + +/******************************************************************************* +** +** Function avrc_bld_get_item_attrs_rsp +** +** Description This function builds the Get Item Attributes +** response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_get_item_attrs_rsp (tAVRC_GET_ATTRS_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + UINT16 param_len; + UINT8 xx; + + AVRC_TRACE_API("avrc_bld_get_item_attrs_rsp"); + if (!p_rsp->p_attr_list) + { + AVRC_TRACE_ERROR("avrc_bld_get_item_attrs_rsp NULL parameter"); + return AVRC_STS_BAD_PARAM; + } + + /* get the existing length, if any, and also the num attributes */ + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start; + UINT8_TO_BE_STREAM(p_data, p_rsp->pdu); + + param_len = 2; /* for status and num_attr*/ + for(xx = 0; xx < p_rsp->attr_count; xx++) + { + /* 8 for attr_id, char_set_id, attr_value_len */ + param_len = param_len + 8 + p_rsp->p_attr_list[xx].name.str_len; + } + AVRC_TRACE_API(" param_len = %d ", param_len); + UINT16_TO_BE_STREAM(p_data, param_len); + UINT8_TO_BE_STREAM(p_data, p_rsp->status); + UINT8_TO_BE_STREAM(p_data, p_rsp->attr_count); + + for (xx=0; xx < p_rsp->attr_count; xx++) + { + if ( !p_rsp->p_attr_list[xx].name.p_str ) + { + p_rsp->p_attr_list[xx].name.str_len = 0; + } + UINT32_TO_BE_STREAM(p_data, p_rsp->p_attr_list[xx].attr_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_attr_list[xx].name.charset_id); + UINT16_TO_BE_STREAM(p_data, p_rsp->p_attr_list[xx].name.str_len); + ARRAY_TO_BE_STREAM(p_data, p_rsp->p_attr_list[xx].name.p_str, \ + p_rsp->p_attr_list[xx].name.str_len); + } + p_pkt->len = (p_data - p_start); + return AVRC_STS_NO_ERROR; +} + + /******************************************************************************* ** ** Function avrc_bld_rejected_rsp @@ -728,6 +1048,34 @@ static tAVRC_STS avrc_bld_rejected_rsp( tAVRC_RSP *p_rsp, BT_HDR *p_pkt ) /******************************************************************************* ** +** Function avrc_bld_browse_rejected_rsp +** +** Description This function builds the Browse Reject response. +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +static tAVRC_STS avrc_bld_browse_rejected_rsp (tAVRC_RSP *p_rsp, BT_HDR *p_pkt) +{ + UINT8 *p_data, *p_start; + + AVRC_TRACE_API("avrc_bld_browse_rejected_rsp: status=%d, pdu:x%x, offset=%d", p_rsp->status, + p_rsp->pdu, p_pkt->offset); + + p_start = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + p_data = p_start; + *p_data++ = p_rsp->pdu; + + UINT16_TO_BE_STREAM(p_data, 1); //Parameter length + UINT8_TO_BE_STREAM(p_data, p_rsp->status); + p_pkt->len = p_data - p_start; + AVRC_TRACE_DEBUG("Browse rejected rsp length=%d", p_pkt->len); + return AVRC_STS_NO_ERROR; +} + +/******************************************************************************* +** ** Function avrc_bld_init_rsp_buffer ** ** Description This function initializes the response buffer based on PDU @@ -902,6 +1250,14 @@ tAVRC_STS AVRC_BldResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */ status = avrc_bld_next_rsp(&p_rsp->abort, p_pkt); break; + + case AVRC_PDU_SET_ADDRESSED_PLAYER: /*PDU 0x60*/ + status = avrc_bld_set_address_player_rsp(&p_rsp->addr_player, p_pkt); + break; + + case AVRC_PDU_PLAY_ITEM: + status = avrc_bld_play_item_rsp(&p_rsp->play_item, p_pkt); + break; } if (alloc && (status != AVRC_STS_NO_ERROR) ) @@ -913,5 +1269,110 @@ tAVRC_STS AVRC_BldResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt return status; } +/******************************************************************************* +** +** Function avrc_bld_init_browse_rsp_buffer +** +** Description This function initializes the response 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_browse_rsp_buffer(tAVRC_RESPONSE *p_rsp) +{ + UINT16 offset = AVCT_BROWSE_OFFSET; + UINT16 chnl = AVCT_DATA_BROWSE; + UINT16 len = AVRC_BROWSE_POOL_SIZE; + BT_HDR *p_pkt = NULL; + + AVRC_TRACE_API("avrc_bld_init_browse_rsp_buffer "); + /* allocate and initialize the buffer */ + p_pkt = (BT_HDR *)GKI_getbuf(len); + + if (p_pkt != NULL) + { + p_pkt->layer_specific = chnl; + p_pkt->event = AVRC_OP_BROWSE; /* Browsing Opcode */ + p_pkt->offset = offset; + } + else + { + AVRC_TRACE_ERROR("### browse_rsp_buffer BUFF not allocated"); + } + return p_pkt; +} + +/******************************************************************************* +** +** Function AVRC_BldBrowseResponse +** +** Description This function builds the given AVRCP response to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +tAVRC_STS AVRC_BldBrowseResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt) +{ + tAVRC_STS status = AVRC_STS_BAD_PARAM; + BT_HDR *p_pkt; + BOOLEAN alloc = FALSE; + + if (!p_rsp || !pp_pkt) + { + AVRC_TRACE_ERROR("### BldResponse error p_rsp=%p, pp_pkt=%p", + p_rsp, pp_pkt); + return AVRC_STS_BAD_PARAM; + } + + if (*pp_pkt == NULL) + { + if ((*pp_pkt = avrc_bld_init_browse_rsp_buffer(p_rsp)) == NULL) + { + AVRC_TRACE_ERROR("### BldResponse: Failed to initialize response buffer"); + return AVRC_STS_INTERNAL_ERR; + } + alloc = TRUE; + } + status = AVRC_STS_NO_ERROR; + p_pkt = *pp_pkt; + + AVRC_TRACE_API("BldResponse: pdu=%x status=%x", p_rsp->rsp.pdu, p_rsp->rsp.status); + if (p_rsp->rsp.status != AVRC_STS_NO_ERROR) + { + AVRC_TRACE_ERROR("###ERROR AVRC_BldBrowseResponse"); + return (avrc_bld_browse_rejected_rsp(&p_rsp->rsp, p_pkt)); + } + + switch (p_rsp->pdu) + { + case AVRC_PDU_GET_FOLDER_ITEMS: + status = avrc_bld_folder_item_values_rsp(&p_rsp->get_items, p_pkt); + break; + + case AVRC_PDU_SET_BROWSED_PLAYER: + status = avrc_bld_set_browse_player_rsp(&p_rsp->br_player, p_pkt); + break; + + case AVRC_PDU_CHANGE_PATH: + status = avrc_bld_change_path_rsp(&p_rsp->chg_path, p_pkt); + break; + + case AVRC_PDU_GET_ITEM_ATTRIBUTES: + status = avrc_bld_get_item_attrs_rsp(&p_rsp->get_attrs, p_pkt); + break; + default : + break; + } + if (alloc && (status != AVRC_STS_NO_ERROR) ) + { + GKI_freebuf(p_pkt); + *pp_pkt = NULL; + AVRC_TRACE_ERROR("### error status:%d",status); + } + return status; +} #endif /* (AVRC_METADATA_INCLUDED == TRUE)*/ diff --git a/stack/avrc/avrc_int.h b/stack/avrc/avrc_int.h index 36bb0c5fa..feb2b0e99 100644 --- a/stack/avrc/avrc_int.h +++ b/stack/avrc/avrc_int.h @@ -42,6 +42,7 @@ /* Number of protocol elements in protocol element list. */ #define AVRC_NUM_PROTO_ELEMS 2 +#define AVRC_NUM_ADDL_PROTO_ELEMS 1 #ifndef AVRC_MIN_CMD_LEN #define AVRC_MIN_CMD_LEN 20 diff --git a/stack/avrc/avrc_pars_tg.c b/stack/avrc/avrc_pars_tg.c index 6e39c1c5a..b75ee907c 100644 --- a/stack/avrc/avrc_pars_tg.c +++ b/stack/avrc/avrc_pars_tg.c @@ -267,7 +267,31 @@ static tAVRC_STS avrc_pars_vendor_cmd(tAVRC_MSG_VENDOR *p_msg, tAVRC_COMMAND *p_ /* case AVRC_PDU_REQUEST_CONTINUATION_RSP: 0x40 */ /* case AVRC_PDU_ABORT_CONTINUATION_RSP: 0x41 */ + case AVRC_PDU_SET_ADDRESSED_PLAYER: + if (len != 2) + { + status = AVRC_STS_NOT_FOUND; + AVRC_TRACE_ERROR("AVRC_PDU_SET_ADDRESSED_PLAYER: bad len"); + } + else + { + BE_STREAM_TO_UINT16 (p_result->addr_player.player_id, p); + } + break; + case AVRC_PDU_PLAY_ITEM: + if (len != 11) + { + status = AVRC_STS_NOT_FOUND; + AVRC_TRACE_ERROR("AVRC_PDU_PLAY_ITEM: bad len"); + } + else + { + BE_STREAM_TO_UINT8 (p_result->play_item.scope, p); + BE_STREAM_TO_UINT64(p_result->play_item.uid, p); + BE_STREAM_TO_UINT16 (p_result->play_item.uid_counter, p); + } + break; default: status = AVRC_STS_BAD_CMD; break; diff --git a/stack/avrc/avrc_sdp.c b/stack/avrc/avrc_sdp.c index ba10c1c5a..96582110b 100644 --- a/stack/avrc/avrc_sdp.c +++ b/stack/avrc/avrc_sdp.c @@ -27,6 +27,7 @@ #include "avrc_api.h" #include "avrc_int.h" + #ifndef SDP_AVRCP_1_4 #define SDP_AVRCP_1_4 FALSE #endif @@ -49,6 +50,9 @@ const tSDP_PROTOCOL_ELEM avrc_proto_list [] = #if SDP_AVCTP_1_4 == TRUE {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_4, 0} } #else +#if SDP_AVRCP_1_5 == TRUE + {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_4, 0} } +#else #if SDP_AVRCP_1_4 == TRUE {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0} } #else @@ -59,8 +63,18 @@ const tSDP_PROTOCOL_ELEM avrc_proto_list [] = #endif #endif #endif +#endif }; +#if SDP_AVRCP_1_5 == 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_4, 0} }}} +}; +#else #if SDP_AVRCP_1_4 == TRUE const tSDP_PROTO_LIST_ELEM avrc_add_proto_list [] = { @@ -70,6 +84,7 @@ const tSDP_PROTO_LIST_ELEM avrc_add_proto_list [] = {UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0} }}} }; #endif +#endif /****************************************************************************** @@ -242,6 +257,13 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, count = 2; } #else +#if SDP_AVRCP_1_5 == TRUE + if( service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL ) + { + class_list[1] = UUID_SERVCLASS_AV_REM_CTRL_CONTROL; + count = 2; + } +#else #if SDP_AVRCP_1_4 == TRUE if( service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL ) { @@ -250,24 +272,35 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, } #endif #endif +#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 SDP_AVRCP_1_5 == TRUE + /* additional protocol list to include browsing channel */ + result &= SDP_AddAdditionProtoLists( sdp_handle, AVRC_NUM_ADDL_PROTO_ELEMS, + (tSDP_PROTO_LIST_ELEM *)avrc_add_proto_list); + + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5); +#else #if SDP_AVRCP_1_4 == 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); + result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5); #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 +#endif /* add supported categories */ p = temp; diff --git a/stack/avrc/avrc_utils.c b/stack/avrc/avrc_utils.c index 432ecc0cd..9c9082f79 100644 --- a/stack/avrc/avrc_utils.c +++ b/stack/avrc/avrc_utils.c @@ -59,6 +59,8 @@ BOOLEAN AVRC_IsValidAvcType(UINT8 pdu_id, UINT8 avc_type) case AVRC_PDU_INFORM_BATTERY_STAT_OF_CT: /* 0x18 */ case AVRC_PDU_REQUEST_CONTINUATION_RSP: /* 0x40 */ case AVRC_PDU_ABORT_CONTINUATION_RSP: /* 0x41 */ + case AVRC_PDU_SET_ADDRESSED_PLAYER: + case AVRC_PDU_PLAY_ITEM: if (avc_type == AVRC_CMD_CTRL) result=TRUE; break; diff --git a/stack/include/avct_api.h b/stack/include/avct_api.h index c2713fdb4..5feb8c25d 100644 --- a/stack/include/avct_api.h +++ b/stack/include/avct_api.h @@ -271,6 +271,22 @@ extern UINT16 AVCT_GetPeerMtu (UINT8 handle); *******************************************************************************/ extern UINT16 AVCT_MsgReq(UINT8 handle, UINT8 label, UINT8 cr, BT_HDR *p_msg); + +/******************************************************************************* +** +** Function avct_get_peer_addr_by_ccb +** +** +** Description Return peer BD address on ccb index (or handle). +** +** +** +** +** Returns BD Address. +** +*******************************************************************************/ +extern BOOLEAN avct_get_peer_addr_by_ccb (UINT8 idx, BD_ADDR addr); + #ifdef __cplusplus } #endif diff --git a/stack/include/avrc_api.h b/stack/include/avrc_api.h index 2198074d9..12601ad47 100644 --- a/stack/include/avrc_api.h +++ b/stack/include/avrc_api.h @@ -608,6 +608,21 @@ extern tAVRC_STS AVRC_BldCommand( tAVRC_COMMAND *p_cmd, BT_HDR **pp_pkt); *******************************************************************************/ extern tAVRC_STS AVRC_BldResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt); +/******************************************************************************* +** +** Function AVRC_BldBrowseResponse +** +** Description This function builds the given AVRCP response to the given +** GKI buffer +** +** Returns AVRC_STS_NO_ERROR, if the response is built successfully +** Otherwise, the error code. +** +*******************************************************************************/ +tAVRC_STS AVRC_BldBrowseResponse( UINT8 handle, tAVRC_RESPONSE *p_rsp, BT_HDR **pp_pkt); + + + /************************************************************************** ** ** Function AVRC_IsValidAvcType diff --git a/stack/include/avrc_defs.h b/stack/include/avrc_defs.h index d6ff8a0b5..7d63a47b1 100644 --- a/stack/include/avrc_defs.h +++ b/stack/include/avrc_defs.h @@ -32,6 +32,7 @@ #define AVRC_REV_1_0 0x0100 #define AVRC_REV_1_3 0x0103 #define AVRC_REV_1_4 0x0104 +#define AVRC_REV_1_5 0x0105 #define AVRC_PACKET_LEN 512 /* Per the spec, you must support 512 byte RC packets */ @@ -839,7 +840,7 @@ typedef union #define AVRC_IS_VALID_CAP_ID(a) (((a == AVRC_CAP_COMPANY_ID) || (a == AVRC_CAP_EVENTS_SUPPORTED)) ? TRUE : FALSE) #define AVRC_IS_VALID_EVENT_ID(a) (((a >= AVRC_EVT_PLAY_STATUS_CHANGE) && \ - (a <= AVRC_EVT_APP_SETTING_CHANGE)) ? TRUE : FALSE) + (a <= AVRC_EVT_ADDR_PLAYER_CHANGE)) ? TRUE : FALSE) #define AVRC_IS_VALID_ATTRIBUTE(a) (((((a > 0) && a <= AVRC_PLAYER_SETTING_SCAN)) || \ (a >= AVRC_PLAYER_SETTING_LOW_MENU_EXT)) ? TRUE : FALSE) @@ -1095,7 +1096,7 @@ typedef struct UINT32 start_item; UINT32 end_item; UINT8 attr_count; - UINT32 *p_attr_list; + UINT32 attrs[AVRC_MAX_ELEM_ATTR_SIZE]; } tAVRC_GET_ITEMS_CMD; /* ChangePath */ @@ -1106,7 +1107,7 @@ typedef struct UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ UINT16 uid_counter; UINT8 direction; - tAVRC_UID folder_uid; + UINT64 folder_uid; } tAVRC_CHG_PATH_CMD; /* GetItemAttrs */ @@ -1116,10 +1117,10 @@ typedef struct tAVRC_STS status; UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ UINT8 scope; - tAVRC_UID uid; + UINT64 uid; UINT16 uid_counter; UINT8 attr_count; - UINT32 *p_attr_list; + UINT32 attrs[AVRC_MAX_ELEM_ATTR_SIZE]; } tAVRC_GET_ATTRS_CMD; /* Search */ @@ -1138,7 +1139,7 @@ typedef struct tAVRC_STS status; UINT8 opcode; /* Op Code (assigned by AVRC_BldCommand according to pdu) */ UINT8 scope; - tAVRC_UID uid; + UINT64 uid; UINT16 uid_counter; } tAVRC_PLAY_ITEM_CMD; @@ -1301,6 +1302,7 @@ typedef union tAVRC_ADDR_PLAYER_PARAM addr_player; UINT16 uid_counter; UINT8 volume; + UINT8 evt;//For Available Player Changed Notification } tAVRC_NOTIF_RSP_PARAM; /* RegNotify */ diff --git a/stack/include/bt_types.h b/stack/include/bt_types.h index 85d41bf47..4ea447eca 100644 --- a/stack/include/bt_types.h +++ b/stack/include/bt_types.h @@ -291,6 +291,11 @@ typedef struct #define BE_STREAM_TO_UINT16(u16, p) {u16 = (UINT16)(((UINT16)(*(p)) << 8) + (UINT16)(*((p) + 1))); (p) += 2;} #define BE_STREAM_TO_UINT24(u32, p) {u32 = (((UINT32)(*((p) + 2))) + ((UINT32)(*((p) + 1)) << 8) + ((UINT32)(*(p)) << 16)); (p) += 3;} #define BE_STREAM_TO_UINT32(u32, p) {u32 = ((UINT32)(*((p) + 3)) + ((UINT32)(*((p) + 2)) << 8) + ((UINT32)(*((p) + 1)) << 16) + ((UINT32)(*(p)) << 24)); (p) += 4;} +#define BE_STREAM_TO_UINT64(u64, p) {u64 = ((UINT64)(*((p) + 7)) + ((UINT64)(*((p) + 6)) << 8) + \ + ((UINT64)(*((p) + 5)) << 16) + ((UINT64)(*((p) + 4)) << 24) + \ + ((UINT64)(*((p) + 3)) << 32) + ((UINT64)(*((p) + 2)) << 40) + \ + ((UINT64)(*((p) + 1)) << 48) + ((UINT64)(*(p)) << 56)); \ + (p) += 8;} #define BE_STREAM_TO_ARRAY(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) ((UINT8 *) a)[ijk] = *p++;} diff --git a/stack/include/sdp_api.h b/stack/include/sdp_api.h index ff44bf5f6..98bdfafe4 100644 --- a/stack/include/sdp_api.h +++ b/stack/include/sdp_api.h @@ -729,6 +729,20 @@ BOOLEAN SDP_ConnClose (UINT32 handle); *******************************************************************************/ BOOLEAN SDP_FindServiceUUIDInRec(tSDP_DISC_REC *p_rec, tBT_UUID *p_uuid); +/********************************************************************************* +** +** Function SDP_Dev_Blacklisted_For_Avrcp15 +** +** Description This function is called to know is local Avrcp Version +** 1.3 as local Avrcp version is send as 1.3 for black listed +** devices +** +** Returns TRUE if AVRCP local Avrcp Version 1.3 else FALSE +** +********************************************************************************/ +BOOLEAN SDP_Dev_Blacklisted_For_Avrcp15 (BD_ADDR addr); + + #ifdef __cplusplus } #endif diff --git a/stack/sdp/sdp_api.c b/stack/sdp/sdp_api.c index 6e84ded80..ee41ff272 100644 --- a/stack/sdp/sdp_api.c +++ b/stack/sdp/sdp_api.c @@ -1270,3 +1270,18 @@ UINT8 SDP_SetTraceLevel (UINT8 new_level) return(sdp_cb.trace_level); } + +/**************************************************************************** +** +** Function SDP_Dev_Blacklisted_For_Avrcp15 +** +** Description This function is called to check if Remote device +** is blacklisted for Avrcp version. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN SDP_Dev_Blacklisted_For_Avrcp15 (BD_ADDR addr) +{ + return sdp_dev_blacklisted_for_avrcp15 (addr); +} diff --git a/stack/sdp/sdp_server.c b/stack/sdp/sdp_server.c index 5dd6b92c3..73715fc8f 100644 --- a/stack/sdp/sdp_server.c +++ b/stack/sdp/sdp_server.c @@ -46,6 +46,26 @@ #define SDP_MAX_SERVICE_RSPHDR_LEN 12 #define SDP_MAX_SERVATTR_RSPHDR_LEN 10 #define SDP_MAX_ATTR_RSPHDR_LEN 10 +#define AVRCP_VERSION_POSITION 7 +#define SDP_AVRCP_PROFILE_DESC_LENGTH 8 +#define AVRCP_SUPPORTED_FEATURES_POSITION 1 +#define AVRCP_BROWSE_SUPPORT_BITMASK 0x40 + +/* Few remote device does not understand AVRCP version greater + * than 1.3 and falls back to 1.0, we would like to blacklist + * and send AVRCP versio as 1.3. + */ +static const UINT8 sdp_black_list_prefix[][3] = {{0x00, 0x1D, 0xBA}, /* JVC carkit */ + {0x64, 0xD4, 0xBD}, /* Honda handsfree carkit */ + {0x00, 0x06, 0xF7}, /* Denso carkit */ + {0x00, 0x1E, 0xB2}, /* AVN 3.0 Hyundai*/ + {0x00, 0x0E, 0x9F}, /* Porshe car kit */ + {0x00, 0x13, 0x7B}, /* BYOM Opel*/ + {0x68, 0x84, 0x70}, /* KIA MOTOR*/ + {0x00, 0x54, 0xAF}, /* Chrysler*/ + {0x04, 0x88, 0xE2}, /* BeatsStudio Wireless*/ + {0xA0, 0x14, 0x3D}, /* VW Sharen*/ + {0xE0, 0x75, 0x0A} /* VW GOLF*/}; /********************************************************************************/ /* L O C A L F U N C T I O N P R O T O T Y P E S */ @@ -101,6 +121,92 @@ static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, #define SDP_TEXT_BAD_MAX_RECORDS_LIST NULL #endif +/**************************************************************************** +** +** Function sdp_dev_blacklisted_for_avrcp15 +** +** Description This function is called to check if Remote device +** is blacklisted for Avrcp version. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN sdp_dev_blacklisted_for_avrcp15 (BD_ADDR addr) +{ + int blacklistsize = 0; + int i =0; + + blacklistsize = sizeof(sdp_black_list_prefix)/sizeof(sdp_black_list_prefix[0]); + for (i=0; i < blacklistsize; i++) + { + if (0 == memcmp(sdp_black_list_prefix[i], addr, 3)) + { + SDP_TRACE_ERROR("SDP Avrcp Version Black List Device"); + return TRUE; + } + } + return FALSE; +} + +/************************************************************************************* +** +** Function sdp_fallback_avrcp_version +** +** Description Checks if UUID is AV Remote Control, attribute id +** is Profile descriptor list and remote BD address +** matches device blacklist, change Avrcp version to 1.3 +** +** Returns BOOLEAN +** +***************************************************************************************/ +BOOLEAN sdp_fallback_avrcp_version (tSDP_ATTRIBUTE *p_attr, BD_ADDR remote_address) +{ + if ((p_attr->id == ATTR_ID_BT_PROFILE_DESC_LIST) && + (p_attr->len >= SDP_AVRCP_PROFILE_DESC_LENGTH)) + { + /* As per current DB implementation UUID is condidered as 16 bit */ + if (((p_attr->value_ptr[3] << 8) | (p_attr->value_ptr[4])) == + UUID_SERVCLASS_AV_REMOTE_CONTROL) + { + if (sdp_dev_blacklisted_for_avrcp15 (remote_address)) + { + p_attr->value_ptr[AVRCP_VERSION_POSITION] = 0x03; // Update AVRCP version as 1.3 + SDP_TRACE_ERROR("SDP Change AVRCP Version = 0x%x", + p_attr->value_ptr[AVRCP_VERSION_POSITION]); + return TRUE; + } + } + } + return FALSE; +} + +/************************************************************************************* +** +** Function sdp_reset_avrcp_browsing_bit +** +** Description Checks if Service Class ID is AV Remote Control TG, attribute id +** is Supported features and remote BD address +** matches device blacklist, reset Browsing Bit +** +** Returns BOOLEAN +** +***************************************************************************************/ +BOOLEAN sdp_reset_avrcp_browsing_bit (tSDP_ATTRIBUTE attr, tSDP_ATTRIBUTE *p_attr, +BD_ADDR remote_address) +{ + if ((p_attr->id == ATTR_ID_SUPPORTED_FEATURES) && (attr.id == ATTR_ID_SERVICE_CLASS_ID_LIST) && + (((attr.value_ptr[1] << 8) | (attr.value_ptr[2])) == UUID_SERVCLASS_AV_REM_CTRL_TARGET)) + { + if (sdp_dev_blacklisted_for_avrcp15 (remote_address)) + { + SDP_TRACE_ERROR("Reset Browse feature bitmask"); + p_attr->value_ptr[AVRCP_SUPPORTED_FEATURES_POSITION] &= ~AVRCP_BROWSE_SUPPORT_BITMASK; + return TRUE; + } + } + return FALSE; +} + /******************************************************************************* ** ** Function sdp_server_handle_client_req @@ -328,6 +434,8 @@ static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, tSDP_ATTRIBUTE *p_attr; BT_HDR *p_buf; BOOLEAN is_cont = FALSE; + BOOLEAN is_avrcp_fallback = FALSE; + BOOLEAN is_avrcp_browse_bit_reset = FALSE; UINT16 attr_len; /* Extract the record handle */ @@ -436,6 +544,12 @@ static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, if (p_attr) { +#if SDP_AVRCP_1_5 == TRUE + /* Check for UUID Remote Control and Remote BD address */ + is_avrcp_fallback = sdp_fallback_avrcp_version (p_attr, p_ccb->device_address); + is_avrcp_browse_bit_reset = sdp_reset_avrcp_browsing_bit( + p_rec->attribute[1], p_attr, p_ccb->device_address); +#endif /* Check if attribute fits. Assume 3-byte value type/length */ rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); @@ -488,8 +602,36 @@ static void process_service_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, xx--; } + if (is_avrcp_fallback) + { + /* Update AVRCP version back to 1.5 */ + p_attr->value_ptr[AVRCP_VERSION_POSITION] = 0x05; + is_avrcp_fallback = FALSE; + } + if (is_avrcp_browse_bit_reset) + { + /* Restore Browsing bit */ + SDP_TRACE_ERROR("Restore Browsing bit"); + p_attr->value_ptr[AVRCP_SUPPORTED_FEATURES_POSITION] + |= AVRCP_BROWSE_SUPPORT_BITMASK; + is_avrcp_browse_bit_reset = FALSE; + } } } + if (is_avrcp_fallback) + { + /* Update AVRCP version back to 1.5 */ + p_attr->value_ptr[AVRCP_VERSION_POSITION] = 0x05; + is_avrcp_fallback = FALSE; + } + if (is_avrcp_browse_bit_reset) + { + /* Restore Browsing bit */ + SDP_TRACE_ERROR("Restore Browsing bit"); + p_attr->value_ptr[AVRCP_SUPPORTED_FEATURES_POSITION] + |= AVRCP_BROWSE_SUPPORT_BITMASK; + is_avrcp_browse_bit_reset = FALSE; + } /* If all the attributes have been accomodated in p_rsp, reset next_attr_index */ if (xx == attr_seq.num_attr) @@ -595,6 +737,8 @@ static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, tSDP_ATTRIBUTE *p_attr; BT_HDR *p_buf; BOOLEAN maxxed_out = FALSE, is_cont = FALSE; + BOOLEAN is_avrcp_fallback = FALSE; + BOOLEAN is_avrcp_browse_bit_reset = FALSE; UINT8 *p_seq_start; UINT16 seq_len, attr_len; UNUSED(p_req_end); @@ -719,6 +863,12 @@ static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, if (p_attr) { +#if SDP_AVRCP_1_5 == TRUE + /* Check for UUID Remote Control and Remote BD address */ + is_avrcp_fallback = sdp_fallback_avrcp_version (p_attr, p_ccb->device_address); + is_avrcp_browse_bit_reset = sdp_reset_avrcp_browsing_bit( + p_rec->attribute[1], p_attr, p_ccb->device_address); +#endif /* Check if attribute fits. Assume 3-byte value type/length */ rem_len = max_list_len - (INT16) (p_rsp - &p_ccb->rsp_list[0]); @@ -776,8 +926,36 @@ static void process_service_search_attr_req (tCONN_CB *p_ccb, UINT16 trans_num, xx--; } + if (is_avrcp_fallback) + { + /* Update AVRCP version back to 1.5 */ + p_attr->value_ptr[AVRCP_VERSION_POSITION] = 0x05; + is_avrcp_fallback = FALSE; + } + if (is_avrcp_browse_bit_reset) + { + /* Restore Browsing bit */ + SDP_TRACE_ERROR("Restore Browsing bit"); + p_attr->value_ptr[AVRCP_SUPPORTED_FEATURES_POSITION] + |= AVRCP_BROWSE_SUPPORT_BITMASK; + is_avrcp_browse_bit_reset = FALSE; + } } } + if (is_avrcp_fallback) + { + /* Update AVRCP version back to 1.5 */ + p_attr->value_ptr[AVRCP_VERSION_POSITION] = 0x05; + is_avrcp_fallback = FALSE; + } + if (is_avrcp_browse_bit_reset) + { + /* Restore Browsing bit */ + SDP_TRACE_ERROR("Restore Browsing bit"); + p_attr->value_ptr[AVRCP_SUPPORTED_FEATURES_POSITION] + |= AVRCP_BROWSE_SUPPORT_BITMASK; + is_avrcp_browse_bit_reset = FALSE; + } /* Go back and put the type and length into the buffer */ if (p_ccb->cont_info.last_attr_seq_desc_sent == FALSE) diff --git a/stack/sdp/sdpint.h b/stack/sdp/sdpint.h index 262ac89f0..edb1412a2 100644 --- a/stack/sdp/sdpint.h +++ b/stack/sdp/sdpint.h @@ -310,6 +310,8 @@ extern void sdp_server_handle_client_req (tCONN_CB *p_ccb, BT_HDR *p_msg); #define sdp_server_handle_client_req(p_ccb, p_msg) #endif +extern BOOLEAN sdp_dev_blacklisted_for_avrcp15 (BD_ADDR addr); + /* Functions provided by sdp_discovery.c */ #if SDP_CLIENT_ENABLED == TRUE |