feat(bt/bluedroid): Support AVRCP Cover Art feature

This commit is contained in:
linruihao 2024-08-09 16:30:17 +08:00 committed by BOT
parent d7298a71c3
commit cb0d073551
24 changed files with 1596 additions and 51 deletions

View File

@ -1,4 +1,4 @@
[codespell]
skip = build,*.yuv,components/fatfs/src/*,alice.txt,*.rgb,components/wpa_supplicant/*,components/esp_wifi/*,*.pem
ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,wel,ot,fane,assertIn
ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,wel,ot,fane,assertIn,registr
write-changes = true

View File

@ -156,6 +156,8 @@ if(CONFIG_BT_ENABLED)
host/bluedroid/stack/avdt/include
host/bluedroid/stack/a2dp/include
host/bluedroid/stack/rfcomm/include
host/bluedroid/stack/obex/include
host/bluedroid/stack/goep/include
host/bluedroid/stack/include
host/bluedroid/common/include
host/bluedroid/config/include)
@ -183,6 +185,8 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/bta/av/bta_av_aact.c"
"host/bluedroid/bta/av/bta_av_act.c"
"host/bluedroid/bta/av/bta_av_api.c"
"host/bluedroid/bta/av/bta_av_ca_act.c"
"host/bluedroid/bta/av/bta_av_ca_sm.c"
"host/bluedroid/bta/av/bta_av_cfg.c"
"host/bluedroid/bta/av/bta_av_ci.c"
"host/bluedroid/bta/av/bta_av_main.c"
@ -378,6 +382,8 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/stack/gatt/gatt_sr.c"
"host/bluedroid/stack/gatt/gatt_sr_hash.c"
"host/bluedroid/stack/gatt/gatt_utils.c"
"host/bluedroid/stack/goep/goepc_api.c"
"host/bluedroid/stack/goep/goepc_main.c"
"host/bluedroid/stack/hcic/hciblecmds.c"
"host/bluedroid/stack/hcic/hcicmds.c"
"host/bluedroid/stack/l2cap/l2c_api.c"
@ -389,6 +395,9 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/stack/l2cap/l2c_ucd.c"
"host/bluedroid/stack/l2cap/l2c_utils.c"
"host/bluedroid/stack/l2cap/l2cap_client.c"
"host/bluedroid/stack/obex/obex_api.c"
"host/bluedroid/stack/obex/obex_main.c"
"host/bluedroid/stack/obex/obex_tl_l2cap.c"
"host/bluedroid/stack/rfcomm/port_api.c"
"host/bluedroid/stack/rfcomm/port_rfc.c"
"host/bluedroid/stack/rfcomm/port_utils.c"

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -232,6 +232,145 @@ esp_err_t esp_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code, uint8_t
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_cover_art_connect(uint16_t mtu)
{
if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) ||
(!btc_avrc_ct_connected_p())) {
return ESP_ERR_INVALID_STATE;
}
if (!btc_avrc_ct_check_cover_art_support()) {
return ESP_ERR_NOT_SUPPORTED;
}
if (mtu > ESP_AVRC_CA_MTU_MAX || mtu < ESP_AVRC_CA_MTU_MIN) {
mtu = ESP_AVRC_CA_MTU_MAX;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CT_API_COVER_ART_CONNECT_EVT;
btc_avrc_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_args_t));
arg.ca_conn.mtu = mtu;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_cover_art_disconnect(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (!btc_avrc_ct_check_cover_art_support()) {
return ESP_ERR_NOT_SUPPORTED;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CT_API_COVER_ART_DISCONNECT_EVT;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_cover_art_get_image_properties(uint8_t *image_handle)
{
if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) ||
(!btc_avrc_ct_connected_p())) {
return ESP_ERR_INVALID_STATE;
}
if (!btc_avrc_ct_check_cover_art_support()) {
return ESP_ERR_NOT_SUPPORTED;
}
if (image_handle == NULL) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_PROPERTIES_EVT;
btc_avrc_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_args_t));
memcpy(arg.ca_get_img_prop.image_handle, image_handle, ESP_AVRC_CA_IMAGE_HANDLE_LEN);
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_cover_art_get_image(uint8_t *image_handle, uint8_t *image_descriptor, uint16_t image_descriptor_len)
{
if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) ||
(!btc_avrc_ct_connected_p())) {
return ESP_ERR_INVALID_STATE;
}
if (!btc_avrc_ct_check_cover_art_support()) {
return ESP_ERR_NOT_SUPPORTED;
}
if (image_handle == NULL || image_descriptor == NULL || image_descriptor_len == 0) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_EVT;
btc_avrc_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_args_t));
memcpy(arg.ca_get_img.image_handle, image_handle, ESP_AVRC_CA_IMAGE_HANDLE_LEN);
arg.ca_get_img.image_descriptor_len = image_descriptor_len;
arg.ca_get_img.image_descriptor = image_descriptor;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), btc_avrc_arg_deep_copy, btc_avrc_arg_deep_free);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_avrc_ct_cover_art_get_linked_thumbnail(uint8_t *image_handle)
{
if ((esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) ||
(!btc_avrc_ct_connected_p())) {
return ESP_ERR_INVALID_STATE;
}
if (!btc_avrc_ct_check_cover_art_support()) {
return ESP_ERR_NOT_SUPPORTED;
}
if (image_handle == NULL) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_AVRC_CT;
msg.act = BTC_AVRC_CT_API_COVER_ART_GET_LINKED_THUMBNAIL_EVT;
btc_avrc_args_t arg;
memset(&arg, 0, sizeof(btc_avrc_args_t));
memcpy(arg.ca_get_lk_thn.image_handle, image_handle, ESP_AVRC_CA_IMAGE_HANDLE_LEN);
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_avrc_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
/*********************************************************************************************/
/** following is the API of AVRCP target role **/
/*********************************************************************************************/

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -18,6 +18,10 @@ extern "C" {
#define ESP_AVRC_TRANS_LABEL_MAX 15 /*!< max transaction label */
#define ESP_AVRC_CA_IMAGE_HANDLE_LEN 7 /* The image handle length is fixed to 7, specified by Basic Image Profile */
#define ESP_AVRC_CA_MTU_MIN 255 /* Minimal MTU can be used in Cover Art OBEX connection */
#define ESP_AVRC_CA_MTU_MAX 1691 /* Maximum MTU can be used in Cover Art OBEX connection */
/// AVRC feature bit mask
typedef enum {
ESP_AVRC_FEAT_RCTG = 0x0001, /*!< remote control target */
@ -30,14 +34,18 @@ typedef enum {
/// AVRC supported features flag retrieved in SDP record
typedef enum {
/* CT and TG common features flag */
ESP_AVRC_FEAT_FLAG_CAT1 = 0x0001, /*!< category 1 */
ESP_AVRC_FEAT_FLAG_CAT2 = 0x0002, /*!< category 2 */
ESP_AVRC_FEAT_FLAG_CAT3 = 0x0004, /*!< category 3 */
ESP_AVRC_FEAT_FLAG_CAT4 = 0x0008, /*!< category 4 */
ESP_AVRC_FEAT_FLAG_BROWSING = 0x0040, /*!< browsing */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE_PROP = 0x0080, /*!< Cover Art GetImageProperties */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE = 0x0100, /*!< Cover Art GetImage */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_LINKED_THUMBNAIL = 0x0200, /*!< Cover Art GetLinkedThumbnail */
/* CT only features flag */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE_PROP = 0x0080, /*!< CT support Cover Art GetImageProperties */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE = 0x0100, /*!< CT support Cover Art GetImage */
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_LINKED_THUMBNAIL = 0x0200, /*!< CT support Cover Art GetLinkedThumbnail */
/* TG only features flag */
ESP_AVRC_FEAT_FLAG_TG_COVER_ART = 0x0100, /*!< TG support Cover Art */
} esp_avrc_feature_flag_t;
/// AVRC passthrough command code
@ -135,6 +143,8 @@ typedef enum {
ESP_AVRC_CT_REMOTE_FEATURES_EVT = 5, /*!< feature of remote device indication event */
ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT = 6, /*!< supported notification events capability of peer device */
ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT = 7, /*!< set absolute volume response event */
ESP_AVRC_CT_COVER_ART_STATE_EVT = 8, /*!< cover art client connection state changed event */
ESP_AVRC_CT_COVER_ART_DATA_EVT = 9, /*!< cover art client data event */
} esp_avrc_ct_cb_event_t;
/// AVRC Target callback events
@ -155,7 +165,8 @@ typedef enum {
ESP_AVRC_MD_ATTR_TRACK_NUM = 0x8, /*!< track position on the album */
ESP_AVRC_MD_ATTR_NUM_TRACKS = 0x10, /*!< number of tracks on the album */
ESP_AVRC_MD_ATTR_GENRE = 0x20, /*!< track genre */
ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40 /*!< total album playing time in miliseconds */
ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40, /*!< total album playing time in milliseconds */
ESP_AVRC_MD_ATTR_COVER_ART = 0x80 /*!< cover art image handle */
} esp_avrc_md_attr_mask_t;
/// AVRC event notification ids
@ -261,6 +272,12 @@ typedef enum {
ESP_AVRC_PLAYBACK_ERROR = 0xFF, /*!< error */
} esp_avrc_playback_stat_t;
/// AVRC Cover Art connection error code
typedef enum {
ESP_AVRC_COVER_ART_DISCONNECTED, /*!< Cover Art connection disconnected or connection failed */
ESP_AVRC_COVER_ART_CONNECTED, /*!< Cover Art connection established */
} esp_avrc_cover_art_conn_state_t;
/// AVRCP notification parameters
typedef union
{
@ -337,6 +354,24 @@ typedef union {
struct avrc_ct_set_volume_rsp_param {
uint8_t volume; /*!< the volume which has actually been set, range is 0 to 0x7f, means 0% to 100% */
} set_volume_rsp; /*!< set absolute volume response event */
/**
* @brief ESP_AVRC_CT_COVER_ART_STATE_EVT
*/
struct avrc_ct_cover_art_state_param {
esp_avrc_cover_art_conn_state_t state; /*!< indicate the Cover Art connection status */
esp_bt_status_t reason; /*!< the disconnect reason of Cover Art connection */
} cover_art_state; /*!< AVRC Cover Art connection state change event */
/**
* @brief ESP_AVRC_CT_COVER_ART_DATA_EVT
*/
struct avrc_ct_cover_art_data_param {
esp_bt_status_t status; /*!< indicate whether the get operation is success, p_data is valid only when status is ESP_BT_STATUS_SUCCESS */
bool final; /*!< indicate whether this data event is the final one, true if we have received the entire object */
uint16_t data_len; /*!< the data length of this data event, in bytes */
uint8_t *p_data; /*!< pointer to data, should copy to other buff before event callback return */
} cover_art_data; /*!< AVRC Cover Art data event */
} esp_avrc_ct_cb_param_t;
/// AVRC target callback parameters
@ -656,11 +691,11 @@ bool esp_avrc_psth_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_psth_b
/**
*
* @brief Get the requested event notification capabilies on local AVRC target. The capability is returned
* @brief Get the requested event notification capabilities on local AVRC target. The capability is returned
* in a bit mask representation in evt_set. This function should be called after esp_avrc_tg_init().
*
* For capability type "ESP_AVRC_RN_CAP_ALLOWED_EVT, the retrieved event set is constant and
* it covers all of the notifcation events that can possibly be supported with current
* it covers all of the notification events that can possibly be supported with current
* implementation.
*
* For capability type ESP_AVRC_RN_CAP_SUPPORTED_EVT, the event set covers the notification
@ -729,6 +764,87 @@ bool esp_avrc_rn_evt_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_rn_e
esp_err_t esp_avrc_tg_send_rn_rsp(esp_avrc_rn_event_ids_t event_id, esp_avrc_rn_rsp_t rsp,
esp_avrc_rn_param_t *param);
/**
*
* @brief Start the process to establish OBEX connection used in Cover Art Client. Once the operation done,
* ESP_AVRC_CT_COVER_ART_STATE_EVT will come, operation result can be found in event param.
*
* @param[in] mtu: MTU used in lower level connection, should not smaller than ESP_AVRC_CA_MTU_MIN or larger than
* ESP_AVRC_CA_MTU_MAX, if value is not valid, will be reset to ESP_AVRC_CA_MTU_MAX. This can limit
* the max data length in cover_art_data event.
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC CT is not initialized
* - ESP_ERR_NOT_SUPPORTED: if peer device does not support Cover Art function
*
*/
esp_err_t esp_avrc_ct_cover_art_connect(uint16_t mtu);
/**
*
* @brief Start the process to release the OBEX connection used in Cover Art Client.Once the operation done,
* ESP_AVRC_CT_COVER_ART_STATE_EVT will come, operation result can be found in event param.
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC CT is not initialized
* - ESP_ERR_NOT_SUPPORTED: if peer device does not support Cover Art function
*
*/
esp_err_t esp_avrc_ct_cover_art_disconnect(void);
/**
*
* @brief Start the process to get image properties from Cover Art server.
*
* @param[in] image_handle: pointer to image handle with a length of ESP_AVRC_CA_IMAGE_HANDLE_LEN bytes, can be freed
* after this function return
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC CT is not initialized
* - ESP_ERR_NOT_SUPPORTED: if peer device does not support Cover Art function
*
*/
esp_err_t esp_avrc_ct_cover_art_get_image_properties(uint8_t *image_handle);
/**
*
* @brief Start the process to get image from Cover Art server.
*
* @param[in] image_handle: pointer to image handle with a length of ESP_AVRC_CA_IMAGE_HANDLE_LEN bytes, can be freed
* after this function return
*
* @param[in] image_descriptor: pointer to image descriptor, will be cache internally by bluetooth stack, can be freed
* once this api return
*
* @param[in] image_descriptor_len: the length of image descriptor
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC CT is not initialized
* - ESP_ERR_NOT_SUPPORTED: if peer device does not support Cover Art function
*
*/
esp_err_t esp_avrc_ct_cover_art_get_image(uint8_t *image_handle, uint8_t *image_descriptor, uint16_t image_descriptor_len);
/**
*
* @brief Start the process to get linked thumbnail from Cover Art server.
*
* @param[in] image_handle: pointer to image handle with a length of ESP_AVRC_CA_IMAGE_HANDLE_LEN bytes, can be freed
* after this function return
*
* @return
* - ESP_OK: success
* - ESP_ERR_INVALID_STATE: if bluetooth stack is not enabled or AVRC CT is not initialized
* - ESP_ERR_NOT_SUPPORTED: if peer device does not support Cover Art function
*
*/
esp_err_t esp_avrc_ct_cover_art_get_linked_thumbnail(uint8_t *image_handle);
#ifdef __cplusplus
}
#endif

View File

@ -34,6 +34,8 @@
#include "stack/l2c_api.h"
#include "osi/allocator.h"
#include "osi/list.h"
#include "stack/goep_common.h"
#include "stack/goepc_api.h"
#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
#include "bta/bta_ar_api.h"
#endif
@ -98,6 +100,9 @@ void bta_av_del_rc(tBTA_AV_RCB *p_rcb)
p_scb = NULL;
if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) {
/* reset cover art state */
bta_av_ca_reset(p_rcb);
if (p_rcb->shdl) {
/* Validate array index*/
if ((p_rcb->shdl - 1) < BTA_AV_NUM_STRS) {
@ -1143,7 +1148,7 @@ void bta_av_conn_chg(tBTA_AV_DATA *p_data)
p_data->conn_chg.peer_addr[5]);
if (p_lcb_rc->conn_msk && bdcmp(p_lcb_rc->addr, p_data->conn_chg.peer_addr) == 0) {
/* AVRCP is already connected.
* need to update the association betwen SCB and RCB */
* need to update the association between SCB and RCB */
p_lcb_rc->conn_msk = 0; /* indicate RC ONLY is not connected */
p_lcb_rc->lidx = 0;
p_scb->rc_handle = p_cb->rc_acp_handle;
@ -1484,6 +1489,37 @@ static void bta_av_acp_sig_timer_cback (TIMER_LIST_ENT *p_tle)
}
}
UINT16 bta_av_extra_tg_cover_art_l2cap_psm(void)
{
tBTA_AV_CB *p_cb = &bta_av_cb;
tSDP_DISC_REC *p_rec = NULL;
tSDP_DISC_ATTR *p_add_prot_desc, *p_prot_desc;
tSDP_PROTOCOL_ELEM elem_l2cap, elem_obex;
UINT16 l2cap_psm = 0;
while (TRUE) {
/* get next record; if none found, we're done */
if ((p_rec = SDP_FindServiceInDb(p_cb->p_disc_db, UUID_SERVCLASS_AV_REM_CTRL_TARGET, p_rec)) == NULL) {
break;
}
p_add_prot_desc = SDP_FindAttributeInRec(p_rec, ATTR_ID_ADDITION_PROTO_DESC_LISTS);
if ((p_add_prot_desc != NULL) && (SDP_DISC_ATTR_TYPE(p_add_prot_desc->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
/* Walk through all protocol descriptor list */
for (p_prot_desc = p_add_prot_desc->attr_value.v.p_sub_attr; p_prot_desc; p_prot_desc = p_prot_desc->p_next_attr) {
if(SDP_FindProtocolListElem(p_prot_desc, UUID_PROTOCOL_L2CAP, &elem_l2cap)
&& SDP_FindProtocolListElem(p_prot_desc, UUID_PROTOCOL_OBEX, &elem_obex))
{
/* found */
l2cap_psm = elem_l2cap.params[0];
break;
}
}
}
}
return l2cap_psm;
}
/*******************************************************************************
**
** Function bta_av_check_peer_rc_features
@ -1539,6 +1575,10 @@ tBTA_AV_FEAT bta_av_check_peer_rc_features (UINT16 service_uuid, UINT16 *rc_feat
if (categories & AVRC_SUPF_CT_BROWSE) {
peer_features |= (BTA_AV_FEAT_BROWSE);
}
if ((service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET) && (categories & AVRC_SUPF_TG_COVER_ART)) {
/* remote target support cover art */
peer_features |= BTA_AV_FEAT_COVER_ART;
}
}
}
}
@ -1573,6 +1613,7 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
tBTA_AV_FEAT peer_features; /* peer features mask */
UINT16 peer_ct_features; /* peer features mask as controller */
UINT16 peer_tg_features; /* peer features mask as target */
UINT16 obex_l2cap_psm = 0; /* target obex l2cap psm */
UNUSED(p_data);
APPL_TRACE_DEBUG("bta_av_rc_disc_done disc:x%x", p_cb->disc);
@ -1601,6 +1642,10 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
peer_features = bta_av_check_peer_rc_features (UUID_SERVCLASS_AV_REMOTE_CONTROL, &peer_ct_features);
peer_features |= bta_av_check_peer_rc_features (UUID_SERVCLASS_AV_REM_CTRL_TARGET, &peer_tg_features);
if (peer_features & BTA_AV_FEAT_COVER_ART) {
obex_l2cap_psm = bta_av_extra_tg_cover_art_l2cap_psm();
}
p_cb->disc = 0;
utl_freebuf((void **) &p_cb->p_disc_db);
@ -1618,6 +1663,7 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
p_cb->rcb[rc_handle].peer_features = peer_features;
p_cb->rcb[rc_handle].peer_ct_features = peer_ct_features;
p_cb->rcb[rc_handle].peer_tg_features = peer_tg_features;
p_cb->rcb[rc_handle].cover_art_l2cap_psm = obex_l2cap_psm;
}
#if (BT_USE_TRACES == TRUE || BT_TRACE_APPL == TRUE)
else {
@ -1636,6 +1682,7 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
}
} else {
p_cb->rcb[rc_handle].peer_features = peer_features;
p_cb->rcb[rc_handle].cover_art_l2cap_psm = obex_l2cap_psm;
rc_feat.rc_handle = rc_handle;
rc_feat.peer_features = peer_features;
rc_feat.peer_ct_features = peer_ct_features;
@ -1676,6 +1723,10 @@ void bta_av_rc_closed(tBTA_AV_DATA *p_data)
p_rcb->peer_features = 0;
p_rcb->peer_ct_features = 0;
p_rcb->peer_tg_features = 0;
/* reset cover art state */
bta_av_ca_reset(p_rcb);
APPL_TRACE_DEBUG(" shdl:%d, lidx:%d", p_rcb->shdl, p_rcb->lidx);
if (p_rcb->shdl) {
if ((p_rcb->shdl - 1) < BTA_AV_NUM_STRS) {
@ -1748,6 +1799,7 @@ void bta_av_rc_disc(UINT8 disc)
tAVRC_SDP_DB_PARAMS db_params;
UINT16 attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST,
ATTR_ID_BT_PROFILE_DESC_LIST,
ATTR_ID_ADDITION_PROTO_DESC_LISTS,
ATTR_ID_SUPPORTED_FEATURES
};
UINT8 hdi;
@ -1785,7 +1837,7 @@ void bta_av_rc_disc(UINT8 disc)
if (p_cb->p_disc_db) {
/* set up parameters */
db_params.db_len = BTA_AV_DISC_BUF_SIZE;
db_params.num_attr = 3;
db_params.num_attr = 4;
db_params.p_db = p_cb->p_disc_db;
db_params.p_attrs = attr_list;

View File

@ -613,4 +613,73 @@ void BTA_AvMetaCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CMD cmd_code, BT_HDR *p
}
}
/*******************************************************************************
**
** Function BTA_AvCaOpen
**
** Description Open a Cover Art OBEX connection to peer device. This function
** can only be used if peer device TG support Cover Art feature and
** AV is enabled with feature BTA_AV_FEAT_METADATA.
**
** Returns void
**
*******************************************************************************/
void BTA_AvCaOpen(UINT8 rc_handle, UINT16 mtu)
{
tBTA_AV_API_CA_OPEN *p_buf;
if ((p_buf = (tBTA_AV_API_CA_OPEN *) osi_malloc(sizeof(tBTA_AV_API_CA_OPEN))) != NULL) {
p_buf->hdr.event = BTA_AV_API_CA_OPEN_EVT;
p_buf->hdr.layer_specific = rc_handle;
p_buf->mtu = mtu;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AvCaClose
**
** Description Close a Cover Art OBEX connection.
**
** Returns void
**
*******************************************************************************/
void BTA_AvCaClose(UINT8 rc_handle)
{
tBTA_AV_API_CA_CLOSE *p_buf;
if ((p_buf = (tBTA_AV_API_CA_CLOSE *) osi_malloc(sizeof(tBTA_AV_API_CA_CLOSE))) != NULL) {
p_buf->hdr.event = BTA_AV_API_CA_CLOSE_EVT;
p_buf->hdr.layer_specific = rc_handle;
bta_sys_sendmsg(p_buf);
}
}
/*******************************************************************************
**
** Function BTA_AvCaGet
**
** Description Start the process to get image properties, get image or get
** linked thumbnail. This function can only be used if Cover Art
** OBEX connection is established.
**
** Returns void
**
*******************************************************************************/
void BTA_AvCaGet(UINT8 rc_handle, tBTA_AV_GET_TYPE type, UINT8 *image_handle, UINT8 *image_descriptor, UINT16 image_descriptor_len)
{
tBTA_AV_API_CA_GET *p_buf;
if ((p_buf = (tBTA_AV_API_CA_GET *) osi_malloc(sizeof(tBTA_AV_API_CA_GET))) != NULL) {
p_buf->hdr.event = BTA_AV_API_CA_GET_EVT;
p_buf->hdr.layer_specific = rc_handle;
p_buf->type = type;
memcpy(p_buf->image_handle, image_handle, BTA_AV_CA_IMG_HDL_LEN);
p_buf->image_descriptor = image_descriptor;
p_buf->image_descriptor_len = image_descriptor_len;
bta_sys_sendmsg(p_buf);
}
}
#endif /* BTA_AV_INCLUDED */

View File

@ -0,0 +1,495 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common/bt_target.h"
#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE)
#include <string.h>
#include "bta/bta_av_api.h"
#include "bta_av_int.h"
#include "stack/avdt_api.h"
#include "bta/utl.h"
#include "stack/l2c_api.h"
#include "osi/allocator.h"
#include "osi/list.h"
#include "stack/goep_common.h"
#include "stack/goepc_api.h"
#include "stack/obex_api.h"
#include "common/bt_trace.h"
#define COVER_ART_HEADER_ID_IMG_HANDLE 0x30
#define COVER_ART_HEADER_ID_IMG_DESCRIPTOR 0x71
static const UINT8 cover_art_uuid[16] = {0x71, 0x63, 0xDD, 0x54, 0x4A, 0x7E, 0x11, 0xE2, 0xB4, 0x7C, 0x00, 0x50, 0xC2, 0x49, 0x00, 0x48};
static const char *cover_art_img_type_img = "x-bt/img-img";
static const char *cover_art_img_type_thm = "x-bt/img-thm";
static const char *cover_art_img_type_prop = "x-bt/img-properties";
#define COVER_ART_IMG_TYPE_IMG_LEN 13
#define COVER_ART_IMG_TYPE_THM_LEN 13
#define COVER_ART_IMG_TYPE_PROP_LEN 20
static BOOLEAN find_rcb_idx_by_goep_handle(UINT16 handle, UINT16 *out_idx)
{
for (UINT16 i = 0; i < BTA_AV_NUM_RCB; ++i) {
if (bta_av_cb.rcb[i].handle != BTA_AV_RC_HANDLE_NONE && bta_av_cb.rcb[i].cover_art_goep_hdl == handle) {
*out_idx = i;
return TRUE;
}
}
return FALSE;
}
static UINT8 get_rcb_idx(tBTA_AV_RCB *p_rcb)
{
return (p_rcb - &bta_av_cb.rcb[0]);
}
static void get_peer_bd_addr(tBTA_AV_RCB *p_rcb, BD_ADDR out_addr)
{
/* check if this rcb is related to a scb */
if (p_rcb->shdl && p_rcb->shdl <= BTA_AV_NUM_STRS) {
tBTA_AV_SCB *p_scb = bta_av_cb.p_scb[p_rcb->shdl - 1];
bdcpy(out_addr, p_scb->peer_addr);
}
/* else, try get peer addr from lcb */
else if (p_rcb->lidx && p_rcb->lidx <= BTA_AV_NUM_LINKS + 1)
{
bdcpy(out_addr, bta_av_cb.lcb[p_rcb->lidx-1].addr);
}
}
static void report_data_event(BT_HDR *pkt, UINT8 *p_data, UINT16 data_len, BOOLEAN final)
{
tBTA_AV_CA_DATA ca_data;
ca_data.status = BT_STATUS_SUCCESS;
ca_data.final = final;
ca_data.data_len = data_len;
ca_data.p_data = p_data;
ca_data.p_hdr = pkt;
(*bta_av_cb.p_cback)(BTA_AV_CA_DATA_EVT, (tBTA_AV *) &ca_data);
}
static void report_error_data_event(UINT16 status)
{
tBTA_AV_CA_DATA ca_data;
ca_data.status = status;
ca_data.final = TRUE;
ca_data.data_len = 0;
ca_data.p_data = NULL;
ca_data.p_hdr = NULL;
(*bta_av_cb.p_cback)(BTA_AV_CA_DATA_EVT, (tBTA_AV *) &ca_data);
}
static void build_and_send_connect_req(tBTA_AV_RCB *p_rcb)
{
tOBEX_PARSE_INFO info = {0};
info.opcode = OBEX_OPCODE_CONNECT;
info.obex_version_number = 0x15;
info.max_packet_length = p_rcb->cover_art_max_rx;
/* before OBEX connect response, we dont know cover_art_max_tx, use BT_SMALL_BUFFER_SIZE as tx buff size */
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, BT_SMALL_BUFFER_SIZE) == GOEP_SUCCESS) {
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TARGET, (UINT8 *)cover_art_uuid, 16);
GOEPC_SendRequest(p_rcb->cover_art_goep_hdl);
}
}
static void build_and_send_disconnect_req(tBTA_AV_RCB *p_rcb)
{
tOBEX_PARSE_INFO info = {0};
info.opcode = OBEX_OPCODE_DISCONNECT;
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, BT_SMALL_BUFFER_SIZE) == GOEP_SUCCESS) {
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_rcb->cover_art_cid), 4);
GOEPC_SendRequest(p_rcb->cover_art_goep_hdl);
}
}
static void build_and_send_empty_get_req(tBTA_AV_RCB *p_rcb)
{
tOBEX_PARSE_INFO info = {0};
info.opcode = OBEX_OPCODE_GET_FINAL;
/* empty get request, use a small buff size */
UINT16 tx_buff_size = BT_SMALL_BUFFER_SIZE < p_rcb->cover_art_max_tx ? BT_SMALL_BUFFER_SIZE : p_rcb->cover_art_max_tx;
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, tx_buff_size) == GOEP_SUCCESS) {
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_rcb->cover_art_cid), 4);
GOEPC_SendRequest(p_rcb->cover_art_goep_hdl);
}
}
static void close_goepc_and_disconnect(tBTA_AV_RCB *p_rcb)
{
if (p_rcb->cover_art_goep_hdl) {
GOEPC_Close(p_rcb->cover_art_goep_hdl);
}
p_rcb->cover_art_goep_hdl = 0;
p_rcb->cover_art_cid = 0;
p_rcb->cover_art_max_tx = 0;
p_rcb->cover_art_max_rx = 0;
tBTA_AV_DATA *p_data = (tBTA_AV_DATA *) osi_malloc(sizeof(tBTA_AV_DATA));
if (p_data == NULL) {
assert(0);
}
p_data->hdr.event = BTA_AV_CA_GOEP_DISCONNECT_EVT;
p_data->hdr.layer_specific = get_rcb_idx(p_rcb);
p_data->ca_disconnect.reason = BT_STATUS_FAIL;
bta_sys_sendmsg(p_data);
}
static void image_handle_to_utf16(const UINT8 *image_handle, UINT8 *buffer)
{
UINT8 pos = 0;
for (int i = 0 ; i < BTA_AV_CA_IMG_HDL_LEN ; i++){
buffer[pos++] = 0;
buffer[pos++] = image_handle[i];
}
buffer[pos++] = 0;
buffer[pos++] = 0;
}
void bta_av_ca_goep_event_handler(UINT16 handle, UINT8 event, tGOEPC_MSG *p_msg)
{
tBTA_AV_DATA *p_data = NULL;
UINT16 rcb_idx;
if (!find_rcb_idx_by_goep_handle(handle, &rcb_idx)) {
/* can not find a rcb, go error */
goto error;
}
if (event == GOEPC_RESPONSE_EVT || event == GOEPC_OPENED_EVT || event == GOEPC_CLOSED_EVT) {
p_data = (tBTA_AV_DATA *) osi_malloc(sizeof(tBTA_AV_DATA));
assert(p_data != NULL);
}
switch (event)
{
case GOEPC_OPENED_EVT:
p_data->hdr.layer_specific = rcb_idx;
p_data->hdr.event = BTA_AV_CA_GOEP_CONNECT_EVT;
p_data->ca_connect.max_rx = p_msg->opened.our_mtu;
break;
case GOEPC_CLOSED_EVT:
p_data->hdr.layer_specific = rcb_idx;
p_data->hdr.event = BTA_AV_CA_GOEP_DISCONNECT_EVT;
p_data->ca_disconnect.reason = BT_STATUS_FAIL;
break;
case GOEPC_RESPONSE_EVT:
p_data->hdr.layer_specific = rcb_idx;
p_data->ca_response.pkt = p_msg->response.pkt;
p_data->ca_response.opcode = p_msg->response.opcode;
p_data->ca_response.srm_en = p_msg->response.srm_en;
p_data->ca_response.srm_wait = p_msg->response.srm_wait;
if (p_msg->response.final) {
p_data->hdr.event = BTA_AV_CA_RESPONSE_FINAL_EVT;
}
else {
p_data->hdr.event = BTA_AV_CA_RESPONSE_EVT;
}
break;
case GOEPC_MTU_CHANGED_EVT:
case GOEPC_CONGEST_EVT:
case GOEPC_UNCONGEST_EVT:
/* ignore these event */
break;
default:
goto error;
break;
}
if (p_data) {
bta_sys_sendmsg(p_data);
}
return;
error:
/* can not find rcb, just free resource and disconnect */
if (p_data != NULL) {
osi_free(p_data);
}
if (event == GOEPC_RESPONSE_EVT && p_msg->response.pkt != NULL) {
osi_free(p_msg->response.pkt);
}
if (event != GOEPC_CLOSED_EVT) {
GOEPC_Close(handle);
}
}
void bta_av_ca_api_open(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
tOBEX_SVR_INFO svr = {0};
svr.tl = OBEX_OVER_L2CAP;
svr.l2cap.psm = p_rcb->cover_art_l2cap_psm;
svr.l2cap.pref_mtu = p_data->api_ca_open.mtu;
/* reuse the security mask store in bta_av_cb, when support multi connection, this may need change */
svr.l2cap.sec_mask = bta_av_cb.sec_mask;
p_rcb->cover_art_max_rx = p_data->api_ca_open.mtu;
get_peer_bd_addr(p_rcb, svr.l2cap.addr);
if (GOEPC_Open(&svr, bta_av_ca_goep_event_handler, &p_rcb->cover_art_goep_hdl) != GOEP_SUCCESS) {
/* open failed */
if ((p_data = (tBTA_AV_DATA *) osi_malloc(sizeof(tBTA_AV_DATA))) == NULL) {
assert(0);
}
p_data->hdr.event = BTA_AV_CA_GOEP_DISCONNECT_EVT;
p_data->hdr.layer_specific = get_rcb_idx(p_rcb);
p_data->ca_disconnect.reason = BT_STATUS_FAIL;
bta_sys_sendmsg(p_data);
}
}
void bta_av_ca_api_close(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
/* this is a normal disconnect, just build and send OBEX disconnect request */
build_and_send_disconnect_req(p_rcb);
}
void bta_av_ca_api_get(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
tOBEX_PARSE_INFO info = {0};
UINT8 image_handle_utf16[BTA_AV_CA_IMG_HDL_UTF16_LEN];
info.opcode = OBEX_OPCODE_GET_FINAL;
/* limit the tx buff size to BT_DEFAULT_BUFFER_SIZE */
UINT16 tx_buff_size = BT_DEFAULT_BUFFER_SIZE < p_rcb->cover_art_max_tx ? BT_DEFAULT_BUFFER_SIZE : p_rcb->cover_art_max_tx;
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, tx_buff_size) != GOEP_SUCCESS) {
/* something error */
goto error;
}
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_rcb->cover_art_cid), 4);
switch (p_data->api_ca_get.type)
{
case BTA_AV_CA_GET_IMAGE_PROPERTIES:
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TYPE, (const UINT8 *)cover_art_img_type_prop, COVER_ART_IMG_TYPE_PROP_LEN);
break;
case BTA_AV_CA_GET_IMAGE:
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TYPE, (const UINT8 *)cover_art_img_type_img, COVER_ART_IMG_TYPE_IMG_LEN);
break;
case BTA_AV_CA_GET_LINKED_THUMBNAIL:
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TYPE, (const UINT8 *)cover_art_img_type_thm, COVER_ART_IMG_TYPE_THM_LEN);
break;
default:
/* should not go here */
assert(0);
break;
}
image_handle_to_utf16(p_data->api_ca_get.image_handle, image_handle_utf16);
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, COVER_ART_HEADER_ID_IMG_HANDLE, (UINT8 *)image_handle_utf16, BTA_AV_CA_IMG_HDL_UTF16_LEN);
if (p_data->api_ca_get.type == BTA_AV_CA_GET_IMAGE) {
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, COVER_ART_HEADER_ID_IMG_DESCRIPTOR, (UINT8 *)p_data->api_ca_get.image_descriptor, p_data->api_ca_get.image_descriptor_len);
}
/* always request to enable srm */
GOEPC_RequestSetSRM(p_rcb->cover_art_goep_hdl, TRUE, FALSE);
if (GOEPC_SendRequest(p_rcb->cover_art_goep_hdl) != GOEP_SUCCESS) {
goto error;
}
return;
error:
close_goepc_and_disconnect(p_rcb);
}
void bta_av_ca_response(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
tOBEX_PARSE_INFO info;
OBEX_ParseResponse(p_data->ca_response.pkt, p_data->ca_response.opcode, &info);
/* we always use a final get */
if (p_data->ca_response.opcode == OBEX_OPCODE_GET_FINAL
&& (info.response_code == OBEX_RESPONSE_CODE_CONTINUE || info.response_code == (OBEX_RESPONSE_CODE_CONTINUE | OBEX_FINAL_BIT_MASK)))
{
UINT8 *header = NULL;
UINT8 *body_data = NULL;
UINT16 body_data_len = 0;
while((header = OBEX_GetNextHeader(p_data->ca_response.pkt, &info)) != NULL) {
switch (*header)
{
case OBEX_HEADER_ID_BODY:
/* actually,END_OF_BODY should not in this continue response */
case OBEX_HEADER_ID_END_OF_BODY:
if (body_data == NULL) {
/* first body header */
body_data = header + 3; /* skip opcode, length */
body_data_len = OBEX_GetHeaderLength(header) - 3;
}
else {
/* another body header found */
report_data_event(NULL, body_data, body_data_len, FALSE);
body_data = header + 3; /* skip opcode, length */
body_data_len = OBEX_GetHeaderLength(header) - 3;
}
break;
default:
break;
}
}
if (body_data != NULL) {
/* the only one or the last body data */
report_data_event(p_data->ca_response.pkt, body_data, body_data_len, FALSE);
}
else {
/* not any body data */
osi_free(p_data->ca_response.pkt);
}
/* if SRM not enable, we need to send a empty get request */
if (!p_data->ca_response.srm_en || p_data->ca_response.srm_wait) {
build_and_send_empty_get_req(p_rcb);
}
}
else {
osi_free(p_data->ca_response.pkt);
goto error;
}
return;
error:
close_goepc_and_disconnect(p_rcb);
}
void bta_av_ca_response_final(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
tOBEX_PARSE_INFO info;
OBEX_ParseResponse(p_data->ca_response.pkt, p_data->ca_response.opcode, &info);
UINT8 *header = NULL;
if (p_data->ca_response.opcode == OBEX_OPCODE_CONNECT) {
/* we expect a success response code with final bit set */
if (info.response_code == (OBEX_RESPONSE_CODE_OK | OBEX_FINAL_BIT_MASK)) {
if (info.max_packet_length < 255) {
p_rcb->cover_art_max_tx = 255;
}
else {
p_rcb->cover_art_max_tx = info.max_packet_length;
}
BOOLEAN cid_found = false;
while((header = OBEX_GetNextHeader(p_data->ca_response.pkt, &info)) != NULL) {
if (*header == OBEX_HEADER_ID_CONNECTION_ID) {
cid_found = true;
memcpy((UINT8 *)(&p_rcb->cover_art_cid), header + 1, 4);
break;
}
}
if (!cid_found) {
goto error;
}
tBTA_AV_CA_STATUS ca_status;
ca_status.connected = TRUE;
ca_status.reason = BT_STATUS_SUCCESS;
(*bta_av_cb.p_cback)(BTA_AV_CA_STATUS_EVT, (tBTA_AV *) &ca_status);
/* done, free response packet */
osi_free(p_data->ca_response.pkt);
}
else {
osi_free(p_data->ca_response.pkt);
goto error;
}
}
else if (p_data->ca_response.opcode == OBEX_OPCODE_GET_FINAL) {
UINT8 *body_data = NULL;
UINT16 body_data_len = 0;
/* check response code is success */
if (info.response_code == (OBEX_RESPONSE_CODE_OK | OBEX_FINAL_BIT_MASK)) {
while((header = OBEX_GetNextHeader(p_data->ca_response.pkt, &info)) != NULL) {
switch (*header)
{
/* actually, BODY should not in this final response */
case OBEX_HEADER_ID_BODY:
case OBEX_HEADER_ID_END_OF_BODY:
if (body_data == NULL) {
/* first body header */
body_data = header + 3; /* skip opcode, length */
body_data_len = OBEX_GetHeaderLength(header) - 3;
}
else {
/* another body header found */
report_data_event(NULL, body_data, body_data_len, FALSE);
body_data = header + 3; /* skip opcode, length */
body_data_len = OBEX_GetHeaderLength(header) - 3;
}
break;
default:
break;
}
}
if (body_data != NULL) {
/* the only one or the last body data, packet will be free by upper layer */
report_data_event(p_data->ca_response.pkt, body_data, body_data_len, TRUE);
}
else {
/* not any body data */
osi_free(p_data->ca_response.pkt);
}
}
else {
report_error_data_event(BT_STATUS_FAIL);
osi_free(p_data->ca_response.pkt);
}
}
else if (p_data->ca_response.opcode == OBEX_OPCODE_DISCONNECT) {
/* received disconnect response, close l2cap channel and reset cover art value */
bta_av_ca_force_disconnect(p_rcb, p_data);
osi_free(p_data->ca_response.pkt);
}
else {
osi_free(p_data->ca_response.pkt);
goto error;
}
return;
error:
close_goepc_and_disconnect(p_rcb);
}
void bta_av_ca_goep_connect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
/* goep connection open, use a smaller value as max_rx */
if (p_rcb->cover_art_max_rx > p_data->ca_connect.max_rx) {
p_rcb->cover_art_max_rx = p_data->ca_connect.max_rx;
}
build_and_send_connect_req(p_rcb);
}
void bta_av_ca_goep_disconnect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
p_rcb->cover_art_goep_hdl = 0;
p_rcb->cover_art_max_rx = 0;
p_rcb->cover_art_max_tx = 0;
p_rcb->cover_art_cid = 0;
tBTA_AV_CA_STATUS ca_status;
ca_status.connected = FALSE;
ca_status.reason = p_data->ca_disconnect.reason;
(*bta_av_cb.p_cback)(BTA_AV_CA_STATUS_EVT, (tBTA_AV *) &ca_status);
}
void bta_av_ca_force_disconnect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
{
if (p_rcb->cover_art_goep_hdl) {
GOEPC_Close(p_rcb->cover_art_goep_hdl);
}
/* dont reset p_rcb->cover_art_l2cap_psm */
p_rcb->cover_art_goep_hdl = 0;
p_rcb->cover_art_cid = 0;
p_rcb->cover_art_max_tx = 0;
p_rcb->cover_art_max_rx = 0;
tBTA_AV_CA_STATUS ca_status;
ca_status.connected = FALSE;
/* force disconnect by upper, set reason to success */
ca_status.reason = BT_STATUS_SUCCESS;
(*bta_av_cb.p_cback)(BTA_AV_CA_STATUS_EVT, (tBTA_AV *) &ca_status);
}
void bta_av_ca_reset(tBTA_AV_RCB *p_rcb)
{
if (p_rcb->cover_art_goep_hdl) {
GOEPC_Close(p_rcb->cover_art_goep_hdl);
}
p_rcb->cover_art_l2cap_psm = 0;
p_rcb->cover_art_goep_hdl = 0;
p_rcb->cover_art_state = 0;
p_rcb->cover_art_cid = 0;
p_rcb->cover_art_max_tx = 0;
p_rcb->cover_art_max_rx = 0;
}
#endif /* BTA_AV_INCLUDED */

View File

@ -0,0 +1,170 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "common/bt_target.h"
#if defined(BTA_AV_INCLUDED) && (BTA_AV_INCLUDED == TRUE)
#include <string.h>
#include "bta/bta_av_api.h"
#include "bta_av_int.h"
#include "stack/avdt_api.h"
#include "bta/utl.h"
#include "stack/l2c_api.h"
#include "osi/allocator.h"
#include "osi/list.h"
#include "common/bt_trace.h"
/* state machine states */
enum {
BTA_AV_CA_INIT_ST,
BTA_AV_CA_OPENING_ST,
BTA_AV_CA_CONNECTING_ST,
BTA_AV_CA_CONNECTED_ST,
BTA_AV_CA_GETTING_ST,
BTA_AV_CA_CLOSING_ST
};
/* state machine action enumeration list */
enum {
BTA_AV_API_CA_OPEN,
BTA_AV_API_CA_CLOSE,
BTA_AV_API_CA_GET,
BTA_AV_CA_RESPONSE,
BTA_AV_CA_RESPONSE_FINAL,
BTA_AV_CA_GOEP_CONNECT,
BTA_AV_CA_GOEP_DISCONNECT,
BTA_AV_CA_FORCE_DISCONNECT,
BTA_AV_CA_NUM_ACTIONS
};
#define BTA_AV_CA_IGNORE BTA_AV_CA_NUM_ACTIONS
/* type for action functions */
typedef void (*tBTA_AV_CA_ACTION)(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
/* action functions */
const tBTA_AV_CA_ACTION bta_av_ca_action[] = {
bta_av_ca_api_open,
bta_av_ca_api_close,
bta_av_ca_api_get,
bta_av_ca_response,
bta_av_ca_response_final,
bta_av_ca_goep_connect,
bta_av_ca_goep_disconnect,
bta_av_ca_force_disconnect,
NULL
};
/* state table information */
#define BTA_AV_CA_ACTION_COL 0 /* position of actions */
#define BTA_AV_CA_NEXT_STATE 1 /* position of next state */
#define BTA_AV_CA_NUM_COLS 2 /* number of columns in state tables */
/* state table for init state */
static const UINT8 bta_av_ca_st_init[][BTA_AV_CA_NUM_COLS] = {
/* Event Action 1 Next state */
/* API_CA_OPEN_EVT */ {BTA_AV_API_CA_OPEN, BTA_AV_CA_OPENING_ST },
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
};
/* state table for opening state */
static const UINT8 bta_av_ca_st_opening[][BTA_AV_CA_NUM_COLS] = {
/* Event Action 1 Next state */
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_GOEP_CONNECT, BTA_AV_CA_CONNECTING_ST },
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
};
/* state table for connecting state */
static const UINT8 bta_av_ca_st_connecting[][BTA_AV_CA_NUM_COLS] = {
/* Event Action 1 Next state */
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_RESPONSE_FINAL, BTA_AV_CA_CONNECTED_ST },
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
};
/* state table for connected state */
static const UINT8 bta_av_ca_st_connected[][BTA_AV_CA_NUM_COLS] = {
/* Event Action 1 Next state */
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
/* API_CA_CLOSE_EVT */ {BTA_AV_API_CA_CLOSE, BTA_AV_CA_CLOSING_ST },
/* API_CA_GET_EVT */ {BTA_AV_API_CA_GET, BTA_AV_CA_GETTING_ST },
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
};
/* state table for getting state */
static const UINT8 bta_av_ca_st_getting[][BTA_AV_CA_NUM_COLS] = {
/* Event Action 1 Next state */
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_GETTING_ST },
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_GETTING_ST },
/* CA_RESPONSE_EVT */ {BTA_AV_CA_RESPONSE, BTA_AV_CA_GETTING_ST },
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_RESPONSE_FINAL, BTA_AV_CA_CONNECTED_ST },
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_GETTING_ST },
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
};
/* state table for closing state */
static const UINT8 bta_av_ca_st_closing[][BTA_AV_CA_NUM_COLS] = {
/* Event Action 1 Next state */
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_RESPONSE_FINAL, BTA_AV_CA_INIT_ST },
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
};
/* type for state table */
typedef const UINT8 (*tBTA_AV_CA_ST_TBL)[BTA_AV_CA_NUM_COLS];
/* state table */
static const tBTA_AV_CA_ST_TBL bta_av_ca_st_tbl[] = {
bta_av_ca_st_init,
bta_av_ca_st_opening,
bta_av_ca_st_connecting,
bta_av_ca_st_connected,
bta_av_ca_st_getting,
bta_av_ca_st_closing
};
void bta_av_ca_sm_execute(tBTA_AV_RCB *p_rcb, UINT16 event, tBTA_AV_DATA *p_data)
{
tBTA_AV_CA_ST_TBL state_table;
UINT8 action;
/* look up the state table for the current state */
state_table = bta_av_ca_st_tbl[p_rcb->cover_art_state];
event -= BTA_AV_CA_FIRST_SM_EVT;
/* set next state */
p_rcb->cover_art_state = state_table[event][BTA_AV_CA_NEXT_STATE];
/* execute action functions */
if ((action = state_table[event][BTA_AV_CA_ACTION_COL]) != BTA_AV_CA_IGNORE) {
(*bta_av_ca_action[action])(p_rcb, p_data);
}
}
#endif /* BTA_AV_INCLUDED */

View File

@ -461,7 +461,7 @@ static void bta_av_a2dp_report_cback(UINT8 handle, AVDT_REPORT_TYPE type,
**
** Function bta_av_api_sink_enable
**
** Description activate, deactive A2DP Sink,
** Description activate, deactivate A2DP Sink,
**
** Returns void
**
@ -593,7 +593,7 @@ static void bta_av_api_register(tBTA_AV_DATA *p_data)
#endif
}
/* Set the Calss of Device (Audio & Capturing/Rendering service class bit) */
/* Set the Class of Device (Audio & Capturing/Rendering service class bit) */
if (p_data->api_reg.tsep == AVDT_TSEP_SRC) {
cod.service = BTM_COD_SERVICE_CAPTURING | BTM_COD_SERVICE_AUDIO;
cod.major = BTM_COD_MAJOR_AUDIO;
@ -1248,6 +1248,11 @@ BOOLEAN bta_av_hdl_event(BT_HDR *p_msg)
APPL_TRACE_VERBOSE("AV sm event=0x%x(%s)\n", event, bta_av_evt_code(event));
/* state machine events */
bta_av_sm_execute(&bta_av_cb, p_msg->event, (tBTA_AV_DATA *) p_msg);
} else if (event >= BTA_AV_CA_FIRST_SM_EVT && event <= BTA_AV_CA_LAST_SM_EVT) {
if (p_msg->layer_specific < BTA_AV_NUM_RCB) {
tBTA_AV_RCB *p_rcb = &bta_av_cb.rcb[p_msg->layer_specific];
bta_av_ca_sm_execute(p_rcb, p_msg->event, (tBTA_AV_DATA *) p_msg);
}
} else {
APPL_TRACE_VERBOSE("handle=0x%x\n", p_msg->layer_specific);
tBTA_AV_SCB *p_scb = bta_av_hndl_to_scb(p_msg->layer_specific);

View File

@ -86,6 +86,15 @@ enum {
BTA_AV_AVDT_DELAY_RPT_EVT,
BTA_AV_ACP_CONNECT_EVT,
/* these events are handled by the Cover Art Client state machine */
BTA_AV_API_CA_OPEN_EVT,
BTA_AV_API_CA_CLOSE_EVT,
BTA_AV_API_CA_GET_EVT,
BTA_AV_CA_RESPONSE_EVT,
BTA_AV_CA_RESPONSE_FINAL_EVT,
BTA_AV_CA_GOEP_CONNECT_EVT,
BTA_AV_CA_GOEP_DISCONNECT_EVT,
/* these events are handled outside of the state machine */
BTA_AV_API_ENABLE_EVT,
BTA_AV_API_REGISTER_EVT,
@ -115,6 +124,10 @@ enum {
#define BTA_AV_FIRST_SM_EVT BTA_AV_API_DISABLE_EVT
#define BTA_AV_LAST_SM_EVT BTA_AV_AVRC_NONE_EVT
/* events for AVRC Cover Art state machine */
#define BTA_AV_CA_FIRST_SM_EVT BTA_AV_API_CA_OPEN_EVT
#define BTA_AV_CA_LAST_SM_EVT BTA_AV_CA_GOEP_DISCONNECT_EVT
/* events for AV stream control block state machine */
#define BTA_AV_FIRST_SSM_EVT BTA_AV_API_OPEN_EVT
@ -348,6 +361,48 @@ typedef struct {
BT_HDR hdr;
} tBTA_AV_API_GET_DELAY_VALUE;
/* data type for BTA_AV_API_CA_OPEN_EVT */
typedef struct {
BT_HDR hdr;
UINT16 mtu;
} tBTA_AV_API_CA_OPEN;
/* data type for BTA_AV_API_CA_CLOSE_EVT */
typedef struct {
BT_HDR hdr;
} tBTA_AV_API_CA_CLOSE;
/* data type for BTA_AV_API_CA_GET_EVT */
typedef struct {
BT_HDR hdr;
tBTA_AV_GET_TYPE type;
UINT8 image_handle[7];
/* Image descriptor used in get image function */
UINT16 image_descriptor_len;
UINT8 *image_descriptor;
} tBTA_AV_API_CA_GET;
/* data type for BTA_AV_CA_RESPONSE_EVT and BTA_AV_CA_RESPONSE_FINAL_EVT */
typedef struct {
BT_HDR hdr;
BT_HDR *pkt;
UINT8 opcode;
BOOLEAN srm_en;
BOOLEAN srm_wait;
} tBTA_AV_CA_RESPONSE;
/* data type for BTA_AV_CA_CONNECT_EVT */
typedef struct {
BT_HDR hdr;
UINT16 max_rx;
} tBTA_AV_CA_CONNECT;
/* data type for BTA_AV_CA_DISCONNECT_EVT */
typedef struct {
BT_HDR hdr;
UINT16 reason;
} tBTA_AV_CA_DISCONNECT;
/* initiator/acceptor role for adaption */
#define BTA_AV_ROLE_AD_INT 0x00 /* initiator */
#define BTA_AV_ROLE_AD_ACP 0x01 /* acceptor */
@ -382,6 +437,12 @@ typedef union {
tBTA_AV_API_META_RSP api_meta_rsp;
tBTA_AV_API_SET_DELAY_VALUE api_set_delay_vlaue;
tBTA_AV_API_GET_DELAY_VALUE api_get_delay_value;
tBTA_AV_API_CA_OPEN api_ca_open;
tBTA_AV_API_CA_CLOSE api_ca_close;
tBTA_AV_API_CA_GET api_ca_get;
tBTA_AV_CA_RESPONSE ca_response;
tBTA_AV_CA_CONNECT ca_connect;
tBTA_AV_CA_DISCONNECT ca_disconnect;
} tBTA_AV_DATA;
typedef void (tBTA_AV_VDP_DATA_ACT)(void *p_scb);
@ -405,8 +466,8 @@ typedef union {
#define BTA_AV_Q_TAG_START 0x02 /* before start sending media packets */
#define BTA_AV_Q_TAG_STREAM 0x03 /* during streaming */
#define BTA_AV_WAIT_ACP_CAPS_ON 0x01 /* retriving the peer capabilities */
#define BTA_AV_WAIT_ACP_CAPS_STARTED 0x02 /* started while retriving peer capabilities */
#define BTA_AV_WAIT_ACP_CAPS_ON 0x01 /* retrieving the peer capabilities */
#define BTA_AV_WAIT_ACP_CAPS_STARTED 0x02 /* started while retrieving peer capabilities */
#define BTA_AV_WAIT_ROLE_SW_RES_OPEN 0x04 /* waiting for role switch result after API_OPEN, before STR_OPENED */
#define BTA_AV_WAIT_ROLE_SW_RES_START 0x08 /* waiting for role switch result before streaming */
#define BTA_AV_WAIT_ROLE_SW_STARTED 0x10 /* started while waiting for role switch result */
@ -462,7 +523,7 @@ typedef struct {
BOOLEAN use_rc; /* TRUE if AVRCP is allowed */
BOOLEAN started; /* TRUE if stream started */
UINT8 co_started; /* non-zero, if stream started from call-out perspective */
BOOLEAN recfg_sup; /* TRUE if the first attempt to reconfigure the stream was successfull, else False if command fails */
BOOLEAN recfg_sup; /* TRUE if the first attempt to reconfigure the stream was successful, else False if command fails */
BOOLEAN suspend_sup; /* TRUE if Suspend stream is supported, else FALSE if suspend command fails */
BOOLEAN deregistring; /* TRUE if deregistering */
BOOLEAN sco_suspend; /* TRUE if SUSPEND is issued automatically for SCO */
@ -471,7 +532,7 @@ typedef struct {
UINT8 wait; /* set 0x1, when getting Caps as ACP, set 0x2, when started */
UINT8 q_tag; /* identify the associated q_info union member */
BOOLEAN no_rtp_hdr; /* TRUE if add no RTP header*/
UINT8 disc_rsn; /* disconenction reason */
UINT8 disc_rsn; /* disconnection reason */
UINT16 uuid_int; /*intended UUID of Initiator to connect to */
} tBTA_AV_SCB;
@ -481,6 +542,13 @@ typedef struct {
#define BTA_AV_RC_CONN_MASK 0x20
#define BTA_AV_CA_IMG_HDL_UTF16_LEN 16 /* Cover Art image handle in utf-16 format, fixed to 16 */
#define BTA_AV_CA_SRM_DISABLE 0x00
#define BTA_AV_CA_SRM_ENABLE_REQ 0x01
#define BTA_AV_CA_SRM_WAIT 0x02
#define BTA_AV_CA_SRM_ENABLE 0x03
/* type for AV RCP control block */
/* index to this control block is the rc handle */
typedef struct {
@ -491,6 +559,13 @@ typedef struct {
tBTA_AV_FEAT peer_features; /* peer features mask */
UINT16 peer_ct_features;
UINT16 peer_tg_features;
UINT16 cover_art_l2cap_psm; /* OBEX over L2CAP PSM */
UINT16 cover_art_goep_hdl; /* Cover Art client GOEP connection handle */
UINT8 cover_art_state; /* Cover Art client state machine */
UINT32 cover_art_cid; /* Cover Art client connection id */
UINT16 cover_art_max_tx; /* max packet length peer device can receive */
UINT16 cover_art_max_rx; /* max packet length we can receive */
} tBTA_AV_RCB;
#define BTA_AV_NUM_RCB (BTA_AV_NUM_STRS + 2)
@ -705,6 +780,17 @@ extern void bta_av_do_disc_vdp (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data);
extern void bta_av_vdp_str_opened (tBTA_AV_SCB *p_scb, tBTA_AV_DATA *p_data);
extern void bta_av_reg_vdp (tAVDT_CS *p_cs, char *p_service_name, void *p_data);
extern void bta_av_ca_api_open(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_api_close(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_api_get(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_response(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_response_final(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_goep_connect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_goep_disconnect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_force_disconnect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
extern void bta_av_ca_sm_execute(tBTA_AV_RCB *p_rcb, UINT16 event, tBTA_AV_DATA *p_data);
extern void bta_av_ca_reset(tBTA_AV_RCB *p_rcb);
#endif ///BTA_AV_INCLUDED == TRUE
#endif /* BTA_AV_INT_H */

View File

@ -43,14 +43,14 @@
#define BTA_AV_FAIL_STREAM 3 /* stream connection failed */
#define BTA_AV_FAIL_RESOURCES 4 /* no resources */
#define BTA_AV_FAIL_ROLE 5 /* failed due to role management related issues */
#define BTA_AV_FAIL_GET_CAP 6 /* get capability failed due to no SEP availale on the peer */
#define BTA_AV_FAIL_GET_CAP 6 /* get capability failed due to no SEP available on the peer */
typedef UINT8 tBTA_AV_STATUS;
/* AV features masks */
#define BTA_AV_FEAT_RCTG 0x0001 /* remote control target */
#define BTA_AV_FEAT_RCCT 0x0002 /* remote control controller */
#define BTA_AV_FEAT_PROTECT 0x0004 /* streaming media contect protection */
#define BTA_AV_FEAT_PROTECT 0x0004 /* streaming media context protection */
#define BTA_AV_FEAT_VENDOR 0x0008 /* remote control vendor dependent commands */
#define BTA_AV_FEAT_REPORT 0x0020 /* use reporting service for VDP */
#define BTA_AV_FEAT_METADATA 0x0040 /* remote control Metadata Transfer command/response */
@ -60,6 +60,7 @@ typedef UINT8 tBTA_AV_STATUS;
#define BTA_AV_FEAT_ADV_CTRL 0x0200 /* remote control Advanced Control command/response */
#define BTA_AV_FEAT_DELAY_RPT 0x0400 /* allow delay reporting */
#define BTA_AV_FEAT_ACP_START 0x0800 /* start stream when 2nd SNK was accepted */
#define BTA_AV_FEAT_COVER_ART 0x1000 /* remote control target cover art */
/* Internal features */
#define BTA_AV_FEAT_NO_SCO_SSPD 0x8000 /* Do not suspend av streaming as to AG events(SCO or Call) */
@ -107,6 +108,7 @@ typedef UINT8 tBTA_AV_HNDL;
#define BTA_AV_MAX_VDP_MTU 1008
#endif
#define BTA_AV_CA_IMG_HDL_LEN 7 /* Cover Art image handle len, fixed to 7 */
/* codec type */
#define BTA_AV_CODEC_SBC A2D_MEDIA_CT_SBC /* SBC media codec type */
@ -224,6 +226,12 @@ typedef UINT8 tBTA_AV_CODE;
typedef UINT8 tBTA_AV_ERR;
/* type codes for BTA_AV_API_CA_GET */
#define BTA_AV_CA_GET_IMAGE_PROPERTIES 0x01
#define BTA_AV_CA_GET_IMAGE 0x02
#define BTA_AV_CA_GET_LINKED_THUMBNAIL 0x03
typedef UINT8 tBTA_AV_GET_TYPE;
/* AV callback events */
#define BTA_AV_ENABLE_EVT 0 /* AV enabled */
@ -253,8 +261,12 @@ typedef UINT8 tBTA_AV_ERR;
#define BTA_AV_SET_DELAY_VALUE_EVT 22 /* set delay reporting value */
#define BTA_AV_GET_DELAY_VALUE_EVT 23 /* get delay reporting value */
#define BTA_AV_SNK_PSC_CFG_EVT 24 /* Protocol service capabilities. */
#define BTA_AV_CA_STATUS_EVT 25 /* Cover Art Client status event */
#define BTA_AV_CA_DATA_EVT 26 /* Cover Art response body data */
/* Max BTA event */
#define BTA_AV_MAX_EVT 25
#define BTA_AV_MAX_EVT 27
/* function types for call-out functions */
@ -481,6 +493,20 @@ typedef struct {
UINT16 psc_mask;
} tBTA_AV_SNK_PSC_CFG;
/* data associated with BTA_AV_CA_STATUS_EVT */
typedef struct {
BOOLEAN connected; /* whether Cover Art connection is connected */
UINT16 reason; /* connect failed or disconnect reason */
} tBTA_AV_CA_STATUS;
/* data associated with BTA_AV_CA_DATA_EVT */
typedef struct {
UINT16 status; /* OBEX response status */
BOOLEAN final; /* final data packet */
UINT16 data_len; /* data len */
UINT8 *p_data; /* point to the data in p_hdr */
BT_HDR *p_hdr; /* after data pass to application, free this packet */
} tBTA_AV_CA_DATA;
/* union of data associated with AV callback */
typedef union {
@ -506,6 +532,8 @@ typedef union {
tBTA_AV_RC_FEAT rc_feat;
tBTA_AV_DELAY delay;
tBTA_AV_SNK_PSC_CFG psc;
tBTA_AV_CA_STATUS ca_status;
tBTA_AV_CA_DATA ca_data;
} tBTA_AV;
/* union of data associated with AV Media callback */
@ -865,6 +893,43 @@ void BTA_AvMetaRsp(UINT8 rc_handle, UINT8 label, tBTA_AV_CODE rsp_code,
*******************************************************************************/
void BTA_AvMetaCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CMD cmd_code, BT_HDR *p_pkt);
/*******************************************************************************
**
** Function BTA_AvCaOpen
**
** Description Open a Cover Art OBEX connection to peer device. This function
** can only be used if peer device TG support Cover Art feature and
** AV is enabled with feature BTA_AV_FEAT_METADATA.
**
** Returns void
**
*******************************************************************************/
void BTA_AvCaOpen(UINT8 rc_handle, UINT16 pref_packet_len);
/*******************************************************************************
**
** Function BTA_AvCaClose
**
** Description Close a Cover Art OBEX connection.
**
** Returns void
**
*******************************************************************************/
void BTA_AvCaClose(UINT8 rc_handle);
/*******************************************************************************
**
** Function BTA_AvCaGet
**
** Description Start the process to get image properties, get image or get
** linked thumbnail. This function can only be used if Cover Art
** OBEX connection is established.
**
** Returns void
**
*******************************************************************************/
void BTA_AvCaGet(UINT8 rc_handle, tBTA_AV_GET_TYPE type, UINT8 *image_handle, UINT8 *image_descriptor, UINT16 image_descriptor_len);
#ifdef __cplusplus
}
#endif

View File

@ -91,7 +91,8 @@ typedef UINT16 tBTA_SYS_HW_MODULE;
#define BTA_ID_GATTC 31 /* GATT Client */
#define BTA_ID_GATTS 32 /* GATT Client */
#define BTA_ID_SDP 33 /* SDP Client */
#define BTA_ID_BLUETOOTH_MAX 34 /* last BT profile */
#define BTA_ID_GOEPC 34 /* GOEP Client */
#define BTA_ID_BLUETOOTH_MAX 35 /* last BT profile */
/* GENERIC */
#define BTA_ID_PRM 38
@ -142,7 +143,7 @@ typedef void (tBTA_SYS_CONN_CBACK)(tBTA_SYS_CONN_STATUS status, UINT8 id, UINT8
typedef void (tBTA_SYS_SSR_CFG_CBACK)(UINT8 id, UINT8 app_id, UINT16 latency, UINT16 tout);
#if (BTA_EIR_CANNED_UUID_LIST != TRUE)
/* eir callback for adding/removeing UUID */
/* eir callback for adding/removing UUID */
typedef void (tBTA_SYS_EIR_CBACK)(tBT_UUID uuid, BOOLEAN adding);
#endif

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -34,7 +34,7 @@
#if BTC_AV_INCLUDED
// global variable to inidcate avrc is initialized with a2dp
// global variable to indicate avrc is initialized with a2dp
bool g_av_with_rc;
// global variable to indicate a2dp is initialized
bool g_a2dp_on_init;
@ -127,6 +127,8 @@ static btc_av_cb_t *btc_av_cb_ptr = NULL;
case BTA_AV_META_MSG_EVT: \
case BTA_AV_RC_FEAT_EVT: \
case BTA_AV_REMOTE_RSP_EVT: \
case BTA_AV_CA_STATUS_EVT: \
case BTA_AV_CA_DATA_EVT: \
{ \
btc_rc_handler(e, d);\
}break; \
@ -382,6 +384,8 @@ static BOOLEAN btc_av_state_idle_handler(btc_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_CA_STATUS_EVT:
case BTA_AV_CA_DATA_EVT:
btc_rc_handler(event, (tBTA_AV *)p_data);
break;
@ -1355,7 +1359,7 @@ static void bte_av_media_callback(tBTA_AV_EVT event, tBTA_AV_MEDIA *p_data)
/* send a command to BT Media Task */
btc_a2dp_sink_reset_decoder((UINT8 *)p_data);
/* currently only supportes SBC */
/* currently only supports SBC */
a2d_status = A2D_ParsSbcInfo(&sbc_cie, (UINT8 *)p_data, FALSE);
if (a2d_status == A2D_SUCCESS) {
btc_msg_t msg;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -40,7 +40,7 @@ static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 c
** Static variables
******************************************************************************/
/* flag indicating wheter TG/CT is initialized */
/* flag indicating whether TG/CT is initialized */
static uint32_t s_rc_ct_init;
static uint32_t s_rc_tg_init;
@ -157,6 +157,11 @@ bool btc_avrc_ct_rn_evt_supported(uint8_t event_id)
true : false;
}
bool btc_avrc_ct_check_cover_art_support(void)
{
return (btc_rc_cb.rc_features & BTA_AV_FEAT_COVER_ART);
}
bool btc_avrc_tg_init_p(void)
{
return (s_rc_tg_init == BTC_RC_TG_INIT_MAGIC);
@ -181,6 +186,44 @@ bool btc_avrc_ct_connected_p(void)
(btc_rc_cb.rc_features & BTA_AV_FEAT_RCTG);
}
void btc_avrc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
{
btc_avrc_args_t *dst = (btc_avrc_args_t *)p_dest;
btc_avrc_args_t *src = (btc_avrc_args_t *)p_src;
size_t len;
switch (msg->act) {
case BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_EVT:
len = src->ca_get_img.image_descriptor_len;
dst->ca_get_img.image_descriptor = (uint8_t *)osi_malloc(len);
if (dst->ca_get_img.image_descriptor) {
memcpy(dst->ca_get_img.image_descriptor, src->ca_get_img.image_descriptor, len);
} else {
BTC_TRACE_ERROR("%s %d no mem\n", __FUNCTION__, msg->act);
}
break;
default:
BTC_TRACE_DEBUG("%s Unhandled deep copy %d\n", __FUNCTION__, msg->act);
break;
}
}
void btc_avrc_arg_deep_free(btc_msg_t *msg)
{
btc_avrc_args_t *arg = (btc_avrc_args_t *)msg->arg;
switch (msg->act) {
case BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_EVT:
if (arg->ca_get_img.image_descriptor) {
osi_free(arg->ca_get_img.image_descriptor);
}
break;
default:
BTC_TRACE_DEBUG("%s Unhandled deep free %d\n", __FUNCTION__, msg->act);
break;
}
}
void btc_avrc_tg_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
{
btc_avrc_tg_args_t *dst = (btc_avrc_tg_args_t *) p_dest;
@ -495,10 +538,12 @@ static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close)
}
tBTA_AV_FEAT rc_features = btc_rc_cb.rc_features;
bool cover_art_connected = btc_rc_cb.rc_cover_art_connected;
// clean up the state
btc_rc_cb.rc_handle = 0;
btc_rc_cb.rc_connected = FALSE;
btc_rc_cb.rc_cover_art_connected = FALSE;
btc_rc_cb.rc_features = 0;
btc_rc_cb.rc_ct_features = 0;
@ -507,6 +552,15 @@ static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close)
memset(btc_rc_cb.rc_ntf, 0, sizeof(btc_rc_cb.rc_ntf));
/* report connection state */
if (cover_art_connected) {
/* if rc disconnect, cover art disconnect too */
esp_avrc_ct_cb_param_t param;
memset(&param, 0, sizeof(esp_avrc_ct_cb_param_t));
param.cover_art_state.state = ESP_AVRC_COVER_ART_DISCONNECTED;
param.cover_art_state.reason = BT_STATUS_FAIL;
btc_avrc_ct_cb_to_app(ESP_AVRC_CT_COVER_ART_STATE_EVT, &param);
}
if (rc_features & BTA_AV_FEAT_RCTG) {
esp_avrc_ct_cb_param_t param;
memset(&param, 0, sizeof(esp_avrc_ct_cb_param_t));
@ -751,7 +805,7 @@ static void btc_rc_upstreams_evt(UINT16 event, tAVRC_COMMAND *pavrc_cmd, UINT8 c
btc_rc_cb.rc_ntf[event_id - 1].registered = TRUE;
btc_rc_cb.rc_ntf[event_id - 1].label = label;
BTC_TRACE_EVENT("%s: New registerd notification: event_id:0x%x, label:0x%x",
BTC_TRACE_EVENT("%s: New registered notification: event_id:0x%x, label:0x%x",
__FUNCTION__, event_id, label);
// set up callback
@ -967,6 +1021,34 @@ void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data)
handle_rc_passthrough_cmd(&p_data->remote_cmd);
}
break;
case BTA_AV_CA_STATUS_EVT: {
btc_rc_cb.rc_cover_art_connected = p_data->ca_status.connected;
esp_avrc_ct_cb_param_t param;
memset(&param, 0, sizeof(esp_avrc_ct_cb_param_t));
if (p_data->ca_status.connected) {
param.cover_art_state.state = ESP_AVRC_COVER_ART_CONNECTED;
}
else {
param.cover_art_state.state = ESP_AVRC_COVER_ART_DISCONNECTED;
}
param.cover_art_state.reason = p_data->ca_status.reason;
btc_avrc_ct_cb_to_app(ESP_AVRC_CT_COVER_ART_STATE_EVT, &param);
}
break;
case BTA_AV_CA_DATA_EVT: {
esp_avrc_ct_cb_param_t param;
memset(&param, 0, sizeof(esp_avrc_ct_cb_param_t));
param.cover_art_data.status = p_data->ca_data.status;
param.cover_art_data.final = p_data->ca_data.final;
param.cover_art_data.data_len = p_data->ca_data.data_len;
param.cover_art_data.p_data = p_data->ca_data.p_data;
btc_avrc_ct_cb_to_app(ESP_AVRC_CT_COVER_ART_DATA_EVT, &param);
/* free the data packet now */
if (p_data->ca_data.p_hdr != NULL) {
osi_free(p_data->ca_data.p_hdr);
}
}
break;
default:
BTC_TRACE_DEBUG("Unhandled RC event : 0x%x", event);
}
@ -1041,7 +1123,7 @@ static void btc_avrc_ct_deinit(void)
BTC_TRACE_API("## %s ##", __FUNCTION__);
if (g_a2dp_on_deinit) {
BTC_TRACE_WARNING("A2DP already deinit, AVRC CT shuold deinit in advance of A2DP !!!");
BTC_TRACE_WARNING("A2DP already deinit, AVRC CT should deinit in advance of A2DP !!!");
}
if (s_rc_ct_init != BTC_RC_CT_INIT_MAGIC) {
@ -1255,7 +1337,7 @@ static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code
BTA_AvRemoteCmd(btc_rc_cb.rc_handle, tl,
(tBTA_AV_RC)key_code, (tBTA_AV_STATE)key_state);
status = BT_STATUS_SUCCESS;
BTC_TRACE_API("%s: succesfully sent passthrough command to BTA", __FUNCTION__);
BTC_TRACE_API("%s: successfully sent passthrough command to BTA", __FUNCTION__);
} else {
status = BT_STATUS_FAIL;
BTC_TRACE_DEBUG("%s: feature not supported", __FUNCTION__);
@ -1267,6 +1349,60 @@ static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code
return status;
}
static void btc_avrc_ct_cover_art_connect(UINT16 mtu)
{
if (!btc_rc_cb.rc_cover_art_connected) {
BTA_AvCaOpen(btc_rc_cb.rc_handle, mtu);
}
else {
BTC_TRACE_WARNING("%s: cover art already connected", __FUNCTION__);
}
return;
}
static void btc_avrc_ct_cover_art_disconnect(void)
{
if (btc_rc_cb.rc_cover_art_connected) {
BTA_AvCaClose(btc_rc_cb.rc_handle);
}
else {
BTC_TRACE_WARNING("%s: cover art not connected", __FUNCTION__);
}
return;
}
static void btc_avrc_ct_cover_art_get_image_properties(UINT8 *image_handle)
{
if (btc_rc_cb.rc_cover_art_connected) {
BTA_AvCaGet(btc_rc_cb.rc_handle, BTA_AV_CA_GET_IMAGE_PROPERTIES, image_handle, NULL, 0);
}
else {
BTC_TRACE_WARNING("%s: cover art not connected", __FUNCTION__);
}
return;
}
static void btc_avrc_ct_cover_art_get_image(UINT8 *image_handle, UINT8 *image_descriptor, UINT16 image_descriptor_len)
{
if (btc_rc_cb.rc_cover_art_connected) {
BTA_AvCaGet(btc_rc_cb.rc_handle, BTA_AV_CA_GET_IMAGE, image_handle, image_descriptor, image_descriptor_len);
}
else {
BTC_TRACE_WARNING("%s: cover art not connected", __FUNCTION__);
}
return;
}
static void btc_avrc_ct_cover_art_get_linked_thumbnail(UINT8 *image_handle)
{
if (btc_rc_cb.rc_cover_art_connected) {
BTA_AvCaGet(btc_rc_cb.rc_handle, BTA_AV_CA_GET_LINKED_THUMBNAIL, image_handle, NULL, 0);
}
else {
BTC_TRACE_WARNING("%s: cover art not connected", __FUNCTION__);
}
return;
}
/*******************************************************************************
**
@ -1298,7 +1434,7 @@ static void btc_avrc_tg_init(void)
}
if (g_a2dp_on_init) {
BTC_TRACE_WARNING("AVRC Taget is expected to be initialized in advance of A2DP !!!");
BTC_TRACE_WARNING("AVRC Target is expected to be initialized in advance of A2DP !!!");
}
}
@ -1320,7 +1456,7 @@ static void btc_avrc_tg_deinit(void)
BTC_TRACE_API("## %s ##", __FUNCTION__);
if (g_a2dp_on_deinit) {
BTC_TRACE_WARNING("A2DP already deinit, AVRC TG shuold deinit in advance of A2DP !!!");
BTC_TRACE_WARNING("A2DP already deinit, AVRC TG should deinit in advance of A2DP !!!");
}
if (s_rc_tg_init != BTC_RC_TG_INIT_MAGIC) {
@ -1418,6 +1554,26 @@ void btc_avrc_ct_call_handler(btc_msg_t *msg)
btc_avrc_ct_send_set_absolute_volume_cmd(arg->set_abs_vol_cmd.tl, arg->set_abs_vol_cmd.volume);
break;
}
case BTC_AVRC_CT_API_COVER_ART_CONNECT_EVT: {
btc_avrc_ct_cover_art_connect(arg->ca_conn.mtu);
break;
}
case BTC_AVRC_CT_API_COVER_ART_DISCONNECT_EVT: {
btc_avrc_ct_cover_art_disconnect();
break;
}
case BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_PROPERTIES_EVT: {
btc_avrc_ct_cover_art_get_image_properties(arg->ca_get_img_prop.image_handle);
break;
}
case BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_EVT: {
btc_avrc_ct_cover_art_get_image(arg->ca_get_img.image_handle, arg->ca_get_img.image_descriptor, arg->ca_get_img.image_descriptor_len);
break;
}
case BTC_AVRC_CT_API_COVER_ART_GET_LINKED_THUMBNAIL_EVT: {
btc_avrc_ct_cover_art_get_linked_thumbnail(arg->ca_get_lk_thn.image_handle);
break;
}
default:
BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act);
}

View File

@ -39,7 +39,12 @@ typedef enum {
BTC_AVRC_STATUS_API_SND_GET_RN_CAPS_EVT,
BTC_AVRC_NOTIFY_API_SND_REG_NOTIFY_EVT,
BTC_AVRC_CTRL_API_SND_SET_PLAYER_SETTING_EVT,
BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT
BTC_AVRC_CTRL_API_SND_SET_ABSOLUTE_VOLUME_EVT,
BTC_AVRC_CT_API_COVER_ART_CONNECT_EVT,
BTC_AVRC_CT_API_COVER_ART_DISCONNECT_EVT,
BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_PROPERTIES_EVT,
BTC_AVRC_CT_API_COVER_ART_GET_IMAGE_EVT,
BTC_AVRC_CT_API_COVER_ART_GET_LINKED_THUMBNAIL_EVT,
} btc_avrc_act_t;
typedef struct {
@ -77,6 +82,24 @@ typedef struct {
uint8_t volume;
} set_abs_vol_cmd_t;
typedef struct {
uint16_t mtu;
} ca_conn_t;
typedef struct {
uint8_t image_handle[7];
} ca_get_img_prop_t;
typedef struct {
uint8_t image_handle[7];
uint16_t image_descriptor_len;
uint8_t *image_descriptor;
} ca_get_img_t;
typedef struct {
uint8_t image_handle[7];
} ca_get_lk_thn_t;
/* btc_avrc_args_t */
typedef union {
pt_cmd_t pt_cmd;
@ -85,6 +108,10 @@ typedef union {
ps_cmd_t ps_cmd;
get_caps_cmd_t get_caps_cmd;
set_abs_vol_cmd_t set_abs_vol_cmd;
ca_conn_t ca_conn;
ca_get_img_prop_t ca_get_img_prop;
ca_get_img_t ca_get_img;
ca_get_lk_thn_t ca_get_lk_thn;
} btc_avrc_args_t;
/* btc_avrc_tg_act_t */
@ -124,6 +151,7 @@ typedef struct {
typedef struct {
BOOLEAN rc_connected;
BOOLEAN rc_cover_art_connected;
UINT8 rc_handle;
tBTA_AV_FEAT rc_features;
UINT16 rc_ct_features;
@ -162,6 +190,8 @@ BOOLEAN btc_rc_get_connected_peer(BD_ADDR peer_addr);
********************************************************************************/
void btc_avrc_ct_call_handler(btc_msg_t *msg);
void btc_avrc_tg_call_handler(btc_msg_t *msg);
void btc_avrc_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
void btc_avrc_arg_deep_free(btc_msg_t *msg);
void btc_avrc_tg_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
void btc_avrc_tg_arg_deep_free(btc_msg_t *msg);
@ -179,6 +209,7 @@ uint16_t btc_avrc_tg_get_rn_supported_evt(void);
bool btc_avrc_tg_check_rn_supported_evt(uint16_t evt_set);
bool btc_avrc_tg_rn_evt_supported(uint8_t event_id);
bool btc_avrc_ct_rn_evt_supported(uint8_t event_id);
bool btc_avrc_ct_check_cover_art_support(void);
#endif ///BTC_AV_INCLUDED == TRUE

View File

@ -171,6 +171,9 @@
#define BTC_HD_INCLUDED TRUE
#endif /* UC_BT_HID_DEVICE_ENABLED */
#define OBEX_INCLUDED TRUE
#define GOEPC_INCLUDED TRUE
#endif /* UC_BT_CLASSIC_ENABLED */
/* This is set to enable use of GAP L2CAP connections. */
@ -2161,6 +2164,20 @@
#define HID_HOST_REPAGE_WIN (2)
#endif
/*************************************************************************
** Definitions for OBEX
*/
#ifndef OBEX_INCLUDED
#define OBEX_INCLUDED FALSE
#endif
/*************************************************************************
** Definitions for OBEX
*/
#ifndef GOEPC_INCLUDED
#define GOEPC_INCLUDED FALSE
#endif
/*************************************************************************
* A2DP Definitions
*/

View File

@ -316,6 +316,27 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l
#define AVRC_TRACE_EVENT(fmt, args...) {if (avrc_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("BT_AVRC", fmt, ## args);}
#define AVRC_TRACE_DEBUG(fmt, args...) {if (avrc_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("BT_AVRC", fmt, ## args);}
/* Define tracing for OBEX */
#define OBEX_TRACE_ERROR(fmt, args...) {if (obex_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVRC, ERROR)) BT_PRINT_E("BT_OBEX", fmt, ## args);}
#define OBEX_TRACE_WARNING(fmt, args...) {if (obex_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVRC, WARNING)) BT_PRINT_W("BT_OBEX", fmt, ## args);}
#define OBEX_TRACE_API(fmt, args...) {if (obex_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVRC,API)) BT_PRINT_I("BT_OBEX", fmt, ## args);}
#define OBEX_TRACE_EVENT(fmt, args...) {if (obex_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("BT_OBEX", fmt, ## args);}
#define OBEX_TRACE_DEBUG(fmt, args...) {if (obex_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("BT_OBEX", fmt, ## args);}
/* Define tracing for OBEX_TL_L2CAP */
#define OBEX_TL_L2CAP_TRACE_ERROR(fmt, args...) {if (obex_tl_l2cap_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVRC, ERROR)) BT_PRINT_E("OBEX_TL_L2CAP", fmt, ## args);}
#define OBEX_TL_L2CAP_TRACE_WARNING(fmt, args...) {if (obex_tl_l2cap_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVRC, WARNING)) BT_PRINT_W("OBEX_TL_L2CAP", fmt, ## args);}
#define OBEX_TL_L2CAP_TRACE_API(fmt, args...) {if (obex_tl_l2cap_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVRC,API)) BT_PRINT_I("OBEX_TL_L2CAP", fmt, ## args);}
#define OBEX_TL_L2CAP_TRACE_EVENT(fmt, args...) {if (obex_tl_l2cap_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("OBEX_TL_L2CAP", fmt, ## args);}
#define OBEX_TL_L2CAP_TRACE_DEBUG(fmt, args...) {if (obex_tl_l2cap_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("OBEX_TL_L2CAP", fmt, ## args);}
/* Define tracing for GOEPC */
#define GOEPC_TRACE_ERROR(fmt, args...) {if (goepc_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVRC, ERROR)) BT_PRINT_E("BT_GOEPC", fmt, ## args);}
#define GOEPC_TRACE_WARNING(fmt, args...) {if (goepc_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVRC, WARNING)) BT_PRINT_W("BT_GOEPC", fmt, ## args);}
#define GOEPC_TRACE_API(fmt, args...) {if (goepc_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVRC,API)) BT_PRINT_I("BT_GOEPC", fmt, ## args);}
#define GOEPC_TRACE_EVENT(fmt, args...) {if (goepc_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("BT_GOEPC", fmt, ## args);}
#define GOEPC_TRACE_DEBUG(fmt, args...) {if (goepc_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("BT_GOEPC", fmt, ## args);}
/* MCAP
*/
#define MCA_TRACE_ERROR(fmt, args...) {if (mca_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(MCA, ERROR)) BT_PRINT_E("BT_MCA", fmt, ## args);}
@ -484,6 +505,26 @@ extern UINT8 btif_trace_level;
#define AVRC_TRACE_DEBUG(fmt, args...)
#define AVRC_TRACE_API(fmt, args...)
/* Define tracing for OBEX */
#define OBEX_TRACE_ERROR(fmt, args...)
#define OBEX_TRACE_WARNING(fmt, args...)
#define OBEX_TRACE_API(fmt, args...)
#define OBEX_TRACE_EVENT(fmt, args...)
#define OBEX_TRACE_DEBUG(fmt, args...)
#define OBEX_TL_L2CAP_TRACE_ERROR(fmt, args...)
#define OBEX_TL_L2CAP_TRACE_WARNING(fmt, args...)
#define OBEX_TL_L2CAP_TRACE_API(fmt, args...)
#define OBEX_TL_L2CAP_TRACE_EVENT(fmt, args...)
#define OBEX_TL_L2CAP_TRACE_DEBUG(fmt, args...)
/* Define tracing for GOEPC */
#define GOEPC_TRACE_ERROR(fmt, args...)
#define GOEPC_TRACE_WARNING(fmt, args...)
#define GOEPC_TRACE_API(fmt, args...)
#define GOEPC_TRACE_EVENT(fmt, args...)
#define GOEPC_TRACE_DEBUG(fmt, args...)
/* MCAP
*/
#define MCA_TRACE_ERROR(fmt, args...)

View File

@ -27,8 +27,8 @@
#include <string.h>
/* Stack Configuation Related Init Definaton
* TODO: Now Just Unmask these defination until stack layer is OK
/* Stack Configuration Related Init Definaton
* TODO: Now Just Unmask these definition until stack layer is OK
*/
#ifndef BTA_INCLUDED
@ -91,6 +91,15 @@
#endif
#endif
#if (defined(OBEX_INCLUDED) && OBEX_INCLUDED == TRUE)
#include "stack/obex_api.h"
#endif
#if (defined(GOEPC_INCLUDED) && GOEPC_INCLUDED == TRUE)
#include "stack/goep_common.h"
#include "stack/goepc_api.h"
#endif
//BTA Modules
#if BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE
#include "bta/bta_api.h"
@ -267,6 +276,14 @@ void BTE_DeinitStack(void)
}
#endif // BTA_INCLUDED == TRUE
#if (defined(GOEPC_INCLUDED) && GOEPC_INCLUDED == TRUE)
GOEPC_Deinit();
#endif
#if (defined(OBEX_INCLUDED) && OBEX_INCLUDED == TRUE)
OBEX_Deinit();
#endif
#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE)
HID_DevDeinit();
#endif
@ -388,6 +405,18 @@ bt_status_t BTE_InitStack(void)
MCA_Init();
#endif
#if (defined(OBEX_INCLUDED) && OBEX_INCLUDED == TRUE)
if (OBEX_Init() != OBEX_SUCCESS) {
goto error_exit;
}
#endif
#if (defined(GOEPC_INCLUDED) && GOEPC_INCLUDED == TRUE)
if (GOEPC_Init() != GOEP_SUCCESS) {
goto error_exit;
}
#endif
//BTA Modules
#if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE)
if ((bta_sys_cb_ptr = (tBTA_SYS_CB *)osi_malloc(sizeof(tBTA_SYS_CB))) == NULL) {

View File

@ -29,8 +29,8 @@
#if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE)
#ifndef SDP_AVRCP_1_5
#define SDP_AVRCP_1_5 TRUE
#ifndef SDP_AVRCP_1_6
#define SDP_AVRCP_1_6 TRUE
#endif
#ifndef SDP_AVCTP_1_4
@ -52,7 +52,7 @@ 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_4 == TRUE || SDP_AVRCP_1_5 == TRUE)
#if SDP_AVRCP_1_6 == TRUE
{UUID_PROTOCOL_AVCTP, 1, {AVCT_REV_1_3, 0} }
#else
#if AVRC_METADATA_INCLUDED == TRUE
@ -64,7 +64,7 @@ const tSDP_PROTOCOL_ELEM avrc_proto_list [] = {
#endif
};
#if SDP_AVRCP_1_5 == TRUE
#if SDP_AVRCP_1_6 == TRUE
const tSDP_PROTO_LIST_ELEM avrc_add_proto_list [] = {
{
AVRC_NUM_PROTO_ELEMS,
@ -251,7 +251,7 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provide
/* add service class id list */
class_list[0] = service_uuid;
#if (SDP_AVCTP_1_4 == TRUE || SDP_AVRCP_1_5 == TRUE)
#if (SDP_AVCTP_1_4 == TRUE || SDP_AVRCP_1_6 == TRUE)
if ( service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL ) {
class_list[1] = UUID_SERVCLASS_AV_REM_CTRL_CONTROL;
count = 2;
@ -263,7 +263,7 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provide
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
#if SDP_AVRCP_1_6 == TRUE
if (browsing_en) {
add_additional_protocol_list = TRUE;
} else if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET &&
@ -277,7 +277,7 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provide
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_5);
result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_6);
#else
#if AVRC_METADATA_INCLUDED == TRUE
result &= SDP_AddProfileDescriptorList(sdp_handle, UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_3);
@ -292,6 +292,13 @@ UINT16 AVRC_AddRecord(UINT16 service_uuid, char *p_service_name, char *p_provide
} else if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_TARGET && media_player_virtual_filesystem_supported) {
supported_feature |= AVRC_SUPF_TG_BROWSE;
}
if (service_uuid == UUID_SERVCLASS_AV_REM_CTRL_CONTROL || service_uuid == UUID_SERVCLASS_AV_REMOTE_CONTROL) {
supported_feature |= AVRC_SUPF_CT_COVER_ART_GIP;
supported_feature |= AVRC_SUPF_CT_COVER_ART_GI;
supported_feature |= AVRC_SUPF_CT_COVER_ART_GLT;
}
/* add supported feature */
p = temp;
UINT16_TO_BE_STREAM(p, supported_feature);
@ -383,7 +390,7 @@ bt_status_t AVRC_Init(void)
**
** Function AVRC_Deinit
**
** Description This function is called at stack shotdown to free the
** Description This function is called at stack shutdown to free the
** control block (if using dynamic memory), and deinitializes the
** control block and tracing level.
**

View File

@ -99,6 +99,9 @@
#define AVRC_SUPF_CT_CAT3 0x0004 /* Category 3 */
#define AVRC_SUPF_CT_CAT4 0x0008 /* Category 4 */
#define AVRC_SUPF_CT_BROWSE 0x0040 /* Browsing */
#define AVRC_SUPF_CT_COVER_ART_GIP 0x0080 /* Cover Art GetImageProperties */
#define AVRC_SUPF_CT_COVER_ART_GI 0x0100 /* Cover Art GetImage */
#define AVRC_SUPF_CT_COVER_ART_GLT 0x0200 /* Cover Art GetLinkedThumbnail */
#define AVRC_SUPF_TG_CAT1 0x0001 /* Category 1 */
#define AVRC_SUPF_TG_CAT2 0x0002 /* Category 2 */
@ -107,7 +110,8 @@
#define AVRC_SUPF_TG_APP_SETTINGS 0x0010 /* Player Application Settings */
#define AVRC_SUPF_TG_GROUP_NAVI 0x0020 /* Group Navigation */
#define AVRC_SUPF_TG_BROWSE 0x0040 /* Browsing */
#define AVRC_SUPF_TG_MULTI_PLAYER 0x0080 /* Muliple Media Player */
#define AVRC_SUPF_TG_MULTI_PLAYER 0x0080 /* Multiple Media Player */
#define AVRC_SUPF_TG_COVER_ART 0x0100 /* Cover Art */
#define AVRC_META_SUCCESS AVRC_SUCCESS
#define AVRC_META_FAIL AVRC_FAIL
@ -561,7 +565,7 @@ extern bt_status_t AVRC_Init(void);
**
** Function AVRC_Deinit
**
** Description This function is called at stack shotdown to free the
** Description This function is called at stack shutdown to free the
** control block (if using dynamic memory), and deinitializes the
** control block and tracing level.
**

View File

@ -35,6 +35,7 @@
#define AVRC_REV_1_3 0x0103
#define AVRC_REV_1_4 0x0104
#define AVRC_REV_1_5 0x0105
#define AVRC_REV_1_6 0x0106
#define AVRC_PACKET_LEN 512 /* Per the spec, you must support 512 byte RC packets */
@ -185,7 +186,7 @@
#define AVRC_PKT_END 3
#define AVRC_PKT_TYPE_MASK 3
/* Define the PDUs carried in the vendor dependant data
/* Define the PDUs carried in the vendor dependent data
*/
#define AVRC_PDU_GET_CAPABILITIES 0x10
#define AVRC_PDU_LIST_PLAYER_APP_ATTR 0x11
@ -297,7 +298,7 @@ typedef UINT8 tAVRC_BATTERY_STATUS;
#define AVRC_MEDIA_ATTR_ID_TRACK_NUM 0x00000004
#define AVRC_MEDIA_ATTR_ID_NUM_TRACKS 0x00000005
#define AVRC_MEDIA_ATTR_ID_GENRE 0x00000006
#define AVRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in miliseconds */
#define AVRC_MEDIA_ATTR_ID_PLAYING_TIME 0x00000007 /* in milliseconds */
#define AVRC_MAX_NUM_MEDIA_ATTR_ID 7
/* Define the possible values of play state

View File

@ -45,6 +45,8 @@
#define HCRP_DYNAMIC_MEMORY TRUE
#define HFP_DYNAMIC_MEMORY TRUE
#define HID_DYNAMIC_MEMORY TRUE
#define OBEX_DYNAMIC_MEMORY TRUE
#define GOEP_DYNAMIC_MEMORY TRUE
#define HSP2_DYNAMIC_MEMORY TRUE
#define ICP_DYNAMIC_MEMORY TRUE
#define OPP_DYNAMIC_MEMORY TRUE
@ -79,6 +81,8 @@
#define HCRP_DYNAMIC_MEMORY FALSE
#define HFP_DYNAMIC_MEMORY FALSE
#define HID_DYNAMIC_MEMORY FALSE
#define OBEX_DYNAMIC_MEMORY FALSE
#define GOEP_DYNAMIC_MEMORY FALSE
#define HSP2_DYNAMIC_MEMORY FALSE
#define ICP_DYNAMIC_MEMORY FALSE
#define OPP_DYNAMIC_MEMORY FALSE
@ -191,6 +195,14 @@
#define HID_DYNAMIC_MEMORY FALSE
#endif
#ifndef OBEX_DYNAMIC_MEMORY
#define OBEX_DYNAMIC_MEMORY FALSE
#endif
#ifndef GOEP_DYNAMIC_MEMORY
#define GOEP_DYNAMIC_MEMORY FALSE
#endif
#ifndef HSP2_DYNAMIC_MEMORY
#define HSP2_DYNAMIC_MEMORY FALSE
#endif

View File

@ -365,6 +365,20 @@ extern BOOLEAN SDP_FindProtocolListElemInRec (tSDP_DISC_REC *p_rec,
UINT16 layer_uuid,
tSDP_PROTOCOL_ELEM *p_elem);
/*******************************************************************************
**
** Function SDP_FindProtocolListElem
**
** Description This function looks at the protocol list for a specific protocol
** list element.
**
** Returns TRUE if found, FALSE if not
** If found, the passed protocol list element is filled in.
**
*******************************************************************************/
extern BOOLEAN SDP_FindProtocolListElem (tSDP_DISC_ATTR *p_protocol_list,
UINT16 layer_uuid,
tSDP_PROTOCOL_ELEM *p_elem);
/*******************************************************************************
**
@ -409,7 +423,7 @@ extern BOOLEAN SDP_FindProfileVersionInRec (tSDP_DISC_REC *p_rec,
**
** Description This function is called to create a record in the database.
** This would be through the SDP database maintenance API. The
** record is created empty, teh application should then call
** record is created empty, the application should then call
** "add_attribute" to add the record's attributes.
**
** Returns Record handle if OK, else 0.

View File

@ -372,7 +372,7 @@ BOOLEAN SDP_FindServiceUUIDInRec(tSDP_DISC_REC *p_rec, tBT_UUID *p_uuid)
if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) {
/* Look through data element sequence until no more UUIDs */
for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) {
/* Increment past this to see if the next attribut is UUID */
/* Increment past this to see if the next attribute is UUID */
if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE)
/* only support 16 bits UUID for now */
&& (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)) {
@ -522,7 +522,7 @@ tSDP_DISC_REC *SDP_FindServiceInDb (tSDP_DISCOVERY_DB *p_db, UINT16 service_uuid
if (SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE) {
/* Look through data element sequence until no more UUIDs */
for (p_extra_sattr = p_sattr->attr_value.v.p_sub_attr; p_extra_sattr; p_extra_sattr = p_extra_sattr->p_next_attr) {
/* Increment past this to see if the next attribut is UUID */
/* Increment past this to see if the next attribute is UUID */
if ((SDP_DISC_ATTR_TYPE(p_extra_sattr->attr_len_type) == UUID_DESC_TYPE)
&& (SDP_DISC_ATTR_LEN(p_extra_sattr->attr_len_type) == 2)
/* for a specific uuid, or any one */
@ -768,6 +768,28 @@ BOOLEAN SDP_FindProtocolListElemInRec (tSDP_DISC_REC *p_rec, UINT16 layer_uuid,
return (FALSE);
}
/*******************************************************************************
**
** Function SDP_FindProtocolListElem
**
** Description This function looks at the protocol list for a specific protocol
** list element.
**
** Returns TRUE if found, FALSE if not
** If found, the passed protocol list element is filled in.
**
*******************************************************************************/
BOOLEAN SDP_FindProtocolListElem (tSDP_DISC_ATTR *p_protocol_list, UINT16 layer_uuid, tSDP_PROTOCOL_ELEM *p_elem)
{
#if SDP_CLIENT_ENABLED == TRUE
/* don't check the input protocol descriptor list id, this api may be use in additional protocol descriptor list */
if ((SDP_DISC_ATTR_TYPE(p_protocol_list->attr_len_type) == DATA_ELE_SEQ_DESC_TYPE)) {
return sdp_fill_proto_elem(p_protocol_list, layer_uuid, p_elem);
}
#endif
/* If here, no match found */
return (FALSE);
}
/*******************************************************************************
**