From cb0d073551e98b9185f4088b9c9d257a48f95f9a Mon Sep 17 00:00:00 2001 From: linruihao Date: Fri, 9 Aug 2024 16:30:17 +0800 Subject: [PATCH] feat(bt/bluedroid): Support AVRCP Cover Art feature --- .codespellrc | 2 +- components/bt/CMakeLists.txt | 9 + .../bt/host/bluedroid/api/esp_avrc_api.c | 141 ++++- .../bluedroid/api/include/api/esp_avrc_api.h | 132 ++++- .../bt/host/bluedroid/bta/av/bta_av_act.c | 56 +- .../bt/host/bluedroid/bta/av/bta_av_api.c | 69 +++ .../bt/host/bluedroid/bta/av/bta_av_ca_act.c | 495 ++++++++++++++++++ .../bt/host/bluedroid/bta/av/bta_av_ca_sm.c | 170 ++++++ .../bt/host/bluedroid/bta/av/bta_av_main.c | 9 +- .../bluedroid/bta/av/include/bta_av_int.h | 94 +++- .../bluedroid/bta/include/bta/bta_av_api.h | 71 ++- .../host/bluedroid/bta/include/bta/bta_sys.h | 5 +- .../bluedroid/btc/profile/std/a2dp/btc_av.c | 10 +- .../bluedroid/btc/profile/std/avrc/btc_avrc.c | 170 +++++- .../btc/profile/std/include/btc_avrc.h | 33 +- .../common/include/common/bt_target.h | 17 + .../common/include/common/bt_trace.h | 41 ++ components/bt/host/bluedroid/main/bte_init.c | 33 +- .../bt/host/bluedroid/stack/avrc/avrc_sdp.c | 23 +- .../bluedroid/stack/include/stack/avrc_api.h | 8 +- .../bluedroid/stack/include/stack/avrc_defs.h | 5 +- .../bluedroid/stack/include/stack/dyn_mem.h | 12 + .../bluedroid/stack/include/stack/sdp_api.h | 16 +- .../bt/host/bluedroid/stack/sdp/sdp_api.c | 26 +- 24 files changed, 1596 insertions(+), 51 deletions(-) create mode 100644 components/bt/host/bluedroid/bta/av/bta_av_ca_act.c create mode 100644 components/bt/host/bluedroid/bta/av/bta_av_ca_sm.c diff --git a/.codespellrc b/.codespellrc index 99cb99a9d7..493a2c9aa6 100644 --- a/.codespellrc +++ b/.codespellrc @@ -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 diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index cc61e549fb..f279585217 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -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" diff --git a/components/bt/host/bluedroid/api/esp_avrc_api.c b/components/bt/host/bluedroid/api/esp_avrc_api.c index 2d3e15325f..0827848dbf 100644 --- a/components/bt/host/bluedroid/api/esp_avrc_api.c +++ b/components/bt/host/bluedroid/api/esp_avrc_api.c @@ -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 **/ /*********************************************************************************************/ diff --git a/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h b/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h index f226577efc..99a189aa61 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h @@ -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 @@ -133,8 +141,10 @@ typedef enum { ESP_AVRC_CT_PLAY_STATUS_RSP_EVT = 3, /*!< play status response event */ ESP_AVRC_CT_CHANGE_NOTIFY_EVT = 4, /*!< notification event */ 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_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 diff --git a/components/bt/host/bluedroid/bta/av/bta_av_act.c b/components/bt/host/bluedroid/bta/av/bta_av_act.c index 13a5750713..f9268cd2b4 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_act.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_act.c @@ -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; diff --git a/components/bt/host/bluedroid/bta/av/bta_av_api.c b/components/bt/host/bluedroid/bta/av/bta_av_api.c index 01993eec8e..115b71f27d 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_api.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_api.c @@ -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 */ diff --git a/components/bt/host/bluedroid/bta/av/bta_av_ca_act.c b/components/bt/host/bluedroid/bta/av/bta_av_ca_act.c new file mode 100644 index 0000000000..cdd439cf3d --- /dev/null +++ b/components/bt/host/bluedroid/bta/av/bta_av_ca_act.c @@ -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 +#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 */ diff --git a/components/bt/host/bluedroid/bta/av/bta_av_ca_sm.c b/components/bt/host/bluedroid/bta/av/bta_av_ca_sm.c new file mode 100644 index 0000000000..d01c5baf90 --- /dev/null +++ b/components/bt/host/bluedroid/bta/av/bta_av_ca_sm.c @@ -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 +#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 */ diff --git a/components/bt/host/bluedroid/bta/av/bta_av_main.c b/components/bt/host/bluedroid/bta/av/bta_av_main.c index 2fde06f61b..8e8cd145ad 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_main.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_main.c @@ -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); diff --git a/components/bt/host/bluedroid/bta/av/include/bta_av_int.h b/components/bt/host/bluedroid/bta/av/include/bta_av_int.h index 8c2554560d..82044c31b7 100644 --- a/components/bt/host/bluedroid/bta/av/include/bta_av_int.h +++ b/components/bt/host/bluedroid/bta/av/include/bta_av_int.h @@ -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 */ diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h index ddf72b9637..6e64af976f 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_av_api.h @@ -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 diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_sys.h b/components/bt/host/bluedroid/bta/include/bta/bta_sys.h index 54376de635..ecf4791995 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_sys.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_sys.h @@ -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 diff --git a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c index 4c237b1e50..d26ff5e757 100644 --- a/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c +++ b/components/bt/host/bluedroid/btc/profile/std/a2dp/btc_av.c @@ -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; diff --git a/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c b/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c index 7e7f1b2bb6..b89082f304 100644 --- a/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c +++ b/components/bt/host/bluedroid/btc/profile/std/avrc/btc_avrc.c @@ -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(¶m, 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, ¶m); + } + if (rc_features & BTA_AV_FEAT_RCTG) { esp_avrc_ct_cb_param_t param; memset(¶m, 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(¶m, 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, ¶m); + } + break; + case BTA_AV_CA_DATA_EVT: { + esp_avrc_ct_cb_param_t param; + memset(¶m, 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, ¶m); + /* 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); } diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h index 83200ae15e..b9cbbd2e4a 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_avrc.h @@ -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 diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 032d38eb41..2f84a83d9f 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -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 */ diff --git a/components/bt/host/bluedroid/common/include/common/bt_trace.h b/components/bt/host/bluedroid/common/include/common/bt_trace.h index a4f1ca749a..74b7f10574 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_trace.h +++ b/components/bt/host/bluedroid/common/include/common/bt_trace.h @@ -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...) diff --git a/components/bt/host/bluedroid/main/bte_init.c b/components/bt/host/bluedroid/main/bte_init.c index 80c4357b6a..77508b3777 100644 --- a/components/bt/host/bluedroid/main/bte_init.c +++ b/components/bt/host/bluedroid/main/bte_init.c @@ -27,8 +27,8 @@ #include -/* 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) { diff --git a/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c b/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c index fa98082e86..0be6663350 100644 --- a/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c +++ b/components/bt/host/bluedroid/stack/avrc/avrc_sdp.c @@ -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. ** diff --git a/components/bt/host/bluedroid/stack/include/stack/avrc_api.h b/components/bt/host/bluedroid/stack/include/stack/avrc_api.h index 21f4bee576..2985c65ca5 100644 --- a/components/bt/host/bluedroid/stack/include/stack/avrc_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/avrc_api.h @@ -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. ** diff --git a/components/bt/host/bluedroid/stack/include/stack/avrc_defs.h b/components/bt/host/bluedroid/stack/include/stack/avrc_defs.h index c90fa6cfeb..06f73c2969 100644 --- a/components/bt/host/bluedroid/stack/include/stack/avrc_defs.h +++ b/components/bt/host/bluedroid/stack/include/stack/avrc_defs.h @@ -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 diff --git a/components/bt/host/bluedroid/stack/include/stack/dyn_mem.h b/components/bt/host/bluedroid/stack/include/stack/dyn_mem.h index fa1ed4982a..9f7eae2076 100644 --- a/components/bt/host/bluedroid/stack/include/stack/dyn_mem.h +++ b/components/bt/host/bluedroid/stack/include/stack/dyn_mem.h @@ -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 diff --git a/components/bt/host/bluedroid/stack/include/stack/sdp_api.h b/components/bt/host/bluedroid/stack/include/stack/sdp_api.h index 70da3182a5..7075e8bbf3 100644 --- a/components/bt/host/bluedroid/stack/include/stack/sdp_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/sdp_api.h @@ -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. diff --git a/components/bt/host/bluedroid/stack/sdp/sdp_api.c b/components/bt/host/bluedroid/stack/sdp/sdp_api.c index 0103e4de01..9b477a1c85 100644 --- a/components/bt/host/bluedroid/stack/sdp/sdp_api.c +++ b/components/bt/host/bluedroid/stack/sdp/sdp_api.c @@ -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); +} /******************************************************************************* **