mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/support_bt_avrcp_cover_art' into 'master'
feat(bt/bluedroid): Support BT AVRCP Cover Art Closes BT-3231 See merge request espressif/esp-idf!32538
This commit is contained in:
commit
cda2846558
@ -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
|
||||
|
@ -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"
|
||||
|
@ -84,9 +84,30 @@ config BT_A2DP_ENABLE
|
||||
bool "A2DP"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
default n
|
||||
select BT_AVRCP_ENABLED
|
||||
help
|
||||
Advanced Audio Distribution Profile
|
||||
|
||||
config BT_AVRCP_ENABLED
|
||||
bool
|
||||
depends on BT_A2DP_ENABLE
|
||||
default y
|
||||
help
|
||||
Audio/Video Remote Control Profile, AVRCP and A2DP are coupled in Bluedroid,
|
||||
AVRCP still controlled by A2DP option, this is a dummy option currently
|
||||
|
||||
menu "AVRCP Features"
|
||||
depends on BT_AVRCP_ENABLED
|
||||
|
||||
config BT_AVRCP_CT_COVER_ART_ENABLED
|
||||
bool "AVRCP CT Cover Art"
|
||||
default y
|
||||
select BT_GOEPC_ENABLED
|
||||
help
|
||||
This enable Cover Art feature of AVRCP CT role
|
||||
|
||||
endmenu
|
||||
|
||||
config BT_SPP_ENABLED
|
||||
bool "SPP"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
@ -143,13 +164,12 @@ endchoice
|
||||
|
||||
config BT_HFP_WBS_ENABLE
|
||||
bool "Wide Band Speech"
|
||||
depends on BT_HFP_AUDIO_DATA_PATH_HCI
|
||||
depends on BT_HFP_ENABLE && BT_HFP_AUDIO_DATA_PATH_HCI
|
||||
default y
|
||||
help
|
||||
This enables Wide Band Speech. Should disable it when SCO data path is PCM.
|
||||
Otherwise there will be no data transmitted via GPIOs.
|
||||
|
||||
|
||||
menuconfig BT_HID_ENABLED
|
||||
bool "Classic BT HID"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
@ -170,6 +190,13 @@ config BT_HID_DEVICE_ENABLED
|
||||
help
|
||||
This enables the BT HID Device
|
||||
|
||||
config BT_GOEPC_ENABLED
|
||||
bool
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
default n
|
||||
help
|
||||
This enables the BT GOEP Profile Client role
|
||||
|
||||
config BT_BLE_ENABLED
|
||||
bool "Bluetooth Low Energy"
|
||||
depends on BT_BLUEDROID_ENABLED
|
||||
|
@ -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,149 @@ 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;
|
||||
}
|
||||
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif /* #if BTC_AV_CA_INCLUDED */
|
||||
|
||||
/*********************************************************************************************/
|
||||
/** following is the API of AVRCP target role **/
|
||||
/*********************************************************************************************/
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -18,6 +18,10 @@ extern "C" {
|
||||
|
||||
#define ESP_AVRC_TRANS_LABEL_MAX 15 /*!< max transaction label */
|
||||
|
||||
#define ESP_AVRC_CA_IMAGE_HANDLE_LEN 7 /* The image handle length is fixed to 7, specified by Basic Image Profile */
|
||||
#define ESP_AVRC_CA_MTU_MIN 255 /* Minimal MTU can be used in Cover Art OBEX connection */
|
||||
#define ESP_AVRC_CA_MTU_MAX 1691 /* Maximum MTU can be used in Cover Art OBEX connection */
|
||||
|
||||
/// AVRC feature bit mask
|
||||
typedef enum {
|
||||
ESP_AVRC_FEAT_RCTG = 0x0001, /*!< remote control target */
|
||||
@ -30,14 +34,18 @@ typedef enum {
|
||||
|
||||
/// AVRC supported features flag retrieved in SDP record
|
||||
typedef enum {
|
||||
/* CT and TG common features flag */
|
||||
ESP_AVRC_FEAT_FLAG_CAT1 = 0x0001, /*!< category 1 */
|
||||
ESP_AVRC_FEAT_FLAG_CAT2 = 0x0002, /*!< category 2 */
|
||||
ESP_AVRC_FEAT_FLAG_CAT3 = 0x0004, /*!< category 3 */
|
||||
ESP_AVRC_FEAT_FLAG_CAT4 = 0x0008, /*!< category 4 */
|
||||
ESP_AVRC_FEAT_FLAG_BROWSING = 0x0040, /*!< browsing */
|
||||
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE_PROP = 0x0080, /*!< Cover Art GetImageProperties */
|
||||
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE = 0x0100, /*!< Cover Art GetImage */
|
||||
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_LINKED_THUMBNAIL = 0x0200, /*!< Cover Art GetLinkedThumbnail */
|
||||
/* CT only features flag */
|
||||
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE_PROP = 0x0080, /*!< CT support Cover Art GetImageProperties */
|
||||
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_IMAGE = 0x0100, /*!< CT support Cover Art GetImage */
|
||||
ESP_AVRC_FEAT_FLAG_COVER_ART_GET_LINKED_THUMBNAIL = 0x0200, /*!< CT support Cover Art GetLinkedThumbnail */
|
||||
/* TG only features flag */
|
||||
ESP_AVRC_FEAT_FLAG_TG_COVER_ART = 0x0100, /*!< TG support Cover Art */
|
||||
} esp_avrc_feature_flag_t;
|
||||
|
||||
/// AVRC passthrough command code
|
||||
@ -135,6 +143,8 @@ typedef enum {
|
||||
ESP_AVRC_CT_REMOTE_FEATURES_EVT = 5, /*!< feature of remote device indication event */
|
||||
ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT = 6, /*!< supported notification events capability of peer device */
|
||||
ESP_AVRC_CT_SET_ABSOLUTE_VOLUME_RSP_EVT = 7, /*!< set absolute volume response event */
|
||||
ESP_AVRC_CT_COVER_ART_STATE_EVT = 8, /*!< cover art client connection state changed event */
|
||||
ESP_AVRC_CT_COVER_ART_DATA_EVT = 9, /*!< cover art client data event */
|
||||
} esp_avrc_ct_cb_event_t;
|
||||
|
||||
/// AVRC Target callback events
|
||||
@ -155,7 +165,8 @@ typedef enum {
|
||||
ESP_AVRC_MD_ATTR_TRACK_NUM = 0x8, /*!< track position on the album */
|
||||
ESP_AVRC_MD_ATTR_NUM_TRACKS = 0x10, /*!< number of tracks on the album */
|
||||
ESP_AVRC_MD_ATTR_GENRE = 0x20, /*!< track genre */
|
||||
ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40 /*!< total album playing time in miliseconds */
|
||||
ESP_AVRC_MD_ATTR_PLAYING_TIME = 0x40, /*!< total album playing time in milliseconds */
|
||||
ESP_AVRC_MD_ATTR_COVER_ART = 0x80 /*!< cover art image handle */
|
||||
} esp_avrc_md_attr_mask_t;
|
||||
|
||||
/// AVRC event notification ids
|
||||
@ -261,6 +272,12 @@ typedef enum {
|
||||
ESP_AVRC_PLAYBACK_ERROR = 0xFF, /*!< error */
|
||||
} esp_avrc_playback_stat_t;
|
||||
|
||||
/// AVRC Cover Art connection error code
|
||||
typedef enum {
|
||||
ESP_AVRC_COVER_ART_DISCONNECTED, /*!< Cover Art connection disconnected or connection failed */
|
||||
ESP_AVRC_COVER_ART_CONNECTED, /*!< Cover Art connection established */
|
||||
} esp_avrc_cover_art_conn_state_t;
|
||||
|
||||
/// AVRCP notification parameters
|
||||
typedef union
|
||||
{
|
||||
@ -337,6 +354,24 @@ typedef union {
|
||||
struct avrc_ct_set_volume_rsp_param {
|
||||
uint8_t volume; /*!< the volume which has actually been set, range is 0 to 0x7f, means 0% to 100% */
|
||||
} set_volume_rsp; /*!< set absolute volume response event */
|
||||
|
||||
/**
|
||||
* @brief ESP_AVRC_CT_COVER_ART_STATE_EVT
|
||||
*/
|
||||
struct avrc_ct_cover_art_state_param {
|
||||
esp_avrc_cover_art_conn_state_t state; /*!< indicate the Cover Art connection status */
|
||||
esp_bt_status_t reason; /*!< the disconnect reason of Cover Art connection */
|
||||
} cover_art_state; /*!< AVRC Cover Art connection state change event */
|
||||
|
||||
/**
|
||||
* @brief ESP_AVRC_CT_COVER_ART_DATA_EVT
|
||||
*/
|
||||
struct avrc_ct_cover_art_data_param {
|
||||
esp_bt_status_t status; /*!< indicate whether the get operation is success, p_data is valid only when status is ESP_BT_STATUS_SUCCESS */
|
||||
bool final; /*!< indicate whether this data event is the final one, true if we have received the entire object */
|
||||
uint16_t data_len; /*!< the data length of this data event, in bytes */
|
||||
uint8_t *p_data; /*!< pointer to data, should copy to other buff before event callback return */
|
||||
} cover_art_data; /*!< AVRC Cover Art data event */
|
||||
} esp_avrc_ct_cb_param_t;
|
||||
|
||||
/// AVRC target callback parameters
|
||||
@ -656,11 +691,11 @@ bool esp_avrc_psth_bit_mask_operation(esp_avrc_bit_mask_op_t op, esp_avrc_psth_b
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Get the requested event notification capabilies on local AVRC target. The capability is returned
|
||||
* @brief Get the requested event notification capabilities on local AVRC target. The capability is returned
|
||||
* in a bit mask representation in evt_set. This function should be called after esp_avrc_tg_init().
|
||||
*
|
||||
* For capability type "ESP_AVRC_RN_CAP_ALLOWED_EVT, the retrieved event set is constant and
|
||||
* it covers all of the notifcation events that can possibly be supported with current
|
||||
* it covers all of the notification events that can possibly be supported with current
|
||||
* implementation.
|
||||
*
|
||||
* For capability type ESP_AVRC_RN_CAP_SUPPORTED_EVT, the event set covers the notification
|
||||
@ -729,6 +764,92 @@ 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. This API
|
||||
* can be used only when AVRC Cover Art feature is enabled.
|
||||
*
|
||||
* @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. This API
|
||||
* can be used only when AVRC Cover Art feature is enabled.
|
||||
*
|
||||
* @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. This API can be used only when AVRC
|
||||
* Cover Art feature is enabled.
|
||||
*
|
||||
* @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. This API can be used only when AVRC Cover Art
|
||||
* feature is enabled.
|
||||
*
|
||||
* @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. This API can be used only when AVRC
|
||||
* Cover Art feature is enabled.
|
||||
*
|
||||
* @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
|
||||
|
@ -37,6 +37,10 @@
|
||||
#if( defined BTA_AR_INCLUDED ) && (BTA_AR_INCLUDED == TRUE)
|
||||
#include "bta/bta_ar_api.h"
|
||||
#endif
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
#include "stack/goep_common.h"
|
||||
#include "stack/goepc_api.h"
|
||||
#endif
|
||||
|
||||
#define LOG_TAG "bt_bta_av"
|
||||
// #include "osi/include/log.h"
|
||||
@ -98,6 +102,10 @@ void bta_av_del_rc(tBTA_AV_RCB *p_rcb)
|
||||
|
||||
p_scb = NULL;
|
||||
if (p_rcb->handle != BTA_AV_RC_HANDLE_NONE) {
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
/* reset cover art state */
|
||||
bta_av_ca_reset(p_rcb);
|
||||
#endif
|
||||
if (p_rcb->shdl) {
|
||||
/* Validate array index*/
|
||||
if ((p_rcb->shdl - 1) < BTA_AV_NUM_STRS) {
|
||||
@ -127,9 +135,6 @@ void bta_av_del_rc(tBTA_AV_RCB *p_rcb)
|
||||
}
|
||||
/* else ACP && connected. do not clear the handle yet */
|
||||
AVRC_Close(rc_handle);
|
||||
if (rc_handle == bta_av_cb.rc_acp_handle) {
|
||||
bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE;
|
||||
}
|
||||
APPL_TRACE_EVENT("end del_rc handle: %d status=0x%x, rc_acp_handle:%d, lidx:%d",
|
||||
p_rcb->handle, p_rcb->status, bta_av_cb.rc_acp_handle, p_rcb->lidx);
|
||||
}
|
||||
@ -302,7 +307,7 @@ UINT8 bta_av_rc_create(tBTA_AV_CB *p_cb, UINT8 role, UINT8 shdl, UINT8 lidx)
|
||||
bda = p_scb->peer_addr;
|
||||
status = BTA_AV_RC_ROLE_INT;
|
||||
} else {
|
||||
if ((p_rcb = bta_av_get_rcb_by_shdl(shdl)) != NULL ) {
|
||||
if (shdl != 0 && ((p_rcb = bta_av_get_rcb_by_shdl(shdl)) != NULL)) {
|
||||
APPL_TRACE_ERROR("bta_av_rc_create ACP handle exist for shdl:%d", shdl);
|
||||
return p_rcb->handle;
|
||||
}
|
||||
@ -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,51 @@ static void bta_av_acp_sig_timer_cback (TIMER_LIST_ENT *p_tle)
|
||||
}
|
||||
}
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_extra_tg_cover_art_l2cap_psm
|
||||
**
|
||||
** Description Extra the AVRC Cover Art L2CAP PSM of peer device from the
|
||||
** SDP record
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
static 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;
|
||||
}
|
||||
|
||||
#endif /* BTA_AV_CA_INCLUDED */
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function bta_av_check_peer_rc_features
|
||||
@ -1539,6 +1589,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 +1627,9 @@ 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 */
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
UINT16 obex_l2cap_psm = 0; /* target obex l2cap psm */
|
||||
#endif
|
||||
UNUSED(p_data);
|
||||
|
||||
APPL_TRACE_DEBUG("bta_av_rc_disc_done disc:x%x", p_cb->disc);
|
||||
@ -1600,7 +1657,11 @@ void bta_av_rc_disc_done(tBTA_AV_DATA *p_data)
|
||||
/* check peer version and whether support CT and TG role */
|
||||
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 BTA_AV_CA_INCLUDED
|
||||
if (peer_features & BTA_AV_FEAT_COVER_ART) {
|
||||
obex_l2cap_psm = bta_av_extra_tg_cover_art_l2cap_psm();
|
||||
}
|
||||
#endif
|
||||
p_cb->disc = 0;
|
||||
utl_freebuf((void **) &p_cb->p_disc_db);
|
||||
|
||||
@ -1618,6 +1679,9 @@ 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;
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
p_cb->rcb[rc_handle].cover_art_l2cap_psm = obex_l2cap_psm;
|
||||
#endif
|
||||
}
|
||||
#if (BT_USE_TRACES == TRUE || BT_TRACE_APPL == TRUE)
|
||||
else {
|
||||
@ -1636,6 +1700,11 @@ 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].peer_ct_features = peer_ct_features;
|
||||
p_cb->rcb[rc_handle].peer_tg_features = peer_tg_features;
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
p_cb->rcb[rc_handle].cover_art_l2cap_psm = obex_l2cap_psm;
|
||||
#endif
|
||||
rc_feat.rc_handle = rc_handle;
|
||||
rc_feat.peer_features = peer_features;
|
||||
rc_feat.peer_ct_features = peer_ct_features;
|
||||
@ -1676,6 +1745,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;
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
/* reset cover art state */
|
||||
bta_av_ca_reset(p_rcb);
|
||||
#endif
|
||||
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) {
|
||||
@ -1711,7 +1784,8 @@ void bta_av_rc_closed(tBTA_AV_DATA *p_data)
|
||||
bta_av_del_rc(p_rcb);
|
||||
|
||||
/* if the AVRCP is no longer listening, create the listening channel */
|
||||
if (bta_av_cb.rc_acp_handle == BTA_AV_RC_HANDLE_NONE && bta_av_cb.features & BTA_AV_FEAT_RCTG) {
|
||||
if (bta_av_cb.rc_acp_handle == p_msg->handle && bta_av_cb.features & BTA_AV_FEAT_RCTG) {
|
||||
bta_av_cb.rc_acp_handle = BTA_AV_RC_HANDLE_NONE;
|
||||
bta_av_rc_create(&bta_av_cb, AVCT_ACP, 0, BTA_AV_NUM_LINKS + 1);
|
||||
}
|
||||
}
|
||||
@ -1748,6 +1822,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 +1860,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;
|
||||
|
||||
|
@ -613,4 +613,77 @@ void BTA_AvMetaCmd(UINT8 rc_handle, UINT8 label, tBTA_AV_CMD cmd_code, BT_HDR *p
|
||||
}
|
||||
}
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** 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_CA_INCLUDED */
|
||||
|
||||
#endif /* BTA_AV_INCLUDED */
|
||||
|
495
components/bt/host/bluedroid/bta/av/bta_av_ca_act.c
Normal file
495
components/bt/host/bluedroid/bta/av/bta_av_ca_act.c
Normal file
@ -0,0 +1,495 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common/bt_target.h"
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
|
||||
#include <string.h>
|
||||
#include "bta/bta_av_api.h"
|
||||
#include "bta_av_int.h"
|
||||
#include "stack/avdt_api.h"
|
||||
#include "bta/utl.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/list.h"
|
||||
#include "stack/goep_common.h"
|
||||
#include "stack/goepc_api.h"
|
||||
#include "stack/obex_api.h"
|
||||
#include "common/bt_trace.h"
|
||||
|
||||
#define COVER_ART_HEADER_ID_IMG_HANDLE 0x30
|
||||
#define COVER_ART_HEADER_ID_IMG_DESCRIPTOR 0x71
|
||||
static const UINT8 cover_art_uuid[16] = {0x71, 0x63, 0xDD, 0x54, 0x4A, 0x7E, 0x11, 0xE2, 0xB4, 0x7C, 0x00, 0x50, 0xC2, 0x49, 0x00, 0x48};
|
||||
static const char *cover_art_img_type_img = "x-bt/img-img";
|
||||
static const char *cover_art_img_type_thm = "x-bt/img-thm";
|
||||
static const char *cover_art_img_type_prop = "x-bt/img-properties";
|
||||
|
||||
#define COVER_ART_IMG_TYPE_IMG_LEN 13
|
||||
#define COVER_ART_IMG_TYPE_THM_LEN 13
|
||||
#define COVER_ART_IMG_TYPE_PROP_LEN 20
|
||||
|
||||
|
||||
static BOOLEAN find_rcb_idx_by_goep_handle(UINT16 handle, UINT16 *out_idx)
|
||||
{
|
||||
for (UINT16 i = 0; i < BTA_AV_NUM_RCB; ++i) {
|
||||
if (bta_av_cb.rcb[i].handle != BTA_AV_RC_HANDLE_NONE && bta_av_cb.rcb[i].cover_art_goep_hdl == handle) {
|
||||
*out_idx = i;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static UINT8 get_rcb_idx(tBTA_AV_RCB *p_rcb)
|
||||
{
|
||||
return (p_rcb - &bta_av_cb.rcb[0]);
|
||||
}
|
||||
|
||||
static void get_peer_bd_addr(tBTA_AV_RCB *p_rcb, BD_ADDR out_addr)
|
||||
{
|
||||
/* check if this rcb is related to a scb */
|
||||
if (p_rcb->shdl && p_rcb->shdl <= BTA_AV_NUM_STRS) {
|
||||
tBTA_AV_SCB *p_scb = bta_av_cb.p_scb[p_rcb->shdl - 1];
|
||||
bdcpy(out_addr, p_scb->peer_addr);
|
||||
}
|
||||
/* else, try get peer addr from lcb */
|
||||
else if (p_rcb->lidx && p_rcb->lidx <= BTA_AV_NUM_LINKS + 1)
|
||||
{
|
||||
bdcpy(out_addr, bta_av_cb.lcb[p_rcb->lidx-1].addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void report_data_event(BT_HDR *pkt, UINT8 *p_data, UINT16 data_len, BOOLEAN final)
|
||||
{
|
||||
tBTA_AV_CA_DATA ca_data;
|
||||
ca_data.status = BT_STATUS_SUCCESS;
|
||||
ca_data.final = final;
|
||||
ca_data.data_len = data_len;
|
||||
ca_data.p_data = p_data;
|
||||
ca_data.p_hdr = pkt;
|
||||
(*bta_av_cb.p_cback)(BTA_AV_CA_DATA_EVT, (tBTA_AV *) &ca_data);
|
||||
}
|
||||
|
||||
static void report_error_data_event(UINT16 status)
|
||||
{
|
||||
tBTA_AV_CA_DATA ca_data;
|
||||
ca_data.status = status;
|
||||
ca_data.final = TRUE;
|
||||
ca_data.data_len = 0;
|
||||
ca_data.p_data = NULL;
|
||||
ca_data.p_hdr = NULL;
|
||||
(*bta_av_cb.p_cback)(BTA_AV_CA_DATA_EVT, (tBTA_AV *) &ca_data);
|
||||
}
|
||||
|
||||
static void build_and_send_connect_req(tBTA_AV_RCB *p_rcb)
|
||||
{
|
||||
tOBEX_PARSE_INFO info = {0};
|
||||
info.opcode = OBEX_OPCODE_CONNECT;
|
||||
info.obex_version_number = 0x15;
|
||||
info.max_packet_length = p_rcb->cover_art_max_rx;
|
||||
/* before OBEX connect response, we dont know cover_art_max_tx, use BT_SMALL_BUFFER_SIZE as tx buff size */
|
||||
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, BT_SMALL_BUFFER_SIZE) == GOEP_SUCCESS) {
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TARGET, (UINT8 *)cover_art_uuid, 16);
|
||||
GOEPC_SendRequest(p_rcb->cover_art_goep_hdl);
|
||||
}
|
||||
}
|
||||
|
||||
static void build_and_send_disconnect_req(tBTA_AV_RCB *p_rcb)
|
||||
{
|
||||
tOBEX_PARSE_INFO info = {0};
|
||||
info.opcode = OBEX_OPCODE_DISCONNECT;
|
||||
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, BT_SMALL_BUFFER_SIZE) == GOEP_SUCCESS) {
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_rcb->cover_art_cid), 4);
|
||||
GOEPC_SendRequest(p_rcb->cover_art_goep_hdl);
|
||||
}
|
||||
}
|
||||
|
||||
static void build_and_send_empty_get_req(tBTA_AV_RCB *p_rcb)
|
||||
{
|
||||
tOBEX_PARSE_INFO info = {0};
|
||||
info.opcode = OBEX_OPCODE_GET_FINAL;
|
||||
/* empty get request, use a small buff size */
|
||||
UINT16 tx_buff_size = BT_SMALL_BUFFER_SIZE < p_rcb->cover_art_max_tx ? BT_SMALL_BUFFER_SIZE : p_rcb->cover_art_max_tx;
|
||||
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, tx_buff_size) == GOEP_SUCCESS) {
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_rcb->cover_art_cid), 4);
|
||||
GOEPC_SendRequest(p_rcb->cover_art_goep_hdl);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_goepc_and_disconnect(tBTA_AV_RCB *p_rcb)
|
||||
{
|
||||
if (p_rcb->cover_art_goep_hdl) {
|
||||
GOEPC_Close(p_rcb->cover_art_goep_hdl);
|
||||
}
|
||||
p_rcb->cover_art_goep_hdl = 0;
|
||||
p_rcb->cover_art_cid = 0;
|
||||
p_rcb->cover_art_max_tx = 0;
|
||||
p_rcb->cover_art_max_rx = 0;
|
||||
|
||||
tBTA_AV_DATA *p_data = (tBTA_AV_DATA *) osi_malloc(sizeof(tBTA_AV_DATA));
|
||||
if (p_data == NULL) {
|
||||
assert(0);
|
||||
}
|
||||
p_data->hdr.event = BTA_AV_CA_GOEP_DISCONNECT_EVT;
|
||||
p_data->hdr.layer_specific = get_rcb_idx(p_rcb);
|
||||
p_data->ca_disconnect.reason = BT_STATUS_FAIL;
|
||||
bta_sys_sendmsg(p_data);
|
||||
}
|
||||
|
||||
static void image_handle_to_utf16(const UINT8 *image_handle, UINT8 *buffer)
|
||||
{
|
||||
UINT8 pos = 0;
|
||||
for (int i = 0 ; i < BTA_AV_CA_IMG_HDL_LEN ; i++){
|
||||
buffer[pos++] = 0;
|
||||
buffer[pos++] = image_handle[i];
|
||||
}
|
||||
buffer[pos++] = 0;
|
||||
buffer[pos++] = 0;
|
||||
}
|
||||
|
||||
void bta_av_ca_goep_event_handler(UINT16 handle, UINT8 event, tGOEPC_MSG *p_msg)
|
||||
{
|
||||
tBTA_AV_DATA *p_data = NULL;
|
||||
UINT16 rcb_idx;
|
||||
if (!find_rcb_idx_by_goep_handle(handle, &rcb_idx)) {
|
||||
/* can not find a rcb, go error */
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (event == GOEPC_RESPONSE_EVT || event == GOEPC_OPENED_EVT || event == GOEPC_CLOSED_EVT) {
|
||||
p_data = (tBTA_AV_DATA *) osi_malloc(sizeof(tBTA_AV_DATA));
|
||||
assert(p_data != NULL);
|
||||
}
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case GOEPC_OPENED_EVT:
|
||||
p_data->hdr.layer_specific = rcb_idx;
|
||||
p_data->hdr.event = BTA_AV_CA_GOEP_CONNECT_EVT;
|
||||
p_data->ca_connect.max_rx = p_msg->opened.our_mtu;
|
||||
break;
|
||||
case GOEPC_CLOSED_EVT:
|
||||
p_data->hdr.layer_specific = rcb_idx;
|
||||
p_data->hdr.event = BTA_AV_CA_GOEP_DISCONNECT_EVT;
|
||||
p_data->ca_disconnect.reason = BT_STATUS_FAIL;
|
||||
break;
|
||||
case GOEPC_RESPONSE_EVT:
|
||||
p_data->hdr.layer_specific = rcb_idx;
|
||||
p_data->ca_response.pkt = p_msg->response.pkt;
|
||||
p_data->ca_response.opcode = p_msg->response.opcode;
|
||||
p_data->ca_response.srm_en = p_msg->response.srm_en;
|
||||
p_data->ca_response.srm_wait = p_msg->response.srm_wait;
|
||||
if (p_msg->response.final) {
|
||||
p_data->hdr.event = BTA_AV_CA_RESPONSE_FINAL_EVT;
|
||||
}
|
||||
else {
|
||||
p_data->hdr.event = BTA_AV_CA_RESPONSE_EVT;
|
||||
}
|
||||
break;
|
||||
case GOEPC_MTU_CHANGED_EVT:
|
||||
case GOEPC_CONGEST_EVT:
|
||||
case GOEPC_UNCONGEST_EVT:
|
||||
/* ignore these event */
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
break;
|
||||
}
|
||||
if (p_data) {
|
||||
bta_sys_sendmsg(p_data);
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
/* can not find rcb, just free resource and disconnect */
|
||||
if (p_data != NULL) {
|
||||
osi_free(p_data);
|
||||
}
|
||||
if (event == GOEPC_RESPONSE_EVT && p_msg->response.pkt != NULL) {
|
||||
osi_free(p_msg->response.pkt);
|
||||
}
|
||||
if (event != GOEPC_CLOSED_EVT) {
|
||||
GOEPC_Close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void bta_av_ca_api_open(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
tOBEX_SVR_INFO svr = {0};
|
||||
svr.tl = OBEX_OVER_L2CAP;
|
||||
svr.l2cap.psm = p_rcb->cover_art_l2cap_psm;
|
||||
svr.l2cap.pref_mtu = p_data->api_ca_open.mtu;
|
||||
/* reuse the security mask store in bta_av_cb, when support multi connection, this may need change */
|
||||
svr.l2cap.sec_mask = bta_av_cb.sec_mask;
|
||||
p_rcb->cover_art_max_rx = p_data->api_ca_open.mtu;
|
||||
get_peer_bd_addr(p_rcb, svr.l2cap.addr);
|
||||
|
||||
if (GOEPC_Open(&svr, bta_av_ca_goep_event_handler, &p_rcb->cover_art_goep_hdl) != GOEP_SUCCESS) {
|
||||
/* open failed */
|
||||
if ((p_data = (tBTA_AV_DATA *) osi_malloc(sizeof(tBTA_AV_DATA))) == NULL) {
|
||||
assert(0);
|
||||
}
|
||||
p_data->hdr.event = BTA_AV_CA_GOEP_DISCONNECT_EVT;
|
||||
p_data->hdr.layer_specific = get_rcb_idx(p_rcb);
|
||||
p_data->ca_disconnect.reason = BT_STATUS_FAIL;
|
||||
bta_sys_sendmsg(p_data);
|
||||
}
|
||||
}
|
||||
|
||||
void bta_av_ca_api_close(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
/* this is a normal disconnect, just build and send OBEX disconnect request */
|
||||
build_and_send_disconnect_req(p_rcb);
|
||||
}
|
||||
|
||||
void bta_av_ca_api_get(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
tOBEX_PARSE_INFO info = {0};
|
||||
UINT8 image_handle_utf16[BTA_AV_CA_IMG_HDL_UTF16_LEN];
|
||||
info.opcode = OBEX_OPCODE_GET_FINAL;
|
||||
/* limit the tx buff size to BT_DEFAULT_BUFFER_SIZE */
|
||||
UINT16 tx_buff_size = BT_DEFAULT_BUFFER_SIZE < p_rcb->cover_art_max_tx ? BT_DEFAULT_BUFFER_SIZE : p_rcb->cover_art_max_tx;
|
||||
if (GOEPC_PrepareRequest(p_rcb->cover_art_goep_hdl, &info, tx_buff_size) != GOEP_SUCCESS) {
|
||||
/* something error */
|
||||
goto error;
|
||||
}
|
||||
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_rcb->cover_art_cid), 4);
|
||||
switch (p_data->api_ca_get.type)
|
||||
{
|
||||
case BTA_AV_CA_GET_IMAGE_PROPERTIES:
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TYPE, (const UINT8 *)cover_art_img_type_prop, COVER_ART_IMG_TYPE_PROP_LEN);
|
||||
break;
|
||||
case BTA_AV_CA_GET_IMAGE:
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TYPE, (const UINT8 *)cover_art_img_type_img, COVER_ART_IMG_TYPE_IMG_LEN);
|
||||
break;
|
||||
case BTA_AV_CA_GET_LINKED_THUMBNAIL:
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, OBEX_HEADER_ID_TYPE, (const UINT8 *)cover_art_img_type_thm, COVER_ART_IMG_TYPE_THM_LEN);
|
||||
break;
|
||||
default:
|
||||
/* should not go here */
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
image_handle_to_utf16(p_data->api_ca_get.image_handle, image_handle_utf16);
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, COVER_ART_HEADER_ID_IMG_HANDLE, (UINT8 *)image_handle_utf16, BTA_AV_CA_IMG_HDL_UTF16_LEN);
|
||||
if (p_data->api_ca_get.type == BTA_AV_CA_GET_IMAGE) {
|
||||
GOEPC_RequestAddHeader(p_rcb->cover_art_goep_hdl, COVER_ART_HEADER_ID_IMG_DESCRIPTOR, (UINT8 *)p_data->api_ca_get.image_descriptor, p_data->api_ca_get.image_descriptor_len);
|
||||
}
|
||||
/* always request to enable srm */
|
||||
GOEPC_RequestSetSRM(p_rcb->cover_art_goep_hdl, TRUE, FALSE);
|
||||
|
||||
if (GOEPC_SendRequest(p_rcb->cover_art_goep_hdl) != GOEP_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
return;
|
||||
error:
|
||||
close_goepc_and_disconnect(p_rcb);
|
||||
}
|
||||
|
||||
void bta_av_ca_response(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
tOBEX_PARSE_INFO info;
|
||||
OBEX_ParseResponse(p_data->ca_response.pkt, p_data->ca_response.opcode, &info);
|
||||
/* we always use a final get */
|
||||
if (p_data->ca_response.opcode == OBEX_OPCODE_GET_FINAL
|
||||
&& (info.response_code == OBEX_RESPONSE_CODE_CONTINUE || info.response_code == (OBEX_RESPONSE_CODE_CONTINUE | OBEX_FINAL_BIT_MASK)))
|
||||
{
|
||||
UINT8 *header = NULL;
|
||||
UINT8 *body_data = NULL;
|
||||
UINT16 body_data_len = 0;
|
||||
while((header = OBEX_GetNextHeader(p_data->ca_response.pkt, &info)) != NULL) {
|
||||
switch (*header)
|
||||
{
|
||||
case OBEX_HEADER_ID_BODY:
|
||||
/* actually,END_OF_BODY should not in this continue response */
|
||||
case OBEX_HEADER_ID_END_OF_BODY:
|
||||
if (body_data == NULL) {
|
||||
/* first body header */
|
||||
body_data = header + 3; /* skip opcode, length */
|
||||
body_data_len = OBEX_GetHeaderLength(header) - 3;
|
||||
}
|
||||
else {
|
||||
/* another body header found */
|
||||
report_data_event(NULL, body_data, body_data_len, FALSE);
|
||||
body_data = header + 3; /* skip opcode, length */
|
||||
body_data_len = OBEX_GetHeaderLength(header) - 3;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (body_data != NULL) {
|
||||
/* the only one or the last body data */
|
||||
report_data_event(p_data->ca_response.pkt, body_data, body_data_len, FALSE);
|
||||
}
|
||||
else {
|
||||
/* not any body data */
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
}
|
||||
|
||||
/* if SRM not enable, we need to send a empty get request */
|
||||
if (!p_data->ca_response.srm_en || p_data->ca_response.srm_wait) {
|
||||
build_and_send_empty_get_req(p_rcb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
goto error;
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
close_goepc_and_disconnect(p_rcb);
|
||||
}
|
||||
|
||||
void bta_av_ca_response_final(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
tOBEX_PARSE_INFO info;
|
||||
OBEX_ParseResponse(p_data->ca_response.pkt, p_data->ca_response.opcode, &info);
|
||||
UINT8 *header = NULL;
|
||||
if (p_data->ca_response.opcode == OBEX_OPCODE_CONNECT) {
|
||||
/* we expect a success response code with final bit set */
|
||||
if (info.response_code == (OBEX_RESPONSE_CODE_OK | OBEX_FINAL_BIT_MASK)) {
|
||||
if (info.max_packet_length < 255) {
|
||||
p_rcb->cover_art_max_tx = 255;
|
||||
}
|
||||
else {
|
||||
p_rcb->cover_art_max_tx = info.max_packet_length;
|
||||
}
|
||||
BOOLEAN cid_found = false;
|
||||
while((header = OBEX_GetNextHeader(p_data->ca_response.pkt, &info)) != NULL) {
|
||||
if (*header == OBEX_HEADER_ID_CONNECTION_ID) {
|
||||
cid_found = true;
|
||||
memcpy((UINT8 *)(&p_rcb->cover_art_cid), header + 1, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!cid_found) {
|
||||
goto error;
|
||||
}
|
||||
tBTA_AV_CA_STATUS ca_status;
|
||||
ca_status.connected = TRUE;
|
||||
ca_status.reason = BT_STATUS_SUCCESS;
|
||||
(*bta_av_cb.p_cback)(BTA_AV_CA_STATUS_EVT, (tBTA_AV *) &ca_status);
|
||||
/* done, free response packet */
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
}
|
||||
else {
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else if (p_data->ca_response.opcode == OBEX_OPCODE_GET_FINAL) {
|
||||
UINT8 *body_data = NULL;
|
||||
UINT16 body_data_len = 0;
|
||||
/* check response code is success */
|
||||
if (info.response_code == (OBEX_RESPONSE_CODE_OK | OBEX_FINAL_BIT_MASK)) {
|
||||
while((header = OBEX_GetNextHeader(p_data->ca_response.pkt, &info)) != NULL) {
|
||||
switch (*header)
|
||||
{
|
||||
/* actually, BODY should not in this final response */
|
||||
case OBEX_HEADER_ID_BODY:
|
||||
case OBEX_HEADER_ID_END_OF_BODY:
|
||||
if (body_data == NULL) {
|
||||
/* first body header */
|
||||
body_data = header + 3; /* skip opcode, length */
|
||||
body_data_len = OBEX_GetHeaderLength(header) - 3;
|
||||
}
|
||||
else {
|
||||
/* another body header found */
|
||||
report_data_event(NULL, body_data, body_data_len, FALSE);
|
||||
body_data = header + 3; /* skip opcode, length */
|
||||
body_data_len = OBEX_GetHeaderLength(header) - 3;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (body_data != NULL) {
|
||||
/* the only one or the last body data, packet will be free by upper layer */
|
||||
report_data_event(p_data->ca_response.pkt, body_data, body_data_len, TRUE);
|
||||
}
|
||||
else {
|
||||
/* not any body data */
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
}
|
||||
}
|
||||
else {
|
||||
report_error_data_event(BT_STATUS_FAIL);
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
}
|
||||
}
|
||||
else if (p_data->ca_response.opcode == OBEX_OPCODE_DISCONNECT) {
|
||||
/* received disconnect response, close l2cap channel and reset cover art value */
|
||||
bta_av_ca_force_disconnect(p_rcb, p_data);
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
}
|
||||
else {
|
||||
osi_free(p_data->ca_response.pkt);
|
||||
goto error;
|
||||
}
|
||||
return;
|
||||
|
||||
error:
|
||||
close_goepc_and_disconnect(p_rcb);
|
||||
}
|
||||
|
||||
void bta_av_ca_goep_connect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
/* goep connection open, use a smaller value as max_rx */
|
||||
if (p_rcb->cover_art_max_rx > p_data->ca_connect.max_rx) {
|
||||
p_rcb->cover_art_max_rx = p_data->ca_connect.max_rx;
|
||||
}
|
||||
build_and_send_connect_req(p_rcb);
|
||||
}
|
||||
|
||||
void bta_av_ca_goep_disconnect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
p_rcb->cover_art_goep_hdl = 0;
|
||||
p_rcb->cover_art_max_rx = 0;
|
||||
p_rcb->cover_art_max_tx = 0;
|
||||
p_rcb->cover_art_cid = 0;
|
||||
|
||||
tBTA_AV_CA_STATUS ca_status;
|
||||
ca_status.connected = FALSE;
|
||||
ca_status.reason = p_data->ca_disconnect.reason;
|
||||
(*bta_av_cb.p_cback)(BTA_AV_CA_STATUS_EVT, (tBTA_AV *) &ca_status);
|
||||
}
|
||||
|
||||
void bta_av_ca_force_disconnect(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
if (p_rcb->cover_art_goep_hdl) {
|
||||
GOEPC_Close(p_rcb->cover_art_goep_hdl);
|
||||
}
|
||||
/* dont reset p_rcb->cover_art_l2cap_psm */
|
||||
p_rcb->cover_art_goep_hdl = 0;
|
||||
p_rcb->cover_art_cid = 0;
|
||||
p_rcb->cover_art_max_tx = 0;
|
||||
p_rcb->cover_art_max_rx = 0;
|
||||
tBTA_AV_CA_STATUS ca_status;
|
||||
ca_status.connected = FALSE;
|
||||
/* force disconnect by upper, set reason to success */
|
||||
ca_status.reason = BT_STATUS_SUCCESS;
|
||||
(*bta_av_cb.p_cback)(BTA_AV_CA_STATUS_EVT, (tBTA_AV *) &ca_status);
|
||||
}
|
||||
|
||||
void bta_av_ca_reset(tBTA_AV_RCB *p_rcb)
|
||||
{
|
||||
if (p_rcb->cover_art_goep_hdl) {
|
||||
GOEPC_Close(p_rcb->cover_art_goep_hdl);
|
||||
}
|
||||
p_rcb->cover_art_l2cap_psm = 0;
|
||||
p_rcb->cover_art_goep_hdl = 0;
|
||||
p_rcb->cover_art_state = 0;
|
||||
p_rcb->cover_art_cid = 0;
|
||||
p_rcb->cover_art_max_tx = 0;
|
||||
p_rcb->cover_art_max_rx = 0;
|
||||
}
|
||||
|
||||
#endif /* BTA_AV_CA_INCLUDED */
|
170
components/bt/host/bluedroid/bta/av/bta_av_ca_sm.c
Normal file
170
components/bt/host/bluedroid/bta/av/bta_av_ca_sm.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common/bt_target.h"
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
|
||||
#include <string.h>
|
||||
#include "bta/bta_av_api.h"
|
||||
#include "bta_av_int.h"
|
||||
#include "stack/avdt_api.h"
|
||||
#include "bta/utl.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/list.h"
|
||||
|
||||
#include "common/bt_trace.h"
|
||||
|
||||
/* state machine states */
|
||||
enum {
|
||||
BTA_AV_CA_INIT_ST,
|
||||
BTA_AV_CA_OPENING_ST,
|
||||
BTA_AV_CA_CONNECTING_ST,
|
||||
BTA_AV_CA_CONNECTED_ST,
|
||||
BTA_AV_CA_GETTING_ST,
|
||||
BTA_AV_CA_CLOSING_ST
|
||||
};
|
||||
|
||||
/* state machine action enumeration list */
|
||||
enum {
|
||||
BTA_AV_API_CA_OPEN,
|
||||
BTA_AV_API_CA_CLOSE,
|
||||
BTA_AV_API_CA_GET,
|
||||
BTA_AV_CA_RESPONSE,
|
||||
BTA_AV_CA_RESPONSE_FINAL,
|
||||
BTA_AV_CA_GOEP_CONNECT,
|
||||
BTA_AV_CA_GOEP_DISCONNECT,
|
||||
BTA_AV_CA_FORCE_DISCONNECT,
|
||||
BTA_AV_CA_NUM_ACTIONS
|
||||
};
|
||||
|
||||
#define BTA_AV_CA_IGNORE BTA_AV_CA_NUM_ACTIONS
|
||||
|
||||
/* type for action functions */
|
||||
typedef void (*tBTA_AV_CA_ACTION)(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data);
|
||||
|
||||
/* action functions */
|
||||
const tBTA_AV_CA_ACTION bta_av_ca_action[] = {
|
||||
bta_av_ca_api_open,
|
||||
bta_av_ca_api_close,
|
||||
bta_av_ca_api_get,
|
||||
bta_av_ca_response,
|
||||
bta_av_ca_response_final,
|
||||
bta_av_ca_goep_connect,
|
||||
bta_av_ca_goep_disconnect,
|
||||
bta_av_ca_force_disconnect,
|
||||
NULL
|
||||
};
|
||||
|
||||
/* state table information */
|
||||
#define BTA_AV_CA_ACTION_COL 0 /* position of actions */
|
||||
#define BTA_AV_CA_NEXT_STATE 1 /* position of next state */
|
||||
#define BTA_AV_CA_NUM_COLS 2 /* number of columns in state tables */
|
||||
|
||||
/* state table for init state */
|
||||
static const UINT8 bta_av_ca_st_init[][BTA_AV_CA_NUM_COLS] = {
|
||||
/* Event Action 1 Next state */
|
||||
/* API_CA_OPEN_EVT */ {BTA_AV_API_CA_OPEN, BTA_AV_CA_OPENING_ST },
|
||||
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
|
||||
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
|
||||
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
|
||||
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
|
||||
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
|
||||
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_INIT_ST },
|
||||
};
|
||||
|
||||
/* state table for opening state */
|
||||
static const UINT8 bta_av_ca_st_opening[][BTA_AV_CA_NUM_COLS] = {
|
||||
/* Event Action 1 Next state */
|
||||
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
|
||||
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
|
||||
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
|
||||
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_OPENING_ST },
|
||||
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_GOEP_CONNECT, BTA_AV_CA_CONNECTING_ST },
|
||||
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
};
|
||||
|
||||
/* state table for connecting state */
|
||||
static const UINT8 bta_av_ca_st_connecting[][BTA_AV_CA_NUM_COLS] = {
|
||||
/* Event Action 1 Next state */
|
||||
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
|
||||
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
|
||||
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
|
||||
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_RESPONSE_FINAL, BTA_AV_CA_CONNECTED_ST },
|
||||
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTING_ST },
|
||||
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
};
|
||||
|
||||
/* state table for connected state */
|
||||
static const UINT8 bta_av_ca_st_connected[][BTA_AV_CA_NUM_COLS] = {
|
||||
/* Event Action 1 Next state */
|
||||
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
|
||||
/* API_CA_CLOSE_EVT */ {BTA_AV_API_CA_CLOSE, BTA_AV_CA_CLOSING_ST },
|
||||
/* API_CA_GET_EVT */ {BTA_AV_API_CA_GET, BTA_AV_CA_GETTING_ST },
|
||||
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
|
||||
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
|
||||
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CONNECTED_ST },
|
||||
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
};
|
||||
|
||||
/* state table for getting state */
|
||||
static const UINT8 bta_av_ca_st_getting[][BTA_AV_CA_NUM_COLS] = {
|
||||
/* Event Action 1 Next state */
|
||||
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_GETTING_ST },
|
||||
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_GETTING_ST },
|
||||
/* CA_RESPONSE_EVT */ {BTA_AV_CA_RESPONSE, BTA_AV_CA_GETTING_ST },
|
||||
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_RESPONSE_FINAL, BTA_AV_CA_CONNECTED_ST },
|
||||
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_GETTING_ST },
|
||||
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
};
|
||||
|
||||
/* state table for closing state */
|
||||
static const UINT8 bta_av_ca_st_closing[][BTA_AV_CA_NUM_COLS] = {
|
||||
/* Event Action 1 Next state */
|
||||
/* API_CA_OPEN_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
|
||||
/* API_CA_CLOSE_EVT */ {BTA_AV_CA_FORCE_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
/* API_CA_GET_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
|
||||
/* CA_RESPONSE_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
|
||||
/* CA_RESPONSE_FINAL_EVT */ {BTA_AV_CA_RESPONSE_FINAL, BTA_AV_CA_INIT_ST },
|
||||
/* CA_GOEP_CONNECT_EVT */ {BTA_AV_CA_IGNORE, BTA_AV_CA_CLOSING_ST },
|
||||
/* CA_GOEP_DISCONNECT_EVT */ {BTA_AV_CA_GOEP_DISCONNECT, BTA_AV_CA_INIT_ST },
|
||||
};
|
||||
|
||||
/* type for state table */
|
||||
typedef const UINT8 (*tBTA_AV_CA_ST_TBL)[BTA_AV_CA_NUM_COLS];
|
||||
|
||||
/* state table */
|
||||
static const tBTA_AV_CA_ST_TBL bta_av_ca_st_tbl[] = {
|
||||
bta_av_ca_st_init,
|
||||
bta_av_ca_st_opening,
|
||||
bta_av_ca_st_connecting,
|
||||
bta_av_ca_st_connected,
|
||||
bta_av_ca_st_getting,
|
||||
bta_av_ca_st_closing
|
||||
};
|
||||
|
||||
void bta_av_ca_sm_execute(tBTA_AV_RCB *p_rcb, UINT16 event, tBTA_AV_DATA *p_data)
|
||||
{
|
||||
tBTA_AV_CA_ST_TBL state_table;
|
||||
UINT8 action;
|
||||
/* look up the state table for the current state */
|
||||
state_table = bta_av_ca_st_tbl[p_rcb->cover_art_state];
|
||||
|
||||
event -= BTA_AV_CA_FIRST_SM_EVT;
|
||||
|
||||
/* set next state */
|
||||
p_rcb->cover_art_state = state_table[event][BTA_AV_CA_NEXT_STATE];
|
||||
|
||||
/* execute action functions */
|
||||
if ((action = state_table[event][BTA_AV_CA_ACTION_COL]) != BTA_AV_CA_IGNORE) {
|
||||
(*bta_av_ca_action[action])(p_rcb, p_data);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* BTA_AV_CA_INCLUDED */
|
@ -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,13 @@ 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);
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
} 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);
|
||||
}
|
||||
#endif
|
||||
} 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);
|
||||
|
@ -85,7 +85,16 @@ enum {
|
||||
BTA_AV_ROLE_CHANGE_EVT,
|
||||
BTA_AV_AVDT_DELAY_RPT_EVT,
|
||||
BTA_AV_ACP_CONNECT_EVT,
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
/* 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,
|
||||
#endif
|
||||
/* these events are handled outside of the state machine */
|
||||
BTA_AV_API_ENABLE_EVT,
|
||||
BTA_AV_API_REGISTER_EVT,
|
||||
@ -115,6 +124,12 @@ enum {
|
||||
#define BTA_AV_FIRST_SM_EVT BTA_AV_API_DISABLE_EVT
|
||||
#define BTA_AV_LAST_SM_EVT BTA_AV_AVRC_NONE_EVT
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
/* 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
|
||||
#endif
|
||||
|
||||
/* events for AV stream control block state machine */
|
||||
#define BTA_AV_FIRST_SSM_EVT BTA_AV_API_OPEN_EVT
|
||||
|
||||
@ -348,6 +363,52 @@ typedef struct {
|
||||
BT_HDR hdr;
|
||||
} tBTA_AV_API_GET_DELAY_VALUE;
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
|
||||
/* 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;
|
||||
|
||||
#endif /* BTA_AV_CA_INCLUDED */
|
||||
|
||||
/* initiator/acceptor role for adaption */
|
||||
#define BTA_AV_ROLE_AD_INT 0x00 /* initiator */
|
||||
#define BTA_AV_ROLE_AD_ACP 0x01 /* acceptor */
|
||||
@ -382,6 +443,14 @@ 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;
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
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;
|
||||
#endif
|
||||
} tBTA_AV_DATA;
|
||||
|
||||
typedef void (tBTA_AV_VDP_DATA_ACT)(void *p_scb);
|
||||
@ -405,8 +474,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 +531,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 +540,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 +550,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 +567,14 @@ typedef struct {
|
||||
tBTA_AV_FEAT peer_features; /* peer features mask */
|
||||
UINT16 peer_ct_features;
|
||||
UINT16 peer_tg_features;
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
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 */
|
||||
#endif
|
||||
} tBTA_AV_RCB;
|
||||
#define BTA_AV_NUM_RCB (BTA_AV_NUM_STRS + 2)
|
||||
|
||||
@ -705,6 +789,19 @@ 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);
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
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
|
||||
|
||||
#endif ///BTA_AV_INCLUDED == TRUE
|
||||
|
||||
#endif /* BTA_AV_INT_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,13 @@ 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. */
|
||||
|
||||
/* still keep Cover Art event here if Cover Art feature not enabled */
|
||||
#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 */
|
||||
@ -482,6 +495,25 @@ typedef struct {
|
||||
} tBTA_AV_SNK_PSC_CFG;
|
||||
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
|
||||
/* 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;
|
||||
|
||||
#endif
|
||||
|
||||
/* union of data associated with AV callback */
|
||||
typedef union {
|
||||
tBTA_AV_CHNL chnl;
|
||||
@ -506,6 +538,10 @@ typedef union {
|
||||
tBTA_AV_RC_FEAT rc_feat;
|
||||
tBTA_AV_DELAY delay;
|
||||
tBTA_AV_SNK_PSC_CFG psc;
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
tBTA_AV_CA_STATUS ca_status;
|
||||
tBTA_AV_CA_DATA ca_data;
|
||||
#endif
|
||||
} tBTA_AV;
|
||||
|
||||
/* union of data associated with AV Media callback */
|
||||
@ -865,6 +901,47 @@ 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);
|
||||
|
||||
#if BTA_AV_CA_INCLUDED
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** 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);
|
||||
|
||||
#endif /* BTA_AV_CA_INCLUDED */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,52 @@ 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) {
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
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;
|
||||
#endif
|
||||
default:
|
||||
BTC_TRACE_DEBUG("%s Unhandled deep copy %d\n", __FUNCTION__, msg->act);
|
||||
UNUSED(dst);
|
||||
UNUSED(src);
|
||||
UNUSED(len);
|
||||
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) {
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
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;
|
||||
#endif
|
||||
default:
|
||||
BTC_TRACE_DEBUG("%s Unhandled deep free %d\n", __FUNCTION__, msg->act);
|
||||
UNUSED(arg);
|
||||
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;
|
||||
@ -499,14 +550,28 @@ static void handle_rc_disconnect (tBTA_AV_RC_CLOSE *p_rc_close)
|
||||
// clean up the state
|
||||
btc_rc_cb.rc_handle = 0;
|
||||
btc_rc_cb.rc_connected = FALSE;
|
||||
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
bool cover_art_connected = btc_rc_cb.rc_cover_art_connected;
|
||||
btc_rc_cb.rc_cover_art_connected = FALSE;
|
||||
#endif
|
||||
btc_rc_cb.rc_features = 0;
|
||||
btc_rc_cb.rc_ct_features = 0;
|
||||
btc_rc_cb.rc_tg_features = 0;
|
||||
memset(btc_rc_cb.rc_addr, 0, sizeof(BD_ADDR));
|
||||
memset(btc_rc_cb.rc_ntf, 0, sizeof(btc_rc_cb.rc_ntf));
|
||||
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
/* 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);
|
||||
}
|
||||
#endif
|
||||
|
||||
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 +816,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
|
||||
@ -947,7 +1012,7 @@ void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data)
|
||||
memset(¶m, 0, sizeof(esp_avrc_ct_cb_param_t));
|
||||
param.conn_stat.connected = true;
|
||||
memcpy(param.conn_stat.remote_bda, btc_rc_cb.rc_addr, sizeof(esp_bd_addr_t));
|
||||
btc_avrc_tg_cb_to_app(ESP_AVRC_CT_CONNECTION_STATE_EVT, ¶m);
|
||||
btc_avrc_tg_cb_to_app(ESP_AVRC_TG_CONNECTION_STATE_EVT, ¶m);
|
||||
}
|
||||
} while (0);
|
||||
btc_rc_cb.rc_features = p_data->rc_feat.peer_features;
|
||||
@ -967,6 +1032,36 @@ void btc_rc_handler(tBTA_AV_EVT event, tBTA_AV *p_data)
|
||||
handle_rc_passthrough_cmd(&p_data->remote_cmd);
|
||||
}
|
||||
break;
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
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;
|
||||
#endif /* BTC_AV_CA_INCLUDED */
|
||||
default:
|
||||
BTC_TRACE_DEBUG("Unhandled RC event : 0x%x", event);
|
||||
}
|
||||
@ -1041,7 +1136,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 +1350,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 +1362,64 @@ static bt_status_t btc_avrc_ct_send_passthrough_cmd(uint8_t tl, uint8_t key_code
|
||||
return status;
|
||||
}
|
||||
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif /* BTC_AV_CA_INCLUDED */
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@ -1298,7 +1451,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 +1473,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 +1571,28 @@ 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;
|
||||
}
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
BTC_TRACE_WARNING("%s : unhandled event: %d\n", __FUNCTION__, msg->act);
|
||||
}
|
||||
|
@ -39,7 +39,14 @@ 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,
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
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,
|
||||
#endif
|
||||
} btc_avrc_act_t;
|
||||
|
||||
typedef struct {
|
||||
@ -77,6 +84,28 @@ typedef struct {
|
||||
uint8_t volume;
|
||||
} set_abs_vol_cmd_t;
|
||||
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
|
||||
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;
|
||||
|
||||
#endif /* BTC_AV_CA_INCLUDED */
|
||||
|
||||
/* btc_avrc_args_t */
|
||||
typedef union {
|
||||
pt_cmd_t pt_cmd;
|
||||
@ -85,6 +114,12 @@ typedef union {
|
||||
ps_cmd_t ps_cmd;
|
||||
get_caps_cmd_t get_caps_cmd;
|
||||
set_abs_vol_cmd_t set_abs_vol_cmd;
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
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;
|
||||
#endif
|
||||
} btc_avrc_args_t;
|
||||
|
||||
/* btc_avrc_tg_act_t */
|
||||
@ -124,6 +159,9 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
BOOLEAN rc_connected;
|
||||
#if BTC_AV_CA_INCLUDED
|
||||
BOOLEAN rc_cover_art_connected;
|
||||
#endif
|
||||
UINT8 rc_handle;
|
||||
tBTA_AV_FEAT rc_features;
|
||||
UINT16 rc_ct_features;
|
||||
@ -162,6 +200,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 +219,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
|
||||
|
||||
|
@ -39,6 +39,19 @@
|
||||
#define UC_BT_A2DP_ENABLED FALSE
|
||||
#endif
|
||||
|
||||
//AVRCP
|
||||
#ifdef CONFIG_BT_AVRCP_ENABLED
|
||||
#define UC_BT_AVRCP_ENABLED TRUE
|
||||
#ifdef CONFIG_BT_AVRCP_CT_COVER_ART_ENABLED
|
||||
#define UC_BT_AVRCP_CT_COVER_ART_ENABLED CONFIG_BT_AVRCP_CT_COVER_ART_ENABLED
|
||||
#else
|
||||
#define UC_BT_AVRCP_CT_COVER_ART_ENABLED FALSE
|
||||
#endif
|
||||
#else
|
||||
#define UC_BT_AVRCP_ENABLED FALSE
|
||||
#define UC_BT_AVRCP_CT_COVER_ART_ENABLED FALSE
|
||||
#endif
|
||||
|
||||
//SPP
|
||||
#ifdef CONFIG_BT_SPP_ENABLED
|
||||
#define UC_BT_SPP_ENABLED CONFIG_BT_SPP_ENABLED
|
||||
@ -111,6 +124,13 @@
|
||||
#define UC_BT_ENC_KEY_SIZE_CTRL_MODE 0
|
||||
#endif
|
||||
|
||||
//GOEPC (BT)
|
||||
#ifdef CONFIG_BT_GOEPC_ENABLED
|
||||
#define UC_BT_GOEPC_ENABLED CONFIG_BT_GOEPC_ENABLED
|
||||
#else
|
||||
#define UC_BT_GOEPC_ENABLED FALSE
|
||||
#endif
|
||||
|
||||
//BLE
|
||||
#ifdef CONFIG_BT_BLE_ENABLED
|
||||
#define UC_BT_BLE_ENABLED CONFIG_BT_BLE_ENABLED
|
||||
|
@ -91,6 +91,11 @@
|
||||
#define SBC_DEC_INCLUDED TRUE
|
||||
#define BTC_AV_SRC_INCLUDED TRUE
|
||||
#define SBC_ENC_INCLUDED TRUE
|
||||
#if UC_BT_AVRCP_CT_COVER_ART_ENABLED
|
||||
#define BTA_AV_CA_INCLUDED TRUE
|
||||
#define BTC_AV_CA_INCLUDED TRUE
|
||||
#define AVRC_CA_INCLUDED TRUE
|
||||
#endif /* UC_BT_AVRCP_CT_COVER_ART_ENABLED */
|
||||
#endif /* UC_BT_A2DP_ENABLED */
|
||||
|
||||
#if (UC_BT_SPP_ENABLED == TRUE)
|
||||
@ -171,6 +176,13 @@
|
||||
#define BTC_HD_INCLUDED TRUE
|
||||
#endif /* UC_BT_HID_DEVICE_ENABLED */
|
||||
|
||||
#if UC_BT_GOEPC_ENABLED
|
||||
#ifndef OBEX_INCLUDED
|
||||
#define OBEX_INCLUDED TRUE
|
||||
#endif
|
||||
#define GOEPC_INCLUDED TRUE
|
||||
#endif /* UC_BT_GOEPC_ENABLED */
|
||||
|
||||
#endif /* UC_BT_CLASSIC_ENABLED */
|
||||
|
||||
/* This is set to enable use of GAP L2CAP connections. */
|
||||
@ -376,6 +388,10 @@
|
||||
#define BTC_AV_INCLUDED FALSE
|
||||
#endif
|
||||
|
||||
#ifndef BTC_AV_CA_INCLUDED
|
||||
#define BTC_AV_CA_INCLUDED FALSE
|
||||
#endif
|
||||
|
||||
#ifndef BTC_AV_SINK_INCLUDED
|
||||
#define BTC_AV_SINK_INCLUDED FALSE
|
||||
#endif
|
||||
@ -449,6 +465,10 @@
|
||||
#define BTA_AV_INCLUDED FALSE
|
||||
#endif
|
||||
|
||||
#ifndef BTA_AV_CA_INCLUDED
|
||||
#define BTA_AV_CA_INCLUDED FALSE
|
||||
#endif
|
||||
|
||||
#ifndef BTA_AV_SINK_INCLUDED
|
||||
#define BTA_AV_SINK_INCLUDED FALSE
|
||||
#endif
|
||||
@ -1809,6 +1829,26 @@
|
||||
#define OBX_FCR_TX_POOL_ID 3
|
||||
#endif
|
||||
|
||||
/* Maximum OBEX connection allowed */
|
||||
#ifndef OBEX_MAX_CONNECTION
|
||||
#define OBEX_MAX_CONNECTION 3
|
||||
#endif
|
||||
|
||||
/* Maximum OBEX server allowed */
|
||||
#ifndef OBEX_MAX_SERVER
|
||||
#define OBEX_MAX_SERVER 2
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
**
|
||||
** GOEP
|
||||
**
|
||||
******************************************************************************/
|
||||
|
||||
/* Maximum GOEP client connection allowed */
|
||||
#ifndef GOEPC_MAX_CONNECTION
|
||||
#define GOEPC_MAX_CONNECTION 3
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
**
|
||||
@ -2141,6 +2181,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
|
||||
*/
|
||||
@ -2179,6 +2233,10 @@
|
||||
#define AVRC_INCLUDED FALSE
|
||||
#endif
|
||||
|
||||
#ifndef AVRC_CA_INCLUDED
|
||||
#define AVRC_CA_INCLUDED FALSE
|
||||
#endif
|
||||
|
||||
#ifndef AVRC_METADATA_INCLUDED
|
||||
#if AVRC_INCLUDED == TRUE
|
||||
#define AVRC_METADATA_INCLUDED TRUE
|
||||
|
@ -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...)
|
||||
|
@ -27,8 +27,8 @@
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Stack Configuation Related Init Definaton
|
||||
* TODO: Now Just Unmask these defination until stack layer is OK
|
||||
/* Stack Configuration Related Init Definaton
|
||||
* TODO: Now Just Unmask these definition until stack layer is OK
|
||||
*/
|
||||
|
||||
#ifndef BTA_INCLUDED
|
||||
@ -91,6 +91,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if (defined(OBEX_INCLUDED) && OBEX_INCLUDED == TRUE)
|
||||
#include "stack/obex_api.h"
|
||||
#endif
|
||||
|
||||
#if (defined(GOEPC_INCLUDED) && GOEPC_INCLUDED == TRUE)
|
||||
#include "stack/goep_common.h"
|
||||
#include "stack/goepc_api.h"
|
||||
#endif
|
||||
|
||||
//BTA Modules
|
||||
#if BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE
|
||||
#include "bta/bta_api.h"
|
||||
@ -267,6 +276,14 @@ void BTE_DeinitStack(void)
|
||||
}
|
||||
#endif // BTA_INCLUDED == TRUE
|
||||
|
||||
#if (defined(GOEPC_INCLUDED) && GOEPC_INCLUDED == TRUE)
|
||||
GOEPC_Deinit();
|
||||
#endif
|
||||
|
||||
#if (defined(OBEX_INCLUDED) && OBEX_INCLUDED == TRUE)
|
||||
OBEX_Deinit();
|
||||
#endif
|
||||
|
||||
#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE)
|
||||
HID_DevDeinit();
|
||||
#endif
|
||||
@ -388,6 +405,18 @@ bt_status_t BTE_InitStack(void)
|
||||
MCA_Init();
|
||||
#endif
|
||||
|
||||
#if (defined(OBEX_INCLUDED) && OBEX_INCLUDED == TRUE)
|
||||
if (OBEX_Init() != OBEX_SUCCESS) {
|
||||
goto error_exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(GOEPC_INCLUDED) && GOEPC_INCLUDED == TRUE)
|
||||
if (GOEPC_Init() != GOEP_SUCCESS) {
|
||||
goto error_exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
//BTA Modules
|
||||
#if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE)
|
||||
if ((bta_sys_cb_ptr = (tBTA_SYS_CB *)osi_malloc(sizeof(tBTA_SYS_CB))) == NULL) {
|
||||
|
@ -430,9 +430,9 @@ BOOLEAN avct_lcb_last_ccb(tAVCT_LCB *p_lcb, tAVCT_CCB *p_ccb_last)
|
||||
tAVCT_CCB *p_ccb = &avct_cb.ccb[0];
|
||||
int i;
|
||||
|
||||
AVCT_TRACE_WARNING("avct_lcb_last_ccb");
|
||||
AVCT_TRACE_DEBUG("avct_lcb_last_ccb");
|
||||
for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) {
|
||||
AVCT_TRACE_WARNING("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p",
|
||||
AVCT_TRACE_DEBUG("%x: aloc:%d, lcb:%p/%p, ccb:%p/%p",
|
||||
i, p_ccb->allocated, p_ccb->p_lcb, p_lcb, p_ccb, p_ccb_last);
|
||||
if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb) && (p_ccb != p_ccb_last)) {
|
||||
return FALSE;
|
||||
|
@ -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 AVRC_CA_INCLUDED
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
/* 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.
|
||||
**
|
||||
|
376
components/bt/host/bluedroid/stack/goep/goepc_api.c
Normal file
376
components/bt/host/bluedroid/stack/goep/goepc_api.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "osi/osi.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/obex_api.h"
|
||||
#include "stack/goep_common.h"
|
||||
#include "stack/goepc_api.h"
|
||||
#include "goep_int.h"
|
||||
|
||||
#if (GOEPC_INCLUDED == TRUE)
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_Init
|
||||
**
|
||||
** Description Initialize GOEP Client role, must call before using any
|
||||
** other GOEPC APIs
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_Init(void)
|
||||
{
|
||||
#if (GOEP_DYNAMIC_MEMORY)
|
||||
if (!goepc_cb_ptr) {
|
||||
goepc_cb_ptr = (tGOEPC_CB *)osi_malloc(sizeof(tGOEPC_CB));
|
||||
if (!goepc_cb_ptr) {
|
||||
return GOEP_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
#endif /* #if (GOEP_DYNAMIC_MEMORY) */
|
||||
memset(&goepc_cb, 0, sizeof(tGOEPC_CB));
|
||||
goepc_cb.trace_level = BT_TRACE_LEVEL_ERROR;
|
||||
return GOEP_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_Deinit
|
||||
**
|
||||
** Description Deinit GOEP Client role, once deinit, can not use any other
|
||||
** GOEPC APIs until call GOEPC_Init again
|
||||
**
|
||||
*******************************************************************************/
|
||||
void GOEPC_Deinit(void)
|
||||
{
|
||||
#if (GOEP_DYNAMIC_MEMORY)
|
||||
if (goepc_cb_ptr) {
|
||||
osi_free(goepc_cb_ptr);
|
||||
goepc_cb_ptr = NULL;
|
||||
}
|
||||
#endif /* #if (GOEP_DYNAMIC_MEMORY) */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_Open
|
||||
**
|
||||
** Description Start the progress to establish a GOEP connection to server
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed, when the
|
||||
** connection is established, GOEPC_OPENED_EVT will come
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_Open(tOBEX_SVR_INFO *svr, tGOEPC_EVT_CBACK callback, UINT16 *out_handle)
|
||||
{
|
||||
UINT16 ret = GOEP_SUCCESS;
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
|
||||
do {
|
||||
/* check parameter, allow out_handle to be NULL */
|
||||
if (svr == NULL || callback == NULL) {
|
||||
ret = GOEP_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb = goepc_allocate_ccb();
|
||||
if (p_ccb == NULL) {
|
||||
ret = GOEP_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
if (OBEX_CreateConn(svr, goepc_obex_callback, &p_ccb->obex_handle) != OBEX_SUCCESS) {
|
||||
ret = GOEP_TL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
/* success */
|
||||
p_ccb->callback = callback;
|
||||
p_ccb->state = GOEPC_STATE_OPENING;
|
||||
if (out_handle) {
|
||||
*out_handle = p_ccb->allocated;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (ret != GOEP_SUCCESS && p_ccb != NULL) {
|
||||
goepc_free_ccb(p_ccb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_Close
|
||||
**
|
||||
** Description Close a GOEP connection immediately
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_Close(UINT16 handle)
|
||||
{
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
|
||||
return GOEP_BAD_HANDLE;
|
||||
}
|
||||
|
||||
p_ccb = &goepc_cb.ccb[ccb_idx];
|
||||
if (p_ccb->obex_handle) {
|
||||
OBEX_RemoveConn(p_ccb->obex_handle);
|
||||
}
|
||||
goepc_free_ccb(p_ccb);
|
||||
|
||||
return GOEP_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_SendRequest
|
||||
**
|
||||
** Description Send the prepared request packet to server
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_SendRequest(UINT16 handle)
|
||||
{
|
||||
UINT16 ret = GOEP_SUCCESS;
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
BOOLEAN final = FALSE;
|
||||
|
||||
do {
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
|
||||
ret = GOEP_BAD_HANDLE;
|
||||
break;
|
||||
}
|
||||
p_ccb = &goepc_cb.ccb[ccb_idx];
|
||||
|
||||
if (p_ccb->pkt == NULL) {
|
||||
ret = GOEP_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
final = OBEX_CheckFinalBit(p_ccb->pkt);
|
||||
/* check whether state machine allow this operation */
|
||||
if (!goepc_check_obex_req_allow(p_ccb->state, final)) {
|
||||
ret = GOEP_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_ccb->congest) {
|
||||
ret = GOEP_CONGEST;
|
||||
break;
|
||||
}
|
||||
|
||||
/* execute srm state machine */
|
||||
goepc_srm_sm_execute(p_ccb, TRUE, p_ccb->pkt_srm_en, p_ccb->pkt_srm_wait);
|
||||
|
||||
tGOEPC_DATA data;
|
||||
data.pkt = p_ccb->pkt;
|
||||
|
||||
p_ccb->last_pkt_opcode = p_ccb->curr_pkt_opcode;
|
||||
p_ccb->pkt = NULL;
|
||||
p_ccb->pkt_srm_en = FALSE;
|
||||
p_ccb->pkt_srm_wait = FALSE;
|
||||
|
||||
/* execute main state machine */
|
||||
if (final) {
|
||||
goepc_sm_execute(p_ccb, GOEPC_SM_EVENT_REQ_FB, &data);
|
||||
}
|
||||
else {
|
||||
goepc_sm_execute(p_ccb, GOEPC_SM_EVENT_REQ, &data);
|
||||
}
|
||||
/* since goepc_sm_execute may free ccb, can not access ccb here */
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_PrepareRequest
|
||||
**
|
||||
** Description Prepare a request packet, packet will be store internally
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_PrepareRequest(UINT16 handle, tOBEX_PARSE_INFO *info, UINT16 buff_size)
|
||||
{
|
||||
UINT16 ret = GOEP_SUCCESS;
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
BT_HDR *pkt = NULL;
|
||||
|
||||
do {
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
|
||||
ret = GOEP_BAD_HANDLE;
|
||||
break;
|
||||
}
|
||||
p_ccb = &goepc_cb.ccb[ccb_idx];
|
||||
|
||||
if (info == NULL || buff_size < OBEX_MIN_PACKET_SIZE) {
|
||||
ret = GOEP_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_ccb->pkt != NULL) {
|
||||
ret = GOEP_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!goepc_check_obex_req_param(info)) {
|
||||
ret = GOEP_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (OBEX_BuildRequest(info, buff_size, &pkt) != OBEX_SUCCESS) {
|
||||
ret = GOEP_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb->curr_pkt_opcode = info->opcode;
|
||||
p_ccb->pkt = pkt;
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_DropRequest
|
||||
**
|
||||
** Description Drop the prepared internal request packet
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_DropRequest(UINT16 handle)
|
||||
{
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
|
||||
return GOEP_BAD_HANDLE;
|
||||
}
|
||||
|
||||
tGOEPC_CCB *p_ccb = &goepc_cb.ccb[ccb_idx];
|
||||
if (p_ccb->pkt == NULL) {
|
||||
return GOEP_INVALID_STATE;
|
||||
}
|
||||
|
||||
osi_free(p_ccb->pkt);
|
||||
p_ccb->pkt = NULL;
|
||||
p_ccb->pkt_srm_en = FALSE;
|
||||
p_ccb->pkt_srm_wait = FALSE;
|
||||
return GOEP_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_RequestSetSRM
|
||||
**
|
||||
** Description Modify the prepared internal request packet, append SRM header
|
||||
** or SRMP header
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_RequestSetSRM(UINT16 handle, BOOLEAN srm_en, BOOLEAN srm_wait)
|
||||
{
|
||||
UINT16 ret = GOEP_SUCCESS;
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
|
||||
do {
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
|
||||
ret = GOEP_BAD_HANDLE;
|
||||
break;
|
||||
}
|
||||
p_ccb = &goepc_cb.ccb[ccb_idx];
|
||||
|
||||
if (!srm_en && !srm_wait) {
|
||||
ret = GOEP_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p_ccb->pkt == NULL) {
|
||||
ret = GOEP_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (srm_en) {
|
||||
if (OBEX_AppendHeaderSRM(p_ccb->pkt, OBEX_SRM_ENABLE) == OBEX_SUCCESS) {
|
||||
p_ccb->pkt_srm_en = TRUE;
|
||||
}
|
||||
else {
|
||||
ret = GOEP_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (srm_wait) {
|
||||
if (OBEX_AppendHeaderSRMP(p_ccb->pkt, OBEX_SRMP_WAIT) == OBEX_SUCCESS) {
|
||||
p_ccb->pkt_srm_wait = TRUE;
|
||||
}
|
||||
else {
|
||||
ret = GOEP_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function GOEPC_RequestAddHeader
|
||||
**
|
||||
** Description Modify the prepared internal request packet, append header
|
||||
**
|
||||
** Returns GOEP_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 GOEPC_RequestAddHeader(UINT16 handle, UINT8 header_id, const UINT8 *data, UINT16 data_len)
|
||||
{
|
||||
UINT16 ret = GOEP_SUCCESS;
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
|
||||
do {
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
|
||||
ret = GOEP_BAD_HANDLE;
|
||||
break;
|
||||
}
|
||||
p_ccb = &goepc_cb.ccb[ccb_idx];
|
||||
|
||||
if (p_ccb->pkt == NULL) {
|
||||
ret = GOEP_INVALID_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (data == NULL || data_len == 0) {
|
||||
ret = GOEP_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (OBEX_AppendHeaderRaw(p_ccb->pkt, header_id, data, data_len) != OBEX_SUCCESS) {
|
||||
ret = GOEP_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* #if (GOEPC_INCLUDED == TRUE) */
|
528
components/bt/host/bluedroid/stack/goep/goepc_main.c
Normal file
528
components/bt/host/bluedroid/stack/goep/goepc_main.c
Normal file
@ -0,0 +1,528 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "osi/osi.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/obex_api.h"
|
||||
#include "stack/goep_common.h"
|
||||
#include "stack/goepc_api.h"
|
||||
#include "goep_int.h"
|
||||
|
||||
#if (GOEPC_INCLUDED == TRUE)
|
||||
|
||||
#if GOEP_DYNAMIC_MEMORY == FALSE
|
||||
tGOEPC_CB goepc_cb;
|
||||
#else
|
||||
tGOEPC_CB *goepc_cb_ptr = NULL;
|
||||
#endif
|
||||
|
||||
tGOEPC_CCB *goepc_allocate_ccb(void)
|
||||
{
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
for (int i = 0; i < GOEPC_MAX_CONNECTION; ++i) {
|
||||
if (!goepc_cb.ccb[i].allocated) {
|
||||
goepc_cb.ccb[i].allocated = i + 1;
|
||||
p_ccb = &goepc_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
void goepc_free_ccb(tGOEPC_CCB *p_ccb)
|
||||
{
|
||||
if (p_ccb->pkt != NULL) {
|
||||
osi_free(p_ccb->pkt);
|
||||
}
|
||||
memset(p_ccb, 0, sizeof(tGOEPC_CCB));
|
||||
}
|
||||
|
||||
BOOLEAN goepc_check_obex_req_param(tOBEX_PARSE_INFO *info)
|
||||
{
|
||||
BOOLEAN ret = TRUE;
|
||||
switch (info->opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
if (info->max_packet_length < 255 || info->obex_version_number == 0) {
|
||||
ret = FALSE;
|
||||
}
|
||||
break;
|
||||
case OBEX_OPCODE_DISCONNECT:
|
||||
case OBEX_OPCODE_PUT:
|
||||
case OBEX_OPCODE_PUT_FINAL:
|
||||
case OBEX_OPCODE_GET:
|
||||
case OBEX_OPCODE_GET_FINAL:
|
||||
case OBEX_OPCODE_SETPATH:
|
||||
case OBEX_OPCODE_ACTION:
|
||||
case OBEX_OPCODE_SESSION:
|
||||
/* opcode allowed */
|
||||
break;
|
||||
case OBEX_OPCODE_ABORT:
|
||||
default:
|
||||
ret = FALSE;
|
||||
/* opcode not allowed */
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static tGOEPC_CCB *find_ccb_by_obex_handle(UINT16 obex_handle)
|
||||
{
|
||||
tGOEPC_CCB *p_ccb = NULL;
|
||||
for (int i = 0; i < GOEPC_MAX_CONNECTION; ++i) {
|
||||
if (goepc_cb.ccb[i].allocated && goepc_cb.ccb[i].obex_handle == obex_handle) {
|
||||
p_ccb = &goepc_cb.ccb[i];
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static void goepc_extra_srm_rsp(UINT8 opcode, BT_HDR *pkt, BOOLEAN *srm_en, BOOLEAN *srm_wait)
|
||||
{
|
||||
tOBEX_PARSE_INFO info;
|
||||
BOOLEAN srm_found = FALSE;
|
||||
BOOLEAN srmp_found = FALSE;
|
||||
if (OBEX_ParseResponse(pkt, opcode, &info) == OBEX_SUCCESS) {
|
||||
UINT8 *header = NULL;
|
||||
while((header = OBEX_GetNextHeader(pkt, &info)) != NULL) {
|
||||
switch (*header)
|
||||
{
|
||||
case OBEX_HEADER_ID_SRM:
|
||||
if (header[1] == OBEX_SRM_ENABLE) {
|
||||
*srm_en = TRUE;
|
||||
}
|
||||
srm_found = TRUE;
|
||||
break;
|
||||
case OBEX_HEADER_ID_SRM_PARAM:
|
||||
switch (header[1])
|
||||
{
|
||||
case OBEX_SRMP_ADD_PKT:
|
||||
/* goep should not use this */
|
||||
break;
|
||||
case OBEX_SRMP_WAIT:
|
||||
*srm_wait = TRUE;
|
||||
break;
|
||||
case OBEX_SRMP_ADD_PKT_WAIT:
|
||||
/* goep should not use this */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
srmp_found = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (srm_found && srmp_found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_act_congest(tGOEPC_CCB *p_ccb)
|
||||
{
|
||||
p_ccb->congest = TRUE;
|
||||
p_ccb->callback(p_ccb->allocated, GOEPC_CONGEST_EVT, NULL);
|
||||
}
|
||||
|
||||
static void goepc_act_uncongest(tGOEPC_CCB *p_ccb)
|
||||
{
|
||||
p_ccb->congest = FALSE;
|
||||
p_ccb->callback(p_ccb->allocated, GOEPC_UNCONGEST_EVT, NULL);
|
||||
}
|
||||
|
||||
static void goepc_act_mtu_chg(tGOEPC_CCB *p_ccb, tGOEPC_MTU_CHG *mtu_chg)
|
||||
{
|
||||
tGOEPC_MSG msg;
|
||||
msg.mtu_changed.peer_mtu = mtu_chg->peer_mtu;
|
||||
msg.mtu_changed.our_mtu = mtu_chg->our_mtu;
|
||||
p_ccb->peer_mtu = mtu_chg->peer_mtu;
|
||||
p_ccb->our_mtu = mtu_chg->our_mtu;
|
||||
p_ccb->callback(p_ccb->allocated, GOEPC_MTU_CHANGED_EVT, &msg);
|
||||
}
|
||||
|
||||
void goepc_obex_callback(UINT16 handle, UINT8 event, tOBEX_MSG *msg)
|
||||
{
|
||||
tGOEPC_DATA data;
|
||||
UINT8 goepc_sm_event = GOEPC_SM_EVENT_DISCONNECT;
|
||||
BOOLEAN exec_sm = FALSE;
|
||||
tGOEPC_CCB *p_ccb = find_ccb_by_obex_handle(handle);
|
||||
if (p_ccb == NULL) {
|
||||
GOEPC_TRACE_ERROR("goepc_obex_callback can not find a ccb\n");
|
||||
/* can not find a ccb in goepc, free resource and remove this connection */
|
||||
if (event == OBEX_DATA_EVT && msg->data.pkt) {
|
||||
osi_free(msg->data.pkt);
|
||||
}
|
||||
OBEX_RemoveConn(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case OBEX_CONNECT_EVT:
|
||||
data.connected.peer_mtu = msg->connect.peer_mtu;
|
||||
data.connected.our_mtu = msg->connect.our_mtu;
|
||||
goepc_sm_event = GOEPC_SM_EVENT_CONNECT;
|
||||
exec_sm = TRUE;
|
||||
break;
|
||||
case OBEX_MTU_CHANGE_EVT:
|
||||
data.mtu_chg.peer_mtu = msg->mtu_change.peer_mtu;
|
||||
data.mtu_chg.our_mtu = msg->mtu_change.our_mtu;
|
||||
goepc_act_mtu_chg(p_ccb, &data.mtu_chg);
|
||||
break;
|
||||
case OBEX_DISCONNECT_EVT:
|
||||
/* when we received this event, obex connection already disconnect */
|
||||
p_ccb->obex_handle = 0;
|
||||
goepc_sm_event = GOEPC_SM_EVENT_DISCONNECT;;
|
||||
exec_sm = TRUE;
|
||||
break;
|
||||
case OBEX_CONGEST_EVT:
|
||||
goepc_act_congest(p_ccb);
|
||||
break;
|
||||
case OBEX_UNCONGEST_EVT:
|
||||
goepc_act_uncongest(p_ccb);
|
||||
break;
|
||||
case OBEX_DATA_EVT:
|
||||
data.pkt = msg->data.pkt;
|
||||
if (OBEX_CheckContinueResponse(data.pkt)) {
|
||||
/* in OBEX 1.0, final bit of response code will always set, we need to check this */
|
||||
goepc_sm_event = GOEPC_SM_EVENT_RSP;
|
||||
}
|
||||
else if (OBEX_CheckFinalBit(data.pkt)) {
|
||||
goepc_sm_event = GOEPC_SM_EVENT_RSP_FB;
|
||||
}
|
||||
else {
|
||||
goepc_sm_event = GOEPC_SM_EVENT_RSP;
|
||||
}
|
||||
exec_sm = TRUE;
|
||||
break;
|
||||
default:
|
||||
/* other event, ignore */
|
||||
break;
|
||||
}
|
||||
|
||||
if (exec_sm) {
|
||||
goepc_sm_execute(p_ccb, goepc_sm_event, &data);
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_sm_act_connect(tGOEPC_CCB *p_ccb, tGOEPC_CONNECTED *connected)
|
||||
{
|
||||
tGOEPC_MSG msg;
|
||||
msg.opened.peer_mtu = connected->peer_mtu;
|
||||
msg.opened.our_mtu = connected->our_mtu;
|
||||
p_ccb->peer_mtu = connected->peer_mtu;
|
||||
p_ccb->our_mtu = connected->our_mtu;
|
||||
|
||||
/* main state machine transfer to OPENED_IDLE */
|
||||
p_ccb->state = GOEPC_STATE_OPENED_IDLE;
|
||||
p_ccb->callback(p_ccb->allocated, GOEPC_OPENED_EVT, &msg);
|
||||
}
|
||||
|
||||
static void goepc_sm_act_disconnect(tGOEPC_CCB *p_ccb)
|
||||
{
|
||||
tGOEPC_MSG msg;
|
||||
if (p_ccb->obex_handle) {
|
||||
OBEX_RemoveConn(p_ccb->obex_handle);
|
||||
}
|
||||
msg.closed.reason = GOEP_TL_ERROR;
|
||||
p_ccb->callback(p_ccb->allocated, GOEPC_CLOSED_EVT, &msg);
|
||||
/* free ccb, main state machine end */
|
||||
goepc_free_ccb(p_ccb);
|
||||
}
|
||||
|
||||
static void goepc_sm_act_send_req(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
|
||||
{
|
||||
UINT16 ret = OBEX_SendPacket(p_ccb->obex_handle, pkt);
|
||||
if (ret == OBEX_SUCCESS) {
|
||||
/* main state machine transfer to OPENED_REQ */
|
||||
p_ccb->state = GOEPC_STATE_OPENED_REQ;
|
||||
}
|
||||
else {
|
||||
/* send failed, something error in transport layer, disconnect */
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_sm_act_send_req_fb(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
|
||||
{
|
||||
UINT16 ret = OBEX_SendPacket(p_ccb->obex_handle, pkt);
|
||||
if (ret == OBEX_SUCCESS) {
|
||||
/* main state machine transfer to OPENED_RSP */
|
||||
p_ccb->state = GOEPC_STATE_OPENED_RSP;
|
||||
}
|
||||
else {
|
||||
/* send failed, something error in transport layer, disconnect */
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_sm_act_rsp(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
|
||||
{
|
||||
/* handle srm state transfer */
|
||||
BOOLEAN srm_en = FALSE;
|
||||
BOOLEAN srm_wait = FALSE;
|
||||
goepc_extra_srm_rsp(p_ccb->last_pkt_opcode, pkt, &srm_en, &srm_wait);
|
||||
goepc_srm_sm_execute(p_ccb, FALSE, srm_en, srm_wait);
|
||||
/* main state machine not change */
|
||||
|
||||
tGOEPC_MSG msg;
|
||||
msg.response.opcode = p_ccb->last_pkt_opcode;
|
||||
msg.response.final = FALSE;
|
||||
msg.response.srm_en = (p_ccb->srm_state == GOEPC_SRM_STATE_ENABLE_WAIT || p_ccb->srm_state == GOEPC_SRM_STATE_ENABLE);
|
||||
msg.response.srm_wait = (p_ccb->srm_state == GOEPC_SRM_STATE_ENABLE_WAIT);
|
||||
msg.response.pkt = pkt;
|
||||
p_ccb->callback(p_ccb->allocated, GOEPC_RESPONSE_EVT, &msg);
|
||||
}
|
||||
|
||||
static void goepc_sm_act_rsp_fb(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
|
||||
{
|
||||
tGOEPC_MSG msg;
|
||||
msg.response.opcode = p_ccb->last_pkt_opcode;
|
||||
msg.response.final = TRUE;
|
||||
msg.response.srm_en = FALSE;
|
||||
msg.response.srm_wait = FALSE;
|
||||
msg.response.pkt = pkt;
|
||||
/* operation complete, reset srm state */
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_IDLE;
|
||||
/* main state machine transfer to OPENED_IDLE */
|
||||
p_ccb->state = GOEPC_STATE_OPENED_IDLE;
|
||||
p_ccb->callback(p_ccb->allocated, GOEPC_RESPONSE_EVT, &msg);
|
||||
}
|
||||
|
||||
|
||||
static void goepc_sm_state_opening(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case GOEPC_SM_EVENT_CONNECT:
|
||||
goepc_sm_act_connect(p_ccb, &p_data->connected);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_DISCONNECT:
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_RSP:
|
||||
case GOEPC_SM_EVENT_RSP_FB:
|
||||
GOEPC_TRACE_ERROR("goepc_sm_state_opening received unexpected response from peer\n");
|
||||
if (p_data->pkt != NULL) {
|
||||
osi_free(p_data->pkt);
|
||||
}
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
break;
|
||||
default:
|
||||
GOEPC_TRACE_ERROR("goepc_sm_state_opening unexpected event: 0x%x\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_sm_state_opened_idle(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case GOEPC_SM_EVENT_DISCONNECT:
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_REQ:
|
||||
goepc_sm_act_send_req(p_ccb, p_data->pkt);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_REQ_FB:
|
||||
goepc_sm_act_send_req_fb(p_ccb, p_data->pkt);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_RSP:
|
||||
case GOEPC_SM_EVENT_RSP_FB:
|
||||
GOEPC_TRACE_ERROR("goepc_sm_state_opened_idle received unexpected response from peer\n");
|
||||
/* peer sent a packet to us when we didn't request */
|
||||
if (p_data->pkt != NULL) {
|
||||
osi_free(p_data->pkt);
|
||||
}
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
break;
|
||||
default:
|
||||
GOEPC_TRACE_ERROR("goepc_sm_state_opened_idle unexpected event: 0x%x\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_sm_state_opened_req(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case GOEPC_SM_EVENT_DISCONNECT:
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_REQ:
|
||||
goepc_sm_act_send_req(p_ccb, p_data->pkt);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_REQ_FB:
|
||||
goepc_sm_act_send_req_fb(p_ccb, p_data->pkt);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_RSP:
|
||||
goepc_sm_act_rsp(p_ccb, p_data->pkt);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_RSP_FB:
|
||||
goepc_sm_act_rsp_fb(p_ccb, p_data->pkt);
|
||||
break;
|
||||
default:
|
||||
GOEPC_TRACE_ERROR("goepc_sm_state_opened_req unexpected event: 0x%x\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_sm_state_opened_rsp(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case GOEPC_SM_EVENT_DISCONNECT:
|
||||
goepc_sm_act_disconnect(p_ccb);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_REQ_FB:
|
||||
goepc_sm_act_send_req_fb(p_ccb, p_data->pkt);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_RSP:
|
||||
goepc_sm_act_rsp(p_ccb, p_data->pkt);
|
||||
break;
|
||||
case GOEPC_SM_EVENT_RSP_FB:
|
||||
goepc_sm_act_rsp_fb(p_ccb, p_data->pkt);
|
||||
break;
|
||||
default:
|
||||
GOEPC_TRACE_ERROR("goepc_sm_state_opened_rsp unexpected event: 0x%x\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BOOLEAN goepc_check_obex_req_allow(UINT8 state, BOOLEAN final)
|
||||
{
|
||||
BOOLEAN ret = FALSE;
|
||||
if (final) {
|
||||
switch (state)
|
||||
{
|
||||
case GOEPC_STATE_OPENED_IDLE:
|
||||
case GOEPC_STATE_OPENED_REQ:
|
||||
case GOEPC_STATE_OPENED_RSP:
|
||||
ret = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (state)
|
||||
{
|
||||
case GOEPC_STATE_OPENED_IDLE:
|
||||
case GOEPC_STATE_OPENED_REQ:
|
||||
ret = TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void goepc_sm_execute(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
|
||||
{
|
||||
switch (p_ccb->state)
|
||||
{
|
||||
case GOEPC_STATE_INIT:
|
||||
/* do nothing */
|
||||
break;
|
||||
case GOEPC_STATE_OPENING:
|
||||
goepc_sm_state_opening(p_ccb, event, p_data);
|
||||
break;
|
||||
case GOEPC_STATE_OPENED_IDLE:
|
||||
goepc_sm_state_opened_idle(p_ccb, event, p_data);
|
||||
break;
|
||||
case GOEPC_STATE_OPENED_REQ:
|
||||
goepc_sm_state_opened_req(p_ccb, event, p_data);
|
||||
break;
|
||||
case GOEPC_STATE_OPENED_RSP:
|
||||
goepc_sm_state_opened_rsp(p_ccb, event, p_data);
|
||||
break;
|
||||
default:
|
||||
GOEPC_TRACE_ERROR("goepc_sm_execute unexpected state: 0x%x\n", p_ccb->state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_srm_sm_act_req(tGOEPC_CCB *p_ccb, BOOLEAN srm_en, BOOLEAN srm_wait)
|
||||
{
|
||||
switch (p_ccb->srm_state)
|
||||
{
|
||||
case GOEPC_SRM_STATE_IDLE:
|
||||
if (srm_en) {
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_REQ;
|
||||
p_ccb->srm_wait = srm_wait;
|
||||
}
|
||||
else {
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_DISABLE;
|
||||
}
|
||||
break;
|
||||
case GOEPC_SRM_STATE_ENABLE_WAIT:
|
||||
if (!srm_wait) {
|
||||
p_ccb->srm_wait = FALSE;
|
||||
}
|
||||
if (!p_ccb->srm_wait && !p_ccb->srm_peer_wait) {
|
||||
/* no more wait, transfer to ENABLE */
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void goepc_srm_sm_act_rsp(tGOEPC_CCB *p_ccb, BOOLEAN srm_en, BOOLEAN srm_wait)
|
||||
{
|
||||
switch (p_ccb->srm_state)
|
||||
{
|
||||
case GOEPC_SRM_STATE_IDLE:
|
||||
/* peer can not request to enable srm, ignore */
|
||||
break;
|
||||
case GOEPC_SRM_STATE_REQ:
|
||||
if (srm_en) {
|
||||
p_ccb->srm_peer_wait = srm_wait;
|
||||
if (p_ccb->srm_wait || p_ccb->srm_peer_wait) {
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE_WAIT;
|
||||
}
|
||||
else {
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_DISABLE;
|
||||
}
|
||||
break;
|
||||
case GOEPC_SRM_STATE_ENABLE_WAIT:
|
||||
if (!srm_wait) {
|
||||
p_ccb->srm_peer_wait = FALSE;
|
||||
}
|
||||
if (!p_ccb->srm_wait && !p_ccb->srm_peer_wait) {
|
||||
/* no more wait, transfer to ENABLE */
|
||||
p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void goepc_srm_sm_execute(tGOEPC_CCB *p_ccb, BOOLEAN is_req, BOOLEAN srm_en, BOOLEAN srm_wait)
|
||||
{
|
||||
if (is_req) {
|
||||
goepc_srm_sm_act_req(p_ccb, srm_en, srm_wait);
|
||||
}
|
||||
else {
|
||||
goepc_srm_sm_act_rsp(p_ccb, srm_en, srm_wait);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if (GOEPC_INCLUDED == TRUE) */
|
103
components/bt/host/bluedroid/stack/goep/include/goep_int.h
Normal file
103
components/bt/host/bluedroid/stack/goep/include/goep_int.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/obex_api.h"
|
||||
#include "stack/goep_common.h"
|
||||
#include "stack/goepc_api.h"
|
||||
|
||||
#if (GOEPC_INCLUDED == TRUE)
|
||||
|
||||
/* GOEPC state machine events */
|
||||
enum {
|
||||
GOEPC_SM_EVENT_CONNECT = 0,
|
||||
GOEPC_SM_EVENT_DISCONNECT,
|
||||
GOEPC_SM_EVENT_REQ,
|
||||
GOEPC_SM_EVENT_REQ_FB,
|
||||
GOEPC_SM_EVENT_RSP,
|
||||
GOEPC_SM_EVENT_RSP_FB,
|
||||
};
|
||||
|
||||
/* GOEPC state machine states */
|
||||
enum {
|
||||
GOEPC_STATE_INIT = 0,
|
||||
GOEPC_STATE_OPENING,
|
||||
GOEPC_STATE_OPENED_IDLE,
|
||||
GOEPC_STATE_OPENED_REQ,
|
||||
GOEPC_STATE_OPENED_RSP,
|
||||
};
|
||||
|
||||
/* GOEPC srm state machine states */
|
||||
enum {
|
||||
GOEPC_SRM_STATE_IDLE = 0,
|
||||
GOEPC_SRM_STATE_REQ,
|
||||
GOEPC_SRM_STATE_ENABLE_WAIT,
|
||||
GOEPC_SRM_STATE_ENABLE,
|
||||
GOEPC_SRM_STATE_DISABLE,
|
||||
};
|
||||
|
||||
/* GOEPC Connection Control block */
|
||||
typedef struct {
|
||||
tGOEPC_EVT_CBACK *callback; /* GOEP event callback function */
|
||||
UINT16 obex_handle; /* OBEX connection handle */
|
||||
UINT16 peer_mtu; /* lower layer connection peer MTU */
|
||||
UINT16 our_mtu; /* lower layer connection our MTU */
|
||||
BOOLEAN congest; /* lower layer connection congestion status */
|
||||
|
||||
BT_HDR *pkt; /* packet prepared in this GOEP client */
|
||||
BOOLEAN pkt_srm_en; /* whether prepared packet had set SRM to enable */
|
||||
BOOLEAN pkt_srm_wait; /* whether prepared packet had set SRMP to wait */
|
||||
UINT8 curr_pkt_opcode; /* prepared packet opcode */
|
||||
|
||||
UINT8 last_pkt_opcode; /* last sent packet opcode */
|
||||
BOOLEAN srm_wait; /* whether we had set SRMP to wait */
|
||||
BOOLEAN srm_peer_wait; /* whether peer had set SRMP to wait */
|
||||
UINT8 srm_state; /* SRM state machine */
|
||||
UINT8 state; /* main state machine */
|
||||
UINT8 allocated; /* 0, not allocated. index+1, otherwise. equal to api handle */
|
||||
} tGOEPC_CCB;
|
||||
|
||||
/* GOEPC Control block */
|
||||
typedef struct {
|
||||
tGOEPC_CCB ccb[GOEPC_MAX_CONNECTION]; /* connection control blocks */
|
||||
UINT8 trace_level; /* trace level */
|
||||
} tGOEPC_CB;
|
||||
|
||||
#if GOEP_DYNAMIC_MEMORY == FALSE
|
||||
extern tGOEPC_CB goepc_cb;
|
||||
#else
|
||||
extern tGOEPC_CB *goepc_cb_ptr;
|
||||
#define goepc_cb (*goepc_cb_ptr)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} tGOEPC_CONNECTED;
|
||||
|
||||
typedef struct {
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} tGOEPC_MTU_CHG;
|
||||
|
||||
typedef union {
|
||||
tGOEPC_CONNECTED connected;
|
||||
tGOEPC_MTU_CHG mtu_chg;
|
||||
BT_HDR *pkt;
|
||||
} tGOEPC_DATA;
|
||||
|
||||
tGOEPC_CCB *goepc_allocate_ccb(void);
|
||||
void goepc_free_ccb(tGOEPC_CCB *p_ccb);
|
||||
void goepc_obex_callback(UINT16 handle, UINT8 event, tOBEX_MSG *msg);
|
||||
BOOLEAN goepc_check_obex_req_allow(UINT8 state, BOOLEAN final);
|
||||
BOOLEAN goepc_check_obex_req_param(tOBEX_PARSE_INFO *info);
|
||||
void goepc_sm_execute(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data);
|
||||
void goepc_srm_sm_execute(tGOEPC_CCB *p_ccb, BOOLEAN is_req, BOOLEAN srm_en, BOOLEAN srm_wait);
|
||||
|
||||
#endif /* #if (GOEPC_INCLUDED == TRUE) */
|
@ -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.
|
||||
**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#define GOEP_SUCCESS 0 /* Operation successful */
|
||||
#define GOEP_FAILURE 1 /* Operation failed */
|
||||
#define GOEP_NO_RESOURCES 3 /* Not enough resources */
|
||||
#define GOEP_BAD_HANDLE 4 /* Bad handle */
|
||||
#define GOEP_INVALID_PARAM 5 /* Invalid parameter */
|
||||
#define GOEP_INVALID_STATE 6 /* Operation not allow in current state */
|
||||
#define GOEP_CONGEST 7 /* Congest */
|
||||
#define GOEP_TL_ERROR 8 /* Lower transport layer error */
|
82
components/bt/host/bluedroid/stack/include/stack/goepc_api.h
Normal file
82
components/bt/host/bluedroid/stack/include/stack/goepc_api.h
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/goep_common.h"
|
||||
#include "stack/obex_api.h"
|
||||
|
||||
#if (GOEPC_INCLUDED == TRUE)
|
||||
|
||||
enum {
|
||||
GOEPC_OPENED_EVT, /* connection open */
|
||||
GOEPC_CLOSED_EVT, /* disconnect unexpected */
|
||||
GOEPC_MTU_CHANGED_EVT, /* lower layer MTU change */
|
||||
GOEPC_CONGEST_EVT, /* lower layer connection congest */
|
||||
GOEPC_UNCONGEST_EVT, /* lower layer connection uncongest */
|
||||
GOEPC_RESPONSE_EVT /* response from server */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
UINT16 peer_mtu; /* peer mtu of lower level connection */
|
||||
UINT16 our_mtu; /* our mtu of lower level connection */
|
||||
} tGOEPC_MSG_OPENED;
|
||||
|
||||
typedef struct {
|
||||
UINT8 reason; /* connection close reason */
|
||||
} tGOEPC_MSG_CLOSED;
|
||||
|
||||
typedef struct {
|
||||
UINT16 peer_mtu; /* peer mtu of lower level connection */
|
||||
UINT16 our_mtu; /* our mtu of lower level connection */
|
||||
} tGOEPC_MSG_MTU_CHANGED;
|
||||
|
||||
typedef struct {
|
||||
UINT8 opcode; /* which opcode that this packet response to */
|
||||
BOOLEAN final; /* whether this is a final packet */
|
||||
BOOLEAN srm_en; /* whether srm is enable */
|
||||
BOOLEAN srm_wait; /* whether srm wait is set, set by peer or by us */
|
||||
BT_HDR *pkt; /* pointer to response packet */
|
||||
} tGOEPC_MSG_RESPONSE;
|
||||
|
||||
typedef union {
|
||||
tGOEPC_MSG_OPENED opened;
|
||||
tGOEPC_MSG_CLOSED closed;
|
||||
tGOEPC_MSG_MTU_CHANGED mtu_changed;
|
||||
tGOEPC_MSG_RESPONSE response;
|
||||
} tGOEPC_MSG;
|
||||
|
||||
typedef void (tGOEPC_EVT_CBACK)(UINT16 handle, UINT8 event, tGOEPC_MSG *msg);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs are called by bluetooth stack automatically
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 GOEPC_Init(void);
|
||||
|
||||
extern void GOEPC_Deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs must be executed in btu task
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 GOEPC_Open(tOBEX_SVR_INFO *p_svr, tGOEPC_EVT_CBACK callback, UINT16 *out_handle);
|
||||
|
||||
extern UINT16 GOEPC_Close(UINT16 handle);
|
||||
|
||||
extern UINT16 GOEPC_SendRequest(UINT16 handle);
|
||||
|
||||
extern UINT16 GOEPC_PrepareRequest(UINT16 handle, tOBEX_PARSE_INFO *info, UINT16 buff_size);
|
||||
|
||||
extern UINT16 GOEPC_DropRequest(UINT16 handle);
|
||||
|
||||
extern UINT16 GOEPC_RequestSetSRM(UINT16 handle, BOOLEAN srm_en, BOOLEAN srm_wait);
|
||||
|
||||
extern UINT16 GOEPC_RequestAddHeader(UINT16 handle, UINT8 header_id, const UINT8 *data, UINT16 data_len);
|
||||
|
||||
#endif /* #if (GOEPC_INCLUDED == TRUE) */
|
264
components/bt/host/bluedroid/stack/include/stack/obex_api.h
Normal file
264
components/bt/host/bluedroid/stack/include/stack/obex_api.h
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
/* API function return value result codes. */
|
||||
#define OBEX_SUCCESS 0 /* Operation successful */
|
||||
#define OBEX_FAILURE 1 /* Operation failed */
|
||||
#define OBEX_NO_RESOURCES 3 /* Not enough resources */
|
||||
#define OBEX_BAD_HANDLE 4 /* Bad handle */
|
||||
#define OBEX_INVALID_PARAM 5 /* Invalid parameter */
|
||||
#define OBEX_NOT_OPEN 6 /* Connection not open */
|
||||
#define OBEX_PACKET_TOO_LARGE 7 /* Packet size large than MTU */
|
||||
#define OBEX_ERROR_TL 8 /* Operation failed in transport layer */
|
||||
#define OBEX_TRY_AGAIN 9 /* Operation failed, connection congestion, try again */
|
||||
|
||||
|
||||
/*
|
||||
* OBEX profile definitions
|
||||
*/
|
||||
#define OBEX_OPCODE_CONNECT 0x80
|
||||
#define OBEX_OPCODE_DISCONNECT 0x81
|
||||
#define OBEX_OPCODE_PUT 0x02
|
||||
#define OBEX_OPCODE_PUT_FINAL 0x82
|
||||
#define OBEX_OPCODE_GET 0x03
|
||||
#define OBEX_OPCODE_GET_FINAL 0x83
|
||||
#define OBEX_OPCODE_SETPATH 0x85
|
||||
#define OBEX_OPCODE_ACTION 0x06
|
||||
#define OBEX_OPCODE_SESSION 0x87
|
||||
#define OBEX_OPCODE_ABORT 0xFF
|
||||
|
||||
#define OBEX_RESPONSE_CODE_CONTINUE 0x10
|
||||
#define OBEX_RESPONSE_CODE_OK 0x20
|
||||
#define OBEX_RESPONSE_CODE_CREATED 0x21
|
||||
#define OBEX_RESPONSE_CODE_ACCEPTED 0x22
|
||||
#define OBEX_RESPONSE_CODE_NON_AUTHORITATIVE_INFO 0x23
|
||||
#define OBEX_RESPONSE_CODE_NO_CONTENT 0x24
|
||||
#define OBEX_RESPONSE_CODE_RESET_CONTENT 0x25
|
||||
#define OBEX_RESPONSE_CODE_PARTIAL_CONTENT 0x26
|
||||
#define OBEX_RESPONSE_CODE_MULTIPLE_CHOICES 0x30
|
||||
#define OBEX_RESPONSE_CODE_MOVED_PERMANENTLY 0x31
|
||||
#define OBEX_RESPONSE_CODE_MOVED_TEMPORARILY 0x31
|
||||
#define OBEX_RESPONSE_CODE_SEE_OHTER 0x33
|
||||
#define OBEX_RESPONSE_CODE_NOT_MODIFIED 0x34
|
||||
#define OBEX_RESPONSE_CODE_USE_PROXY 0x35
|
||||
#define OBEX_RESPONSE_CODE_BAD_REQUEST 0x40
|
||||
#define OBEX_RESPONSE_CODE_UNAUTHORIZED 0x41
|
||||
#define OBEX_RESPONSE_CODE_PAYMENT_REQUIRED 0x42
|
||||
#define OBEX_RESPONSE_CODE_FORBIDDEN 0x43
|
||||
#define OBEX_RESPONSE_CODE_NOT_FOUND 0x44
|
||||
#define OBEX_RESPONSE_CODE_METHOD_NOT_ALLOWED 0x45
|
||||
#define OBEX_RESPONSE_CODE_NOT_ACCEPTABLE 0x46
|
||||
#define OBEX_RESPONSE_CODE_PROXY_AUTHENTICATION_REQUIRED 0x47
|
||||
#define OBEX_RESPONSE_CODE_REQUEST_TIME_OUT 0x48
|
||||
#define OBEX_RESPONSE_CODE_CONFLICT 0x49
|
||||
#define OBEX_RESPONSE_CODE_GONE 0x4A
|
||||
#define OBEX_RESPONSE_CODE_LENGTH_REQUIRED 0x4B
|
||||
#define OBEX_RESPONSE_CODE_PRECONDITION_FAILED 0x4C
|
||||
#define OBEX_RESPONSE_CODE_REQUESTED_ENTITY_TOO_LARGE 0x4D
|
||||
#define OBEX_RESPONSE_CODE_REQUEST_URL_TOO_LARGE 0x4E
|
||||
#define OBEX_RESPONSE_CODE_UNSUPPORTED_MEDIA_TYPE 0x4F
|
||||
#define OBEX_RESPONSE_CODE_INTERNAL_SERVER_ERROR 0x50
|
||||
#define OBEX_RESPONSE_CODE_NOT_IMPLEMENTED 0x51
|
||||
#define OBEX_RESPONSE_CODE_BAD_GATEWAY 0x52
|
||||
#define OBEX_RESPONSE_CODE_SERVICE_UNAVAILABLE 0x53
|
||||
#define OBEX_RESPONSE_CODE_GATEWAY_TIMEOUT 0x54
|
||||
#define OBEX_RESPONSE_CODE_HTTP_VERSION_NOT_SUPPORTED 0x55
|
||||
#define OBEX_RESPONSE_CODE_DATABASE_FULL 0x60
|
||||
#define OBEX_RESPONSE_CODE_DATABASE_LOCKED 0x61
|
||||
|
||||
#define OBEX_FINAL_BIT_MASK 0x80
|
||||
|
||||
#define OBEX_CONNECT_FLAGS 0x01 /* support multiple link */
|
||||
#define OBEX_SETPATH_FLAGS 0x03 /* default flags */
|
||||
|
||||
#define OBEX_PACKET_LENGTH_MAX (0xFFFF-1)
|
||||
#define OBEX_PACKET_LENGTH_MIN 255
|
||||
|
||||
#define OBEX_VERSION_NUMBER 0x15
|
||||
|
||||
/* Header identifiers */
|
||||
#define OBEX_HEADER_ID_U2B_MASK 0xC0 /* upper 2 bits of header ID are user to indicate the header encoding */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE1 0x00 /* null terminated Unicode text, length prefixed with 2 byte unsigned integer */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE2 0x40 /* byte sequence, length prefixed with 2 byte unsigned integer */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE3 0x80 /* 1 byte quantity */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE4 0xC0 /* 4 byte quantity - transmitted in network byte order (high byte first) */
|
||||
|
||||
#define OBEX_HEADER_ID_COUNT 0xC0
|
||||
#define OBEX_HEADER_ID_NAME 0x01
|
||||
#define OBEX_HEADER_ID_TYPE 0x42
|
||||
#define OBEX_HEADER_ID_LENGTH 0xC3
|
||||
#define OBEX_HEADER_ID_TIME_ISO8601 0x44
|
||||
#define OBEX_HEADER_ID_TIME_4BYTE 0xC4
|
||||
#define OBEX_HEADER_ID_DESCRIPTION 0x05
|
||||
#define OBEX_HEADER_ID_TARGET 0x46
|
||||
#define OBEX_HEADER_ID_HTTP 0x47
|
||||
#define OBEX_HEADER_ID_BODY 0x48
|
||||
#define OBEX_HEADER_ID_END_OF_BODY 0x49
|
||||
#define OBEX_HEADER_ID_WHO 0x4A
|
||||
#define OBEX_HEADER_ID_CONNECTION_ID 0xCB
|
||||
#define OBEX_HEADER_ID_APP_PARAM 0x4C
|
||||
#define OBEX_HEADER_ID_AUTH_CHALLENGE 0x4D
|
||||
#define OBEX_HEADER_ID_AUTH_RESPONSE 0x4E
|
||||
#define OBEX_HEADER_ID_CREATOR_ID 0xCF
|
||||
#define OBEX_HEADER_ID_WAN_UUID 0x50
|
||||
#define OBEX_HEADER_ID_OBJECT_CLASS 0x51
|
||||
#define OBEX_HEADER_ID_SESSION_PARAM 0x52
|
||||
#define OBEX_HEADER_ID_SESSION_SEQ_NUM 0x93
|
||||
#define OBEX_HEADER_ID_ACTION_ID 0x94
|
||||
#define OBEX_HEADER_ID_DESTNAME 0x15
|
||||
#define OBEX_HEADER_ID_PERMISSIONS 0xD6
|
||||
#define OBEX_HEADER_ID_SRM 0x97
|
||||
#define OBEX_HEADER_ID_SRM_PARAM 0x98
|
||||
/* Reserved for future use: 0x19 to 0x2F */
|
||||
/* User defined: 0x30 to 0x3F */
|
||||
|
||||
#define OBEX_ACTION_ID_COPY 0x00
|
||||
#define OBEX_ACTION_ID_MOVE_RENAME 0x01
|
||||
#define OBEX_ACTION_ID_SET_PERMISSIONS 0x02
|
||||
|
||||
#define OBEX_SRM_DISABLE 0x00
|
||||
#define OBEX_SRM_ENABLE 0x01
|
||||
#define OBEX_SRM_SUPPORT 0x02
|
||||
|
||||
#define OBEX_SRMP_ADD_PKT 0x00
|
||||
#define OBEX_SRMP_WAIT 0x01
|
||||
#define OBEX_SRMP_ADD_PKT_WAIT 0x02
|
||||
|
||||
#define OBEX_MIN_PACKET_SIZE 3
|
||||
|
||||
enum {
|
||||
/* client event */
|
||||
OBEX_CONNECT_EVT, /* connection opened */
|
||||
OBEX_DISCONNECT_EVT, /* connection disconnected */
|
||||
/* server event */
|
||||
OBEX_CONN_INCOME_EVT, /* an incoming connection */
|
||||
/* client or server event */
|
||||
OBEX_MTU_CHANGE_EVT, /* connection mtu changed */
|
||||
OBEX_CONGEST_EVT, /* connection congested */
|
||||
OBEX_UNCONGEST_EVT, /* connection is not congested */
|
||||
OBEX_DATA_EVT /* data received */
|
||||
};
|
||||
|
||||
enum {
|
||||
OBEX_OVER_L2CAP = 0,
|
||||
OBEX_OVER_RFCOMM,
|
||||
OBEX_NUM_TL
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 psm; /* l2cap psm */
|
||||
UINT16 sec_mask; /* security mask */
|
||||
UINT16 pref_mtu; /* preferred mtu, limited by L2CAP_MTU_SIZE */
|
||||
BD_ADDR addr; /* peer bluetooth device address */
|
||||
} tOBEX_OVER_L2CAP_SVR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT8 tl; /* transport type, OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */
|
||||
union
|
||||
{
|
||||
tOBEX_OVER_L2CAP_SVR l2cap;
|
||||
};
|
||||
} tOBEX_SVR_INFO;
|
||||
|
||||
typedef struct {
|
||||
UINT8 opcode;
|
||||
UINT8 response_code;
|
||||
/* Connect */
|
||||
UINT8 obex_version_number;
|
||||
UINT16 max_packet_length;
|
||||
/* Connect or SetPath */
|
||||
UINT8 flags;
|
||||
/* Internal use */
|
||||
UINT16 next_header_pos;
|
||||
} tOBEX_PARSE_INFO;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} connect;
|
||||
|
||||
struct {
|
||||
UINT16 svr_handle;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} conn_income;
|
||||
|
||||
struct {
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} mtu_change;
|
||||
|
||||
struct {
|
||||
BT_HDR *pkt;
|
||||
} data;
|
||||
} tOBEX_MSG;
|
||||
|
||||
typedef void (tOBEX_MSG_CBACK)(UINT16 handle, UINT8 event, tOBEX_MSG *msg);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs are called by bluetooth stack automatically
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 OBEX_Init(void);
|
||||
|
||||
extern void OBEX_Deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs must be executed in btu task
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 OBEX_CreateConn(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_handle);
|
||||
|
||||
extern UINT16 OBEX_RemoveConn(UINT16 handle);
|
||||
|
||||
extern UINT16 OBEX_SendPacket(UINT16 handle, BT_HDR *pkt);
|
||||
|
||||
extern UINT16 OBEX_RegisterServer(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_svr_handle);
|
||||
|
||||
extern UINT16 OBEX_DeregisterServer(UINT16 svr_handle);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs are util function, can be executed in btu or btc task
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt);
|
||||
|
||||
extern UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt);
|
||||
|
||||
extern UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header);
|
||||
|
||||
extern UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UINT16 data_len);
|
||||
|
||||
extern UINT16 OBEX_AppendHeaderSRM(BT_HDR *pkt, UINT8 value);
|
||||
|
||||
extern UINT16 OBEX_AppendHeaderSRMP(BT_HDR *pkt, UINT8 value);
|
||||
|
||||
extern UINT16 OBEX_GetPacketFreeSpace(BT_HDR *pkt);
|
||||
|
||||
extern UINT16 OBEX_GetPacketLength(BT_HDR *pkt);
|
||||
|
||||
extern UINT16 OBEX_ParseRequest(BT_HDR *pkt, tOBEX_PARSE_INFO *info);
|
||||
|
||||
extern UINT16 OBEX_ParseResponse(BT_HDR *pkt, UINT8 opcode, tOBEX_PARSE_INFO *info);
|
||||
|
||||
extern BOOLEAN OBEX_CheckFinalBit(BT_HDR *pkt);
|
||||
|
||||
extern BOOLEAN OBEX_CheckContinueResponse(BT_HDR *pkt);
|
||||
|
||||
extern UINT8 *OBEX_GetNextHeader(BT_HDR *pkt, tOBEX_PARSE_INFO *info);
|
||||
|
||||
extern UINT16 OBEX_GetHeaderLength(UINT8 *header);
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
@ -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.
|
||||
|
73
components/bt/host/bluedroid/stack/obex/include/obex_int.h
Normal file
73
components/bt/host/bluedroid/stack/obex/include/obex_int.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/obex_api.h"
|
||||
#include "obex_tl.h"
|
||||
#include "obex_tl_l2cap.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
#define OBEX_BT_HDR_MIN_OFFSET OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET /* should set to max value of all transport layer */
|
||||
|
||||
#define OBEX_ROLE_CLIENT 0x01
|
||||
#define OBEX_ROLE_SERVER 0x02
|
||||
|
||||
/* OBEX connection state */
|
||||
#define OBEX_STATE_IDLE 0 /* No connection */
|
||||
#define OBEX_STATE_OPENING 1 /* Starting to open a connection */
|
||||
#define OBEX_STATE_OPENED 2 /* Connection opened */
|
||||
|
||||
/* Store 16 bits data in big endian format, not modify the p_buf */
|
||||
#define STORE16BE(p_buf, data) do { *p_buf = ((data)>>8)&0xff; \
|
||||
*(p_buf+1) = (data)&0xff;} while(0)
|
||||
|
||||
/* OBEX Connection Control block */
|
||||
typedef struct {
|
||||
tOBEX_MSG_CBACK *callback; /* Connection msg callback function */
|
||||
UINT16 tl_hdl; /* Transport layer non-zeros connection handle*/
|
||||
UINT16 tl_peer_mtu; /* Transport layer peer mtu */
|
||||
UINT16 tl_our_mtu; /* Transport layer our mtu */
|
||||
UINT8 tl_cong; /* 1 if transport layer congestion, otherwise 0 */
|
||||
UINT8 tl; /* OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */
|
||||
UINT8 allocated; /* 0, not allocated. index+1, otherwise. equal to api handle */
|
||||
UINT8 state; /* This OBEX connection state */
|
||||
UINT8 role; /* This OBEX connection role */
|
||||
} tOBEX_CCB;
|
||||
|
||||
/* OBEX Server Control block */
|
||||
typedef struct {
|
||||
tOBEX_MSG_CBACK *callback; /* Connection msg callback function */
|
||||
UINT16 tl_hdl; /* Transport layer non-zeros server handle*/
|
||||
UINT8 tl; /* OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */
|
||||
UINT8 allocated; /* 0, not allocated. index+1, otherwise. */
|
||||
} tOBEX_SCB;
|
||||
|
||||
/* OBEX Control block */
|
||||
typedef struct {
|
||||
tOBEX_CCB ccb[OBEX_MAX_CONNECTION]; /* connection control blocks */
|
||||
tOBEX_SCB scb[OBEX_MAX_SERVER]; /* server control blocks */
|
||||
tOBEX_TL_OPS *tl_ops[OBEX_NUM_TL]; /* transport operation function pointer */
|
||||
UINT8 trace_level; /* trace level */
|
||||
} tOBEX_CB;
|
||||
|
||||
#if OBEX_DYNAMIC_MEMORY == FALSE
|
||||
extern tOBEX_CB obex_cb;
|
||||
#else
|
||||
extern tOBEX_CB *obex_cb_ptr;
|
||||
#define obex_cb (*obex_cb_ptr)
|
||||
#endif
|
||||
|
||||
void obex_tl_l2cap_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg);
|
||||
tOBEX_CCB *obex_allocate_ccb(void);
|
||||
tOBEX_SCB *obex_allocate_scb(void);
|
||||
void obex_free_ccb(tOBEX_CCB *p_ccb);
|
||||
void obex_free_scb(tOBEX_SCB *p_scb);
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
88
components/bt/host/bluedroid/stack/obex/include/obex_tl.h
Normal file
88
components/bt/host/bluedroid/stack/obex/include/obex_tl.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
/* Return code of obex_tl_send_data */
|
||||
#define OBEX_TL_FAILED FALSE
|
||||
#define OBEX_TL_SUCCESS TRUE
|
||||
#define OBEX_TL_CONGESTED 2
|
||||
|
||||
typedef enum {
|
||||
OBEX_TL_CONN_OPEN_EVT,
|
||||
OBEX_TL_CONN_INCOME_EVT,
|
||||
OBEX_TL_DIS_CONN_EVT,
|
||||
OBEX_TL_CONGEST_EVT,
|
||||
OBEX_TL_UNCONGEST_EVT,
|
||||
OBEX_TL_MTU_CHANGE_EVT,
|
||||
OBEX_TL_DATA_EVT
|
||||
} tOBEX_TL_EVT;
|
||||
|
||||
typedef union {
|
||||
/* general struct, used to retrieve handle */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
} any;
|
||||
|
||||
/* struct for OBEX_TL_CONN_OPEN_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} conn_open;
|
||||
|
||||
/* struct for OBEX_TL_CONN_INCOME_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
UINT16 svr_hdl;
|
||||
} conn_income;
|
||||
|
||||
/* struct for OBEX_TL_MTU_CHANGE_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} mtu_chg;
|
||||
|
||||
/* struct for OBEX_TL_DATA_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
BT_HDR *p_buf;
|
||||
} data;
|
||||
} tOBEX_TL_MSG;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 psm; /* l2cap psm */
|
||||
UINT16 sec_mask; /* security mask */
|
||||
UINT16 pref_mtu; /* preferred mtu, limited by L2CAP_MTU_SIZE */
|
||||
BD_ADDR addr; /* peer bluetooth device address */
|
||||
} tOBEX_TL_L2CAP_SVR;
|
||||
|
||||
typedef union
|
||||
{
|
||||
tOBEX_TL_L2CAP_SVR l2cap;
|
||||
} tOBEX_TL_SVR_INFO;
|
||||
|
||||
typedef void (tOBEX_TL_CBACK)(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg);
|
||||
|
||||
typedef struct {
|
||||
void (*init)(tOBEX_TL_CBACK *callback);
|
||||
void (*deinit)(void);
|
||||
UINT16 (*connect)(tOBEX_TL_SVR_INFO *server);
|
||||
void (*disconnect)(UINT16 tl_hdl);
|
||||
UINT16 (*send)(UINT16 tl_hdl, BT_HDR *p_buf);
|
||||
UINT16 (*bind)(tOBEX_TL_SVR_INFO *server);
|
||||
void (*unbind)(UINT16 tl_hdl);
|
||||
} tOBEX_TL_OPS;
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "obex_tl.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
#define OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET 13 /* L2CAP_MIN_OFFSET */
|
||||
|
||||
tOBEX_TL_OPS *obex_tl_l2cap_ops_get(void);
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
764
components/bt/host/bluedroid/stack/obex/obex_api.c
Normal file
764
components/bt/host/bluedroid/stack/obex/obex_api.c
Normal file
@ -0,0 +1,764 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "osi/osi.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/obex_api.h"
|
||||
#include "obex_int.h"
|
||||
#include "obex_tl.h"
|
||||
#include "obex_tl_l2cap.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
static inline void obex_server_to_tl_server(tOBEX_SVR_INFO *server, tOBEX_TL_SVR_INFO *tl_server)
|
||||
{
|
||||
if (server->tl == OBEX_OVER_L2CAP) {
|
||||
tl_server->l2cap.psm = server->l2cap.psm;
|
||||
tl_server->l2cap.sec_mask = server->l2cap.sec_mask;
|
||||
tl_server->l2cap.pref_mtu = server->l2cap.pref_mtu;
|
||||
bdcpy(tl_server->l2cap.addr, server->l2cap.addr);
|
||||
}
|
||||
else {
|
||||
OBEX_TRACE_ERROR("Unsupported OBEX transport type\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void obex_updata_packet_length(BT_HDR *p_buf, UINT16 len)
|
||||
{
|
||||
UINT8 *p_pkt_len = (UINT8 *)(p_buf + 1) + p_buf->offset + 1;
|
||||
STORE16BE(p_pkt_len, len);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_Init
|
||||
**
|
||||
** Description Initialize OBEX Profile, must call before using any other
|
||||
** OBEX APIs
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_Init(void)
|
||||
{
|
||||
#if (OBEX_DYNAMIC_MEMORY)
|
||||
if (!obex_cb_ptr) {
|
||||
obex_cb_ptr = (tOBEX_CB *)osi_malloc(sizeof(tOBEX_CB));
|
||||
if (!obex_cb_ptr) {
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
|
||||
memset(&obex_cb, 0, sizeof(tOBEX_CB));
|
||||
obex_cb.tl_ops[OBEX_OVER_L2CAP] = obex_tl_l2cap_ops_get();
|
||||
if (obex_cb.tl_ops[OBEX_OVER_L2CAP]->init != NULL) {
|
||||
obex_cb.tl_ops[OBEX_OVER_L2CAP]->init(obex_tl_l2cap_callback);
|
||||
}
|
||||
/* Not implement yet */
|
||||
/*
|
||||
obex_cb.tl_ops[OBEX_OVER_RFCOMM] = obex_tl_rfcomm_ops_get();
|
||||
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init != NULL) {
|
||||
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init(obex_tl_rfcomm_callback);
|
||||
}
|
||||
*/
|
||||
obex_cb.trace_level = BT_TRACE_LEVEL_ERROR;
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_Deinit
|
||||
**
|
||||
** Description Deinit OBEX profile, once deinit, can not use any other
|
||||
** APIs until call OBEX_Init again
|
||||
**
|
||||
*******************************************************************************/
|
||||
void OBEX_Deinit(void)
|
||||
{
|
||||
if (obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit != NULL) {
|
||||
obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit();
|
||||
}
|
||||
/*
|
||||
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit != NULL) {
|
||||
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit();
|
||||
}
|
||||
*/
|
||||
#if (OBEX_DYNAMIC_MEMORY)
|
||||
if (obex_cb_ptr) {
|
||||
osi_free(obex_cb_ptr);
|
||||
obex_cb_ptr = NULL;
|
||||
}
|
||||
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_CreateConn
|
||||
**
|
||||
** Description Start the progress of creating an OBEX connection
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed, when the
|
||||
** connection is opened, an OBEX_CONNECT_EVT will come
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_CreateConn(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_handle)
|
||||
{
|
||||
UINT16 ret = OBEX_SUCCESS;
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
|
||||
do {
|
||||
if (server->tl >= OBEX_NUM_TL) {
|
||||
ret = OBEX_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb = obex_allocate_ccb();
|
||||
if (p_ccb == NULL) {
|
||||
ret = OBEX_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
tOBEX_TL_SVR_INFO tl_server = {0};
|
||||
obex_server_to_tl_server(server, &tl_server);
|
||||
p_ccb->tl = server->tl;
|
||||
p_ccb->tl_hdl = obex_cb.tl_ops[p_ccb->tl]->connect(&tl_server);
|
||||
if (p_ccb->tl_hdl == 0) {
|
||||
ret = OBEX_ERROR_TL;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb->callback = callback;
|
||||
p_ccb->role = OBEX_ROLE_CLIENT;
|
||||
p_ccb->state = OBEX_STATE_OPENING;
|
||||
*out_handle = p_ccb->allocated;
|
||||
} while (0);
|
||||
|
||||
if (ret != OBEX_SUCCESS && p_ccb != NULL) {
|
||||
obex_free_ccb(p_ccb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_RemoveConn
|
||||
**
|
||||
** Description Remove an OBEX connection
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_RemoveConn(UINT16 handle)
|
||||
{
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= OBEX_MAX_CONNECTION || !obex_cb.ccb[ccb_idx].allocated) {
|
||||
return OBEX_BAD_HANDLE;
|
||||
}
|
||||
|
||||
p_ccb = &obex_cb.ccb[ccb_idx];
|
||||
obex_cb.tl_ops[p_ccb->tl]->disconnect(p_ccb->tl_hdl);
|
||||
obex_free_ccb(p_ccb);
|
||||
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_RegisterServer
|
||||
**
|
||||
** Description Register an OBEX server and listen the incoming connection
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_RegisterServer(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_svr_handle)
|
||||
{
|
||||
UINT8 ret = OBEX_SUCCESS;
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
|
||||
do {
|
||||
if (server->tl >= OBEX_NUM_TL) {
|
||||
ret = OBEX_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
p_scb = obex_allocate_scb();
|
||||
if (p_scb == NULL) {
|
||||
ret = OBEX_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
tOBEX_TL_SVR_INFO tl_server = {0};
|
||||
obex_server_to_tl_server(server, &tl_server);
|
||||
p_scb->tl = server->tl;
|
||||
p_scb->tl_hdl = obex_cb.tl_ops[p_scb->tl]->bind(&tl_server);
|
||||
if (p_scb->tl_hdl == 0) {
|
||||
ret = OBEX_ERROR_TL;
|
||||
break;
|
||||
}
|
||||
p_scb->callback = callback;
|
||||
|
||||
if (out_svr_handle) {
|
||||
/* To avoid confuse with connection handle, left shift 8 bit */
|
||||
*out_svr_handle = p_scb->allocated << 8;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (ret != OBEX_SUCCESS && p_scb != NULL) {
|
||||
obex_free_scb(p_scb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_DeregisterServer
|
||||
**
|
||||
** Description Deregister an OBEX server, if there are still a connection
|
||||
** alive, the behavior depend on the implementation of transport
|
||||
** layer
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_DeregisterServer(UINT16 svr_handle)
|
||||
{
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
|
||||
UINT16 scb_idx = (svr_handle >> 8) - 1;
|
||||
if (scb_idx >= OBEX_MAX_SERVER || !obex_cb.scb[scb_idx].allocated) {
|
||||
return OBEX_BAD_HANDLE;
|
||||
}
|
||||
|
||||
p_scb = &obex_cb.scb[scb_idx];
|
||||
obex_cb.tl_ops[p_scb->tl]->unbind(p_scb->tl_hdl);
|
||||
obex_free_scb(p_scb);
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_SendPacket
|
||||
**
|
||||
** Description Send a packet to peer OBEX server or client, once call
|
||||
** this function, the ownership of pkt is lost, do not free
|
||||
** or modify the pkt any more
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_SendPacket(UINT16 handle, BT_HDR *pkt)
|
||||
{
|
||||
UINT16 ret = OBEX_SUCCESS;
|
||||
BOOLEAN free_pkt = true;
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
do {
|
||||
if (pkt == NULL) {
|
||||
ret = OBEX_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= OBEX_MAX_CONNECTION || !obex_cb.ccb[ccb_idx].allocated) {
|
||||
ret = OBEX_BAD_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb = &obex_cb.ccb[ccb_idx];
|
||||
if (p_ccb->state != OBEX_STATE_OPENED) {
|
||||
ret = OBEX_NOT_OPEN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pkt->len > p_ccb->tl_peer_mtu) {
|
||||
ret = OBEX_PACKET_TOO_LARGE;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = obex_cb.tl_ops[p_ccb->tl]->send(p_ccb->tl_hdl, pkt);
|
||||
/* packet has pass to lower layer, do not free it */
|
||||
free_pkt = false;
|
||||
if (ret == OBEX_TL_SUCCESS || ret == OBEX_TL_CONGESTED) {
|
||||
ret = OBEX_SUCCESS;
|
||||
}
|
||||
else {
|
||||
ret = OBEX_ERROR_TL;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (free_pkt) {
|
||||
osi_free(pkt);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_BuildRequest
|
||||
**
|
||||
** Description Build a request packet with opcode and additional info,
|
||||
** packet can free by osi_free
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt)
|
||||
{
|
||||
if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET;
|
||||
|
||||
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
|
||||
if (p_buf == NULL) {
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
|
||||
UINT16 pkt_len = OBEX_MIN_PACKET_SIZE;
|
||||
p_buf->offset = OBEX_BT_HDR_MIN_OFFSET;
|
||||
/* use layer_specific to store the max data length allowed */
|
||||
p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET;
|
||||
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
|
||||
/* byte 0: opcode */
|
||||
*p_data++ = info->opcode;
|
||||
/* byte 1, 2: packet length, skip, we will update at last */
|
||||
UINT8 *p_pkt_len = p_data;
|
||||
p_data += 2;
|
||||
|
||||
switch (info->opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
/* byte 3: OBEX version number */
|
||||
*p_data++ = info->obex_version_number;
|
||||
/* byte 4: flags */
|
||||
*p_data++ = info->flags;
|
||||
/* byte 5, 6: maximum OBEX packet length, recommend to set as our mtu*/
|
||||
STORE16BE(p_data, info->max_packet_length);
|
||||
pkt_len += 4;
|
||||
break;
|
||||
case OBEX_OPCODE_SETPATH:
|
||||
/* byte 3: flags */
|
||||
*p_data++ = info->flags;
|
||||
/* byte 4: constants, reserved, must be zero */
|
||||
*p_data++ = 0;
|
||||
pkt_len += 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
STORE16BE(p_pkt_len, pkt_len);
|
||||
p_buf->len = pkt_len;
|
||||
*out_pkt = p_buf;
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_BuildResponse
|
||||
**
|
||||
** Description Build a response packet with opcode and response and additional
|
||||
** info, packet can by free by osi_free
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt)
|
||||
{
|
||||
if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET;
|
||||
|
||||
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
|
||||
if (p_buf == NULL) {
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
|
||||
UINT16 pkt_len = OBEX_MIN_PACKET_SIZE;
|
||||
p_buf->offset = OBEX_BT_HDR_MIN_OFFSET;
|
||||
/* use layer_specific to store the max data length allowed */
|
||||
p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET;
|
||||
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
|
||||
/* byte 0: response code */
|
||||
*p_data++ = info->response_code;
|
||||
/* byte 1, 2: packet length, skip, we will update at last */
|
||||
UINT8 *p_pkt_len = p_data;
|
||||
p_data += 2;
|
||||
|
||||
/* we need to use opcode to decide the response format */
|
||||
switch (info->opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
/* byte 3: OBEX version number */
|
||||
*p_data++ = info->obex_version_number;
|
||||
/* byte 4: flags */
|
||||
*p_data++ = info->flags;
|
||||
/* byte 5, 6: maximum OBEX packet length, recommend to set as our mtu */
|
||||
STORE16BE(p_data, info->max_packet_length);
|
||||
pkt_len += 4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
STORE16BE(p_pkt_len, pkt_len);
|
||||
p_buf->len = pkt_len;
|
||||
*out_pkt = p_buf;
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeader
|
||||
**
|
||||
** Description Append a header to specific packet, packet can be request
|
||||
** or response, the format of header must be valid
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header)
|
||||
{
|
||||
if (pkt == NULL || header == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT16 header_len = 0;
|
||||
UINT8 header_id = *header;
|
||||
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
|
||||
{
|
||||
case OBEX_HEADER_ID_U2B_TYPE1:
|
||||
case OBEX_HEADER_ID_U2B_TYPE2:
|
||||
header_len = (header[1] << 8) + header[2];
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE3:
|
||||
header_len = 2;
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE4:
|
||||
header_len = 5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (header_len == 0) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (pkt->layer_specific - pkt->len < header_len) {
|
||||
/* the packet can not hold this header */
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
UINT8 *p_start = p_data + pkt->len;
|
||||
memcpy(p_start, header, header_len);
|
||||
pkt->len += header_len;
|
||||
/* point to packet len */
|
||||
p_data++;
|
||||
STORE16BE(p_data, pkt->len);
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeaderRaw
|
||||
**
|
||||
** Description Append a header to specific packet, packet can be request
|
||||
** or response, data not include 2 byte length prefixed
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UINT16 data_len)
|
||||
{
|
||||
if (pkt == NULL || data == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT16 header_len = 0;
|
||||
BOOLEAN store_header_len = FALSE;
|
||||
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
|
||||
{
|
||||
case OBEX_HEADER_ID_U2B_TYPE1:
|
||||
case OBEX_HEADER_ID_U2B_TYPE2:
|
||||
/* header id + 2 byte length prefixed + data */
|
||||
header_len = data_len + 3;
|
||||
store_header_len = TRUE;
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE3:
|
||||
header_len = 2;
|
||||
if (data_len != 1) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE4:
|
||||
header_len = 5;
|
||||
if (data_len != 4) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (header_len == 0) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (pkt->layer_specific - pkt->len < header_len) {
|
||||
/* the packet can not hold this header */
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
UINT8 *p_start = p_data + pkt->len;
|
||||
/* store header id */
|
||||
*p_start++ = header_id;
|
||||
if (store_header_len) {
|
||||
/* store header length */
|
||||
STORE16BE(p_start, header_len);
|
||||
p_start+= 2;
|
||||
}
|
||||
/* store data */
|
||||
memcpy(p_start, data, data_len);
|
||||
pkt->len += header_len;
|
||||
/* point to packet len */
|
||||
p_data++;
|
||||
STORE16BE(p_data, pkt->len);
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeaderSRM
|
||||
**
|
||||
** Description Append a Single Response Mode header
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeaderSRM(BT_HDR *pkt, UINT8 value)
|
||||
{
|
||||
return OBEX_AppendHeaderRaw(pkt, OBEX_HEADER_ID_SRM, &value, 1);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeaderSRMP
|
||||
**
|
||||
** Description Append a Single Response Mode Parameters header
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeaderSRMP(BT_HDR *pkt, UINT8 value)
|
||||
{
|
||||
return OBEX_AppendHeaderRaw(pkt, OBEX_HEADER_ID_SRM_PARAM, &value, 1);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetPacketFreeSpace
|
||||
**
|
||||
** Description Get the current free space of a packet, use this to check
|
||||
** if a packet can hold a header
|
||||
**
|
||||
** Returns Current free space of a packet, in bytes
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_GetPacketFreeSpace(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return pkt->layer_specific - pkt->len;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetPacketLength
|
||||
**
|
||||
** Description Get the current packet length
|
||||
**
|
||||
** Returns Current packet length, in bytes
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_GetPacketLength(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return pkt->len;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_ParseRequest
|
||||
**
|
||||
** Description Parse a request packet
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_ParseRequest(BT_HDR *pkt, tOBEX_PARSE_INFO *info)
|
||||
{
|
||||
if (pkt == NULL || info == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
info->opcode = *p_data;
|
||||
switch (info->opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
info->obex_version_number = p_data[3];
|
||||
info->flags = p_data[4];
|
||||
info->max_packet_length = (p_data[5] << 8) + p_data[6];
|
||||
info->next_header_pos = 7;
|
||||
break;
|
||||
case OBEX_OPCODE_SETPATH:
|
||||
info->flags = p_data[3];
|
||||
info->next_header_pos = 5;
|
||||
break;
|
||||
default:
|
||||
info->next_header_pos = 3;
|
||||
break;
|
||||
}
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_ParseResponse
|
||||
**
|
||||
** Description Parse a request response packet
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_ParseResponse(BT_HDR *pkt, UINT8 opcode, tOBEX_PARSE_INFO *info)
|
||||
{
|
||||
if (pkt == NULL || info == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
info->opcode = opcode;
|
||||
info->response_code = *p_data;
|
||||
switch (opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
info->obex_version_number = p_data[3];
|
||||
info->flags = p_data[4];
|
||||
info->max_packet_length = (p_data[5] << 8) + p_data[6];
|
||||
info->next_header_pos = 7;
|
||||
break;
|
||||
default:
|
||||
info->next_header_pos = 3;
|
||||
break;
|
||||
}
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_CheckFinalBit
|
||||
**
|
||||
** Description Check whether a packet had set the final bit
|
||||
**
|
||||
** Returns TRUE if final bit set, otherwise, false
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN OBEX_CheckFinalBit(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
return (*p_data) & OBEX_FINAL_BIT_MASK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_CheckContinueResponse
|
||||
**
|
||||
** Description Check whether a packet is continue response
|
||||
**
|
||||
** Returns TRUE if continue response, otherwise, false
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN OBEX_CheckContinueResponse(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
return (*p_data == 0x90) || (*p_data == 0x10);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetHeaderLength
|
||||
**
|
||||
** Description Get header length
|
||||
**
|
||||
** Returns header length
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_GetHeaderLength(UINT8 *header)
|
||||
{
|
||||
UINT16 header_len = 0;
|
||||
UINT8 header_id = *header;
|
||||
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
|
||||
{
|
||||
case OBEX_HEADER_ID_U2B_TYPE1:
|
||||
case OBEX_HEADER_ID_U2B_TYPE2:
|
||||
header_len = (header[1] << 8) + header[2];
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE3:
|
||||
header_len = 2;
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE4:
|
||||
header_len = 5;
|
||||
break;
|
||||
default:
|
||||
/* unreachable */
|
||||
break;
|
||||
}
|
||||
return header_len;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetNextHeader
|
||||
**
|
||||
** Description Get next header pointer from a packet
|
||||
**
|
||||
** Returns Pointer to start address of a header, NULL if no more header
|
||||
** or failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT8 *OBEX_GetNextHeader(BT_HDR *pkt, tOBEX_PARSE_INFO *info)
|
||||
{
|
||||
if (pkt == NULL || info == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
if (info->next_header_pos == 0 || info->next_header_pos >= pkt->len) {
|
||||
return NULL;
|
||||
}
|
||||
UINT8 *header = p_data + info->next_header_pos;
|
||||
UINT16 header_len = OBEX_GetHeaderLength(header);
|
||||
info->next_header_pos += header_len;
|
||||
return header;
|
||||
}
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
211
components/bt/host/bluedroid/stack/obex/obex_main.c
Normal file
211
components/bt/host/bluedroid/stack/obex/obex_main.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "osi/osi.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/obex_api.h"
|
||||
#include "obex_int.h"
|
||||
#include "obex_tl.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
#if OBEX_DYNAMIC_MEMORY == FALSE
|
||||
tOBEX_CB obex_cb;
|
||||
#else
|
||||
tOBEX_CB *obex_cb_ptr = NULL;
|
||||
#endif
|
||||
|
||||
static tOBEX_CCB *obex_find_ccb_by_tl_hdl(UINT8 tl, UINT16 tl_hdl)
|
||||
{
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_CONNECTION; ++i) {
|
||||
if (obex_cb.ccb[i].allocated && obex_cb.ccb[i].tl == tl && obex_cb.ccb[i].tl_hdl == tl_hdl) {
|
||||
p_ccb = &obex_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static tOBEX_SCB *obex_find_scb_by_tl_hdl(UINT8 tl, UINT16 tl_hdl)
|
||||
{
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_SERVER; ++i) {
|
||||
if (obex_cb.scb[i].allocated && obex_cb.scb[i].tl == tl && obex_cb.scb[i].tl_hdl == tl_hdl) {
|
||||
p_scb = &obex_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
tOBEX_CCB *obex_allocate_ccb(void)
|
||||
{
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_CONNECTION; ++i) {
|
||||
if (!obex_cb.ccb[i].allocated) {
|
||||
obex_cb.ccb[i].allocated = i + 1;
|
||||
p_ccb = &obex_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
void obex_free_ccb(tOBEX_CCB *p_ccb)
|
||||
{
|
||||
assert(p_ccb->allocated != 0);
|
||||
memset(p_ccb, 0, sizeof(tOBEX_CCB));
|
||||
}
|
||||
|
||||
tOBEX_SCB *obex_allocate_scb(void)
|
||||
{
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_SERVER; ++i) {
|
||||
if (!obex_cb.scb[i].allocated) {
|
||||
obex_cb.scb[i].allocated = i + 1;
|
||||
p_scb = &obex_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
void obex_free_scb(tOBEX_SCB *p_scb)
|
||||
{
|
||||
assert(p_scb->allocated != 0);
|
||||
memset(p_scb, 0, sizeof(tOBEX_SCB));
|
||||
}
|
||||
|
||||
/* check whether connection mtu is valid, if not, disconnect it */
|
||||
static bool check_conn_mtu_valid(tOBEX_CCB *p_ccb, BOOLEAN call_cb)
|
||||
{
|
||||
if (p_ccb->tl_our_mtu < 255 || p_ccb->tl_peer_mtu < 255) {
|
||||
if (call_cb && p_ccb->callback) {
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_DISCONNECT_EVT, NULL);
|
||||
}
|
||||
OBEX_TRACE_ERROR("Check OBEX transport layer MTU failed, disconnect");
|
||||
obex_cb.tl_ops[p_ccb->tl]->disconnect(p_ccb->tl_hdl);
|
||||
obex_free_ccb(p_ccb);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void obex_tl_evt_handler(UINT8 tl, tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg)
|
||||
{
|
||||
UINT16 tl_hdl = msg->any.hdl;
|
||||
tOBEX_CCB *p_ccb = obex_find_ccb_by_tl_hdl(tl, tl_hdl);
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
tOBEX_MSG cb_msg = {0};
|
||||
|
||||
switch (evt)
|
||||
{
|
||||
case OBEX_TL_CONN_OPEN_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
p_ccb->tl_peer_mtu = msg->conn_open.peer_mtu;
|
||||
p_ccb->tl_our_mtu = msg->conn_open.our_mtu;
|
||||
if (!check_conn_mtu_valid(p_ccb, TRUE)) {
|
||||
break;
|
||||
}
|
||||
p_ccb->state = OBEX_STATE_OPENED;
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.connect.peer_mtu = msg->conn_open.peer_mtu;
|
||||
cb_msg.connect.our_mtu = msg->conn_open.our_mtu;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_CONNECT_EVT, &cb_msg);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_DIS_CONN_EVT:
|
||||
if (p_ccb != NULL) {
|
||||
if (p_ccb->callback) {
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_DISCONNECT_EVT, NULL);
|
||||
}
|
||||
obex_free_ccb(p_ccb);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_CONGEST_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
if (p_ccb->callback) {
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_CONGEST_EVT, NULL);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_UNCONGEST_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
if (p_ccb->callback) {
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_UNCONGEST_EVT, NULL);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_MTU_CHANGE_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
p_ccb->tl_peer_mtu = msg->mtu_chg.peer_mtu;
|
||||
p_ccb->tl_our_mtu = msg->mtu_chg.our_mtu;
|
||||
if (!check_conn_mtu_valid(p_ccb, TRUE)) {
|
||||
break;
|
||||
}
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.mtu_change.peer_mtu = msg->mtu_chg.peer_mtu;
|
||||
cb_msg.mtu_change.our_mtu = msg->mtu_chg.our_mtu;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_MTU_CHANGE_EVT, &cb_msg);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_DATA_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.data.pkt = msg->data.p_buf;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_DATA_EVT, &cb_msg);
|
||||
}
|
||||
else {
|
||||
/* No callback, just free the packet */
|
||||
osi_free(msg->data.p_buf);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_CONN_INCOME_EVT:
|
||||
/* New connection, p_ccb should be NULL */
|
||||
assert(p_ccb == NULL);
|
||||
p_scb = obex_find_scb_by_tl_hdl(tl, msg->conn_income.svr_hdl);
|
||||
if (p_scb == NULL) {
|
||||
obex_cb.tl_ops[tl]->disconnect(tl_hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((p_ccb = obex_allocate_ccb()) == NULL) {
|
||||
obex_cb.tl_ops[tl]->disconnect(tl_hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb->tl = tl;
|
||||
p_ccb->tl_hdl = tl_hdl;
|
||||
p_ccb->role = OBEX_ROLE_SERVER;
|
||||
p_ccb->tl_peer_mtu = msg->conn_income.peer_mtu;
|
||||
p_ccb->tl_our_mtu = msg->conn_income.our_mtu;
|
||||
if (!check_conn_mtu_valid(p_ccb, FALSE)) {
|
||||
break;
|
||||
}
|
||||
p_ccb->state = OBEX_STATE_OPENED;
|
||||
p_ccb->callback = p_scb->callback;
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.conn_income.svr_handle = p_scb->allocated << 8;
|
||||
cb_msg.conn_income.peer_mtu = msg->conn_income.peer_mtu;
|
||||
cb_msg.conn_income.our_mtu = msg->conn_income.our_mtu;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_CONN_INCOME_EVT, &cb_msg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OBEX_TRACE_ERROR("Unknown OBEX transport event: 0x%x\n", evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void obex_tl_l2cap_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg)
|
||||
{
|
||||
obex_tl_evt_handler(OBEX_OVER_L2CAP, evt, msg);
|
||||
}
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
807
components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c
Normal file
807
components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c
Normal file
@ -0,0 +1,807 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "osi/osi.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#include "stack/l2c_api.h"
|
||||
#include "stack/l2cdefs.h"
|
||||
#include "stack/btm_api.h"
|
||||
#include "btm_int.h"
|
||||
#include "obex_tl.h"
|
||||
#include "obex_tl_l2cap.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
#define OBEX_TL_L2CAP_NUM_CONN 4
|
||||
#define OBEX_TL_L2CAP_NUM_SERVER 2
|
||||
|
||||
#define OBEX_TL_L2CAP_STATUS_FLAG_CFG_DONE 0x01
|
||||
#define OBEX_TL_L2CAP_STATUS_FLAG_PEER_CFG_DONE 0x02
|
||||
#define OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED 0x80
|
||||
|
||||
/* ERTM Tx window size */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_TX_WINDOW_SIZE 10
|
||||
|
||||
/* ERTM Maximum transmissions before disconnecting */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_MAX_TX_B4_DISCNT 20
|
||||
|
||||
/* ERTM Retransmission timeout (2 secs) */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_RETX_TOUT 2000
|
||||
|
||||
/* ERTM Monitor timeout (12 secs) */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_MONITOR_TOUT 12000
|
||||
|
||||
/* ERTM ERTM MPS segment size */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_MAX_PDU_SIZE 1010
|
||||
|
||||
typedef struct {
|
||||
UINT16 vpsm; /* psm in our side */
|
||||
UINT16 lcid; /* local channel id */
|
||||
UINT16 peer_mtu; /* MTU that peer device set */
|
||||
UINT16 our_mtu; /* MTU that we set to peer device */
|
||||
BOOLEAN initiator; /* TRUE if is initiator, otherwise FALSE */
|
||||
UINT8 id; /* remote channel id */
|
||||
UINT8 status_flag; /* status flag used in config procedure */
|
||||
UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, equal to handle */
|
||||
BD_ADDR addr; /* peer bluetooth device address */
|
||||
} tOBEX_TL_L2CAP_CCB;
|
||||
|
||||
typedef struct {
|
||||
UINT16 psm; /* psm in our side */
|
||||
UINT16 pref_mtu; /* preferred mtu */
|
||||
UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, handle of server will left shift 8 bits */
|
||||
} tOBEX_TL_L2CAP_SCB;
|
||||
|
||||
typedef struct {
|
||||
tOBEX_TL_CBACK *callback; /* Upper layer callback */
|
||||
tOBEX_TL_L2CAP_CCB ccb[OBEX_TL_L2CAP_NUM_CONN];
|
||||
tOBEX_TL_L2CAP_SCB scb[OBEX_TL_L2CAP_NUM_SERVER];
|
||||
tL2CAP_APPL_INFO l2cap_reg_info; /* Default L2CAP Registration info */
|
||||
UINT8 trace_level; /* trace level */
|
||||
} tOBEX_TL_L2CAP_CB;
|
||||
|
||||
#if OBEX_DYNAMIC_MEMORY == FALSE
|
||||
static tOBEX_TL_L2CAP_CB obex_tl_l2cap_cb;
|
||||
#else
|
||||
static tOBEX_TL_L2CAP_CB *obex_tl_l2cap_cb_ptr = NULL;
|
||||
#define obex_tl_l2cap_cb (*obex_tl_l2cap_cb_ptr)
|
||||
#endif
|
||||
|
||||
static tL2CAP_ERTM_INFO obex_tl_l2cap_etm_opts =
|
||||
{
|
||||
L2CAP_FCR_ERTM_MODE,
|
||||
L2CAP_FCR_CHAN_OPT_ERTM | L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */
|
||||
L2CAP_USER_RX_BUF_SIZE,
|
||||
L2CAP_USER_TX_BUF_SIZE,
|
||||
L2CAP_FCR_RX_BUF_SIZE,
|
||||
L2CAP_FCR_TX_BUF_SIZE
|
||||
};
|
||||
|
||||
static tL2CAP_FCR_OPTS obex_tl_l2cap_fcr_opts =
|
||||
{
|
||||
L2CAP_FCR_ERTM_MODE,
|
||||
OBEX_TL_L2CAP_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */
|
||||
OBEX_TL_L2CAP_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */
|
||||
OBEX_TL_L2CAP_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */
|
||||
OBEX_TL_L2CAP_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
|
||||
OBEX_TL_L2CAP_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */
|
||||
};
|
||||
|
||||
static tOBEX_TL_L2CAP_CCB *allocate_ccb(void)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = NULL;
|
||||
for(int i = 0; i < OBEX_TL_L2CAP_NUM_CONN; ++i) {
|
||||
if (obex_tl_l2cap_cb.ccb[i].allocated == 0) {
|
||||
obex_tl_l2cap_cb.ccb[i].allocated = i + 1;
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static void free_ccb(tOBEX_TL_L2CAP_CCB *p_ccb)
|
||||
{
|
||||
memset(p_ccb, 0, sizeof(tOBEX_TL_L2CAP_CCB));
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_CCB *find_ccb_by_lcid(UINT16 lcid)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = NULL;
|
||||
for (int i = 0; i < OBEX_TL_L2CAP_NUM_CONN; ++i) {
|
||||
if (obex_tl_l2cap_cb.ccb[i].allocated && obex_tl_l2cap_cb.ccb[i].lcid == lcid) {
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_CCB *find_ccb_by_hdl(UINT16 hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = NULL;
|
||||
if (hdl > 0 && hdl <= OBEX_TL_L2CAP_NUM_CONN) {
|
||||
if (obex_tl_l2cap_cb.ccb[hdl-1].allocated == hdl) {
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[hdl-1];
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_CCB *find_ccb_by_psm(UINT16 psm)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = NULL;
|
||||
for(int i = 0; i < OBEX_TL_L2CAP_NUM_CONN; ++i) {
|
||||
if (obex_tl_l2cap_cb.ccb[i].allocated && obex_tl_l2cap_cb.ccb[i].vpsm == psm) {
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_SCB *allocate_scb(void)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = NULL;
|
||||
for(int i = 0; i < OBEX_TL_L2CAP_NUM_SERVER; ++i) {
|
||||
if (obex_tl_l2cap_cb.scb[i].allocated == 0) {
|
||||
obex_tl_l2cap_cb.scb[i].allocated = i + 1;
|
||||
p_scb = &obex_tl_l2cap_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_SCB *find_scb_by_psm(UINT16 psm)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = NULL;
|
||||
for(int i = 0; i < OBEX_TL_L2CAP_NUM_SERVER; ++i) {
|
||||
if (obex_tl_l2cap_cb.scb[i].allocated && obex_tl_l2cap_cb.scb[i].psm == psm) {
|
||||
p_scb = &obex_tl_l2cap_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
static void free_scb(tOBEX_TL_L2CAP_SCB *p_scb)
|
||||
{
|
||||
memset(p_scb, 0, sizeof(tOBEX_TL_L2CAP_SCB));
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_SCB *find_scb_by_hdl(UINT16 hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = NULL;
|
||||
hdl = hdl >> 8;
|
||||
if (hdl > 0 && hdl <= OBEX_TL_L2CAP_NUM_SERVER) {
|
||||
if (obex_tl_l2cap_cb.scb[hdl-1].allocated == hdl) {
|
||||
p_scb = &obex_tl_l2cap_cb.scb[hdl-1];
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function obex_tl_l2cap_sec_check_complete_term
|
||||
*
|
||||
* Description OBEX over L2CAP security check complete callback function
|
||||
* (terminated).
|
||||
*
|
||||
******************************************************************************/
|
||||
static void l2cap_sec_check_complete_term(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = (tOBEX_TL_L2CAP_CCB *)p_ref_data;
|
||||
|
||||
if (res == BTM_SUCCESS) {
|
||||
L2CA_ErtmConnectRsp(p_ccb->addr, p_ccb->id, p_ccb->lcid, L2CAP_CONN_OK, 0, &obex_tl_l2cap_etm_opts);
|
||||
tL2CAP_CFG_INFO cfg = {0};
|
||||
cfg.mtu_present = TRUE;
|
||||
cfg.mtu = p_ccb->our_mtu;
|
||||
cfg.fcr_present = TRUE;
|
||||
cfg.fcr = obex_tl_l2cap_fcr_opts;
|
||||
L2CA_ConfigReq(p_ccb->lcid, &cfg);
|
||||
}
|
||||
else{
|
||||
L2CA_ErtmConnectRsp(p_ccb->addr, p_ccb->id, p_ccb->lcid, L2CAP_CONN_SECURITY_BLOCK, 0, &obex_tl_l2cap_etm_opts);
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
|
||||
free_ccb(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function obex_tl_l2cap_sec_check_complete_orig
|
||||
*
|
||||
* Description OBEX over L2CAP security check complete callback function
|
||||
* (originated).
|
||||
*
|
||||
******************************************************************************/
|
||||
static void l2cap_sec_check_complete_orig(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = (tOBEX_TL_L2CAP_CCB *)p_ref_data;
|
||||
|
||||
if (res == BTM_SUCCESS) {
|
||||
tL2CAP_CFG_INFO cfg = {0};
|
||||
cfg.mtu_present = TRUE;
|
||||
cfg.mtu = p_ccb->our_mtu;
|
||||
cfg.fcr_present = TRUE;
|
||||
cfg.fcr = obex_tl_l2cap_fcr_opts;
|
||||
L2CA_ConfigReq(p_ccb->lcid, &cfg);
|
||||
} else {
|
||||
L2CA_DisconnectReq(p_ccb->lcid);
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
|
||||
free_ccb(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_connect_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_ConnectInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_connect_ind(BD_ADDR addr, UINT16 lcid, UINT16 psm, UINT8 id)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(psm);
|
||||
if (p_scb == NULL) {
|
||||
/* An incoming connection, but no corresponding server found, reject */
|
||||
L2CA_ErtmConnectRsp (addr, id, lcid, L2CAP_CONN_NO_PSM, 0, &obex_tl_l2cap_etm_opts);
|
||||
OBEX_TL_L2CAP_TRACE_WARNING("No corresponding server found, reject incoming connection\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = allocate_ccb();
|
||||
if (p_ccb == NULL) {
|
||||
/* No resource, can not allocate ccb, reject */
|
||||
L2CA_ErtmConnectRsp (addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0, &obex_tl_l2cap_etm_opts);
|
||||
OBEX_TL_L2CAP_TRACE_WARNING("Can not allocate a ccb for new connection\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bdcpy(p_ccb->addr, addr);
|
||||
p_ccb->initiator = FALSE;
|
||||
p_ccb->lcid = lcid;
|
||||
p_ccb->id = id;
|
||||
p_ccb->vpsm = psm;
|
||||
p_ccb->our_mtu = p_scb->pref_mtu;
|
||||
if (btm_sec_mx_access_request(p_ccb->addr, p_ccb->vpsm,
|
||||
FALSE, BTM_SEC_PROTO_OBEX,
|
||||
OBEX_TL_L2CAP_NUM_CONN + p_scb->allocated - 1,
|
||||
&l2cap_sec_check_complete_term, p_ccb) == BTM_CMD_STARTED) {
|
||||
L2CA_ErtmConnectRsp(addr, id, lcid, L2CAP_CONN_PENDING, 0, &obex_tl_l2cap_etm_opts);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_connect_cfm
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** ConnectCfm received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_connect_cfm(UINT16 lcid, UINT16 result)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_connect_cfm but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == L2CAP_CONN_OK) {
|
||||
btm_sec_mx_access_request(p_ccb->addr, p_ccb->vpsm,
|
||||
TRUE, BTM_SEC_PROTO_OBEX,
|
||||
p_ccb->allocated - 1,
|
||||
&l2cap_sec_check_complete_orig, p_ccb);
|
||||
} else {
|
||||
OBEX_TL_L2CAP_TRACE_WARNING("l2cap_connect_cfm result != L2CAP_CONN_OK: result: 0x%x\n", result);
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
|
||||
free_ccb(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_config_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_ConfigInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_config_ind(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_ind but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(p_ccb->vpsm);
|
||||
if (p_ccb->initiator == FALSE && p_scb == NULL) {
|
||||
/* not a initiator, but can not find corresponding server */
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_ind, not a initiator, but can not find corresponding server\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* save peer mtu if present */
|
||||
UINT16 peer_mtu = L2CAP_DEFAULT_MTU;
|
||||
if (p_cfg->mtu_present) {
|
||||
peer_mtu = p_cfg->mtu;
|
||||
}
|
||||
|
||||
p_cfg->mtu_present = FALSE;
|
||||
p_cfg->flush_to_present = FALSE;
|
||||
p_cfg->qos_present = FALSE;
|
||||
p_cfg->result = L2CAP_CFG_OK;
|
||||
L2CA_ConfigRsp(p_ccb->lcid, p_cfg);
|
||||
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED) {
|
||||
/* reconfig l2cap channel */
|
||||
if (p_ccb->peer_mtu != peer_mtu) {
|
||||
/* only report to upper if mtu change */
|
||||
p_ccb->peer_mtu = peer_mtu;
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.mtu_chg.hdl = p_ccb->allocated;
|
||||
msg.mtu_chg.peer_mtu = peer_mtu;
|
||||
msg.mtu_chg.our_mtu = p_ccb->our_mtu;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_MTU_CHANGE_EVT, &msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
p_ccb->peer_mtu = peer_mtu;
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_PEER_CFG_DONE;
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CFG_DONE) {
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED;
|
||||
}
|
||||
|
||||
if ((p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED)) {
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
if (p_ccb->initiator) {
|
||||
msg.conn_open.hdl = p_ccb->allocated;
|
||||
msg.conn_open.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_open.our_mtu = p_ccb->our_mtu;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_OPEN_EVT, &msg);
|
||||
}
|
||||
else {
|
||||
msg.conn_income.hdl = p_ccb->allocated;
|
||||
msg.conn_income.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_income.our_mtu = p_ccb->our_mtu;
|
||||
msg.conn_income.svr_hdl = p_scb->allocated << 8;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_INCOME_EVT, &msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_config_cfm
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_ConfigCnf received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_config_cfm(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_cfm but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(p_ccb->vpsm);
|
||||
if (p_ccb->initiator == FALSE && p_scb == NULL) {
|
||||
/* not a initiator, but can not find corresponding server */
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_cfm, not a initiator, but can not find corresponding server\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_cfg->result != L2CAP_CFG_OK) {
|
||||
OBEX_TL_L2CAP_TRACE_WARNING("l2cap_config_cfm result != L2CAP_CFG_OK: result: 0x%x\n", p_cfg->result);
|
||||
L2CA_DisconnectReq(p_ccb->lcid);
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
|
||||
free_ccb(p_ccb);
|
||||
return;
|
||||
}
|
||||
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_CFG_DONE;
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_PEER_CFG_DONE) {
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED;
|
||||
}
|
||||
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED) {
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
if (p_ccb->initiator) {
|
||||
msg.conn_open.hdl = p_ccb->allocated;
|
||||
msg.conn_open.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_open.our_mtu = p_ccb->our_mtu;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_OPEN_EVT, &msg);
|
||||
}
|
||||
else {
|
||||
msg.conn_income.hdl = p_ccb->allocated;
|
||||
msg.conn_income.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_income.our_mtu = p_ccb->our_mtu;
|
||||
msg.conn_income.svr_hdl = p_scb->allocated << 8;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_INCOME_EVT, &msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_qos_violation_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_QoSViolationIndInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_qos_violation_ind(BD_ADDR addr)
|
||||
{
|
||||
UNUSED(addr);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_disconnect_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_DisconnectInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_disconnect_ind(UINT16 lcid, BOOLEAN is_conf_needed)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
|
||||
if (is_conf_needed) {
|
||||
L2CA_DisconnectRsp(lcid);
|
||||
}
|
||||
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_disconnect_ind but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
|
||||
free_ccb(p_ccb);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_buf_data_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** data frame is received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_buf_data_ind(UINT16 lcid, BT_HDR *p_buf)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_buf_data_ind but no ccb found\n");
|
||||
osi_free(p_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.data.hdl = p_ccb->allocated;
|
||||
msg.data.p_buf = p_buf;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DATA_EVT, &msg);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_congestion_status_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CAP channel congestion status changes
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_congestion_status_ind(UINT16 lcid, BOOLEAN is_congested)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_congestion_status_ind but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
if (is_congested) {
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONGEST_EVT, &msg);
|
||||
}
|
||||
else {
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_UNCONGEST_EVT, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_init
|
||||
**
|
||||
** Description Initialize OBEX over L2CAP transport layer, callback
|
||||
** can not be NULL, must be called once before using any
|
||||
** other APIs
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_init(tOBEX_TL_CBACK callback)
|
||||
{
|
||||
assert(callback != NULL);
|
||||
#if (OBEX_DYNAMIC_MEMORY)
|
||||
if (!obex_tl_l2cap_cb_ptr) {
|
||||
obex_tl_l2cap_cb_ptr = (tOBEX_TL_L2CAP_CB *)osi_malloc(sizeof(tOBEX_TL_L2CAP_CB));
|
||||
if (!obex_tl_l2cap_cb_ptr) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("OBEX over L2CAP transport layer initialize failed, no memory\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
|
||||
memset(&obex_tl_l2cap_cb, 0, sizeof(tOBEX_TL_L2CAP_CB));
|
||||
obex_tl_l2cap_cb.callback = callback;
|
||||
obex_tl_l2cap_cb.trace_level = BT_TRACE_LEVEL_ERROR;
|
||||
|
||||
tL2CAP_APPL_INFO *p_reg_info = &obex_tl_l2cap_cb.l2cap_reg_info;
|
||||
|
||||
p_reg_info->pL2CA_ConnectInd_Cb = NULL; /* obex_tl_l2cap_connect_ind or NULL, depend on server or not */
|
||||
p_reg_info->pL2CA_ConnectCfm_Cb = obex_tl_l2cap_connect_cfm;
|
||||
p_reg_info->pL2CA_ConnectPnd_Cb = NULL;
|
||||
p_reg_info->pL2CA_ConfigInd_Cb = obex_tl_l2cap_config_ind;
|
||||
p_reg_info->pL2CA_ConfigCfm_Cb = obex_tl_l2cap_config_cfm;
|
||||
p_reg_info->pL2CA_DisconnectInd_Cb = obex_tl_l2cap_disconnect_ind;
|
||||
p_reg_info->pL2CA_DisconnectCfm_Cb = NULL;
|
||||
p_reg_info->pL2CA_QoSViolationInd_Cb = obex_tl_l2cap_qos_violation_ind;
|
||||
p_reg_info->pL2CA_DataInd_Cb = obex_tl_l2cap_buf_data_ind;
|
||||
p_reg_info->pL2CA_CongestionStatus_Cb = obex_tl_l2cap_congestion_status_ind;
|
||||
p_reg_info->pL2CA_TxComplete_Cb = NULL;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_init
|
||||
**
|
||||
** Description Deinitialize OBEX over L2CAP transport layer
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_deinit(void)
|
||||
{
|
||||
#if (OBEX_DYNAMIC_MEMORY)
|
||||
if (obex_tl_l2cap_cb_ptr) {
|
||||
osi_free(obex_tl_l2cap_cb_ptr);
|
||||
obex_tl_l2cap_cb_ptr = NULL;
|
||||
}
|
||||
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_connect
|
||||
**
|
||||
** Description Start the process of establishing a L2CAP connection
|
||||
**
|
||||
** Returns Non-zeros handle, 0 while failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 obex_tl_l2cap_connect(tOBEX_TL_SVR_INFO *server)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = allocate_ccb();
|
||||
if (p_ccb == NULL) {
|
||||
/* can not allocate a ccb */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (find_scb_by_psm(server->l2cap.psm) == NULL) {
|
||||
/* if the psm not register by a server, we register it as outgoing only record */
|
||||
tL2CAP_APPL_INFO *p_reg_info = &obex_tl_l2cap_cb.l2cap_reg_info;
|
||||
p_reg_info->pL2CA_ConnectInd_Cb = NULL;
|
||||
p_ccb->vpsm = L2CA_Register(server->l2cap.psm, p_reg_info);
|
||||
if (p_ccb->vpsm == 0) {
|
||||
free_ccb(p_ccb);
|
||||
return 0;
|
||||
}
|
||||
/* set security level */
|
||||
BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_OBEX, server->l2cap.sec_mask, p_ccb->vpsm, BTM_SEC_PROTO_OBEX, p_ccb->allocated - 1);
|
||||
}
|
||||
else {
|
||||
p_ccb->vpsm = server->l2cap.psm;
|
||||
}
|
||||
|
||||
if (server->l2cap.pref_mtu == 0 || server->l2cap.pref_mtu > L2CAP_MTU_SIZE) {
|
||||
p_ccb->our_mtu = L2CAP_MTU_SIZE;
|
||||
}
|
||||
else {
|
||||
p_ccb->our_mtu = server->l2cap.pref_mtu;
|
||||
}
|
||||
p_ccb->initiator = TRUE;
|
||||
p_ccb->lcid = L2CA_ErtmConnectReq(p_ccb->vpsm, server->l2cap.addr, &obex_tl_l2cap_etm_opts);
|
||||
if (p_ccb->lcid == 0) {
|
||||
free_ccb(p_ccb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return p_ccb->allocated;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_disconnect
|
||||
**
|
||||
** Description Disconnect a L2CAP connection
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_disconnect(UINT16 hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_hdl(hdl);
|
||||
if (p_ccb != NULL) {
|
||||
L2CA_DisconnectReq(p_ccb->lcid);
|
||||
if (p_ccb->initiator && find_scb_by_psm(p_ccb->vpsm) == NULL) {
|
||||
L2CA_Deregister(p_ccb->vpsm);
|
||||
}
|
||||
free_ccb(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_send_data
|
||||
**
|
||||
** Description Start the process of establishing a L2CAP connection
|
||||
**
|
||||
** Returns OBEX_TL_SUCCESS, if data accepted
|
||||
** OBEX_TL_CONGESTED, if data accepted and the channel is congested
|
||||
** OBEX_TL_FAILED, if error
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 obex_tl_l2cap_send_data(UINT16 hdl, BT_HDR *p_buf)
|
||||
{
|
||||
UINT16 ret = OBEX_TL_FAILED;
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_hdl(hdl);
|
||||
if (p_ccb == NULL) {
|
||||
osi_free(p_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Can not send data size larger than peer MTU */
|
||||
/* Offset should not smaller than L2CAP_MIN_OFFSET */
|
||||
if (p_buf->len > p_ccb->peer_mtu || p_buf->offset < L2CAP_MIN_OFFSET) {
|
||||
osi_free(p_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
UINT16 status = L2CA_DataWrite(p_ccb->lcid, p_buf);
|
||||
switch (status)
|
||||
{
|
||||
case L2CAP_DW_SUCCESS:
|
||||
ret = OBEX_TL_SUCCESS;
|
||||
break;
|
||||
case L2CAP_DW_CONGESTED:
|
||||
ret = OBEX_TL_CONGESTED;
|
||||
break;
|
||||
default:
|
||||
ret = OBEX_TL_FAILED;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_bind
|
||||
**
|
||||
** Description Register a server in L2CAP module
|
||||
**
|
||||
** Returns Non-zeros handle, 0 while failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 obex_tl_l2cap_bind(tOBEX_TL_SVR_INFO *server)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(server->l2cap.psm);
|
||||
|
||||
if (p_scb != NULL) {
|
||||
/* psm already used */
|
||||
return 0;
|
||||
}
|
||||
|
||||
p_scb = allocate_scb();
|
||||
if (p_scb == NULL) {
|
||||
/* can not allocate a new scb */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (server->l2cap.pref_mtu == 0 || server->l2cap.pref_mtu > L2CAP_MTU_SIZE) {
|
||||
p_scb->pref_mtu= L2CAP_MTU_SIZE;
|
||||
}
|
||||
else {
|
||||
p_scb->pref_mtu = server->l2cap.pref_mtu;
|
||||
}
|
||||
|
||||
tL2CAP_APPL_INFO *p_reg_info = &obex_tl_l2cap_cb.l2cap_reg_info;
|
||||
p_reg_info->pL2CA_ConnectInd_Cb = obex_tl_l2cap_connect_ind;
|
||||
/* Register a l2cap server, in this case, L2CA_Register always return the same psm as input */
|
||||
p_scb->psm = L2CA_Register(server->l2cap.psm, p_reg_info);
|
||||
if (p_scb->psm == 0) {
|
||||
free_scb(p_scb);
|
||||
return 0;
|
||||
}
|
||||
/* set security level */
|
||||
BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_OBEX, server->l2cap.sec_mask, p_scb->psm, BTM_SEC_PROTO_OBEX, OBEX_TL_L2CAP_NUM_CONN + p_scb->allocated - 1);
|
||||
BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_OBEX, server->l2cap.sec_mask, p_scb->psm, BTM_SEC_PROTO_OBEX, OBEX_TL_L2CAP_NUM_CONN + p_scb->allocated - 1);
|
||||
/* left shift 8 bits to avoid confuse with connection handle */
|
||||
return p_scb->allocated << 8;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_unbind
|
||||
**
|
||||
** Description Deregister a server in L2CAP module
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_unbind(UINT16 tl_hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_hdl(tl_hdl);
|
||||
if (p_scb) {
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = NULL;
|
||||
while ((p_ccb = find_ccb_by_psm(p_scb->psm)) != NULL) {
|
||||
L2CA_DisconnectReq(p_ccb->lcid);
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
|
||||
free_ccb(p_ccb);
|
||||
}
|
||||
L2CA_Deregister(p_scb->psm);
|
||||
free_scb(p_scb);
|
||||
}
|
||||
}
|
||||
|
||||
static tOBEX_TL_OPS obex_tl_l2cap_ops = {
|
||||
.init = obex_tl_l2cap_init,
|
||||
.deinit = obex_tl_l2cap_deinit,
|
||||
.connect = obex_tl_l2cap_connect,
|
||||
.disconnect = obex_tl_l2cap_disconnect,
|
||||
.bind = obex_tl_l2cap_bind,
|
||||
.unbind = obex_tl_l2cap_unbind,
|
||||
.send = obex_tl_l2cap_send_data
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_ops_get
|
||||
**
|
||||
** Description Get the operation function structure pointer of OBEX over
|
||||
** L2CAP transport layer
|
||||
**
|
||||
** Returns Pointer to operation function structure
|
||||
**
|
||||
*******************************************************************************/
|
||||
tOBEX_TL_OPS *obex_tl_l2cap_ops_get(void)
|
||||
{
|
||||
return &obex_tl_l2cap_ops;
|
||||
}
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
@ -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);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
@ -36,7 +36,7 @@ idf.py menuconfig
|
||||
|
||||
* Choose external I2S codec or internal DAC for audio output, and configure the output PINs under A2DP Example Configuration
|
||||
|
||||
* Enable Classic Bluetooth and A2DP under **Component config --> Bluetooth --> Bluedroid Enable**
|
||||
* For AVRCP CT Cover Art feature, is enabled by default, we can disable it by unselecting menuconfig option `Component config --> Bluetooth --> Bluedroid Options --> Classic Bluetooth --> AVRCP Features --> AVRCP CT Cover Art`. This example will try to use AVRCP CT Cover Art feature, get cover art image and count the image size if peer device support, this can be disable in `A2DP Example Configuration --> Use AVRCP CT Cover Art Feature`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
@ -66,7 +66,13 @@ I (122697) BT_AV: Audio packet count 100
|
||||
I (124697) BT_AV: Audio packet count 200
|
||||
I (126697) BT_AV: Audio packet count 300
|
||||
I (128697) BT_AV: Audio packet count 400
|
||||
```
|
||||
|
||||
The output when receiving a cover art image:
|
||||
|
||||
```
|
||||
I (53349) RC_CT: AVRC metadata rsp: attribute id 0x80, 1000748
|
||||
I (53639) RC_CT: Cover Art Client final data event, image size: 14118 bytes
|
||||
```
|
||||
|
||||
Also, the sound will be heard if a loudspeaker is connected and possible external I2S codec is correctly configured. For ESP32 A2DP source example, the sound is noise as the audio source generates the samples with a random sequence.
|
||||
|
@ -53,4 +53,12 @@ menu "A2DP Example Configuration"
|
||||
default "ESP_SPEAKER"
|
||||
help
|
||||
Use this option to set local device name.
|
||||
|
||||
config EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
bool "Use AVRCP CT Cover Art Feature"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
default y
|
||||
help
|
||||
This enables the AVRCP Cover Art feature in example and try to get cover art image from peer device.
|
||||
|
||||
endmenu
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -93,6 +93,13 @@ i2s_chan_handle_t tx_chan = NULL;
|
||||
dac_continuous_handle_t tx_chan;
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
static bool cover_art_connected = false;
|
||||
static bool cover_art_getting = false;
|
||||
static uint32_t cover_art_image_size = 0;
|
||||
static uint8_t image_handle_old[7];
|
||||
#endif
|
||||
|
||||
/********************************
|
||||
* STATIC FUNCTION DEFINITIONS
|
||||
*******************************/
|
||||
@ -107,6 +114,18 @@ static void bt_app_alloc_meta_buffer(esp_avrc_ct_cb_param_t *param)
|
||||
rc->meta_rsp.attr_text = attr_text;
|
||||
}
|
||||
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
static bool image_handle_check(uint8_t *image_handle, int len)
|
||||
{
|
||||
/* Image handle length must be 7 */
|
||||
if (len == 7 && memcmp(image_handle_old, image_handle, 7) != 0) {
|
||||
memcpy(image_handle_old, image_handle, 7);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bt_av_new_track(void)
|
||||
{
|
||||
/* request metadata */
|
||||
@ -114,6 +133,11 @@ static void bt_av_new_track(void)
|
||||
ESP_AVRC_MD_ATTR_ARTIST |
|
||||
ESP_AVRC_MD_ATTR_ALBUM |
|
||||
ESP_AVRC_MD_ATTR_GENRE;
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if (cover_art_connected) {
|
||||
attr_mask |= ESP_AVRC_MD_ATTR_COVER_ART;
|
||||
}
|
||||
#endif
|
||||
esp_avrc_ct_send_metadata_cmd(APP_RC_CT_TL_GET_META_DATA, attr_mask);
|
||||
|
||||
/* register notification if peer support the event_id */
|
||||
@ -364,7 +388,7 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
|
||||
if (a2d->a2d_psc_cfg_stat.psc_mask & ESP_A2D_PSC_DELAY_RPT) {
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device support delay reporting");
|
||||
} else {
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device unsupport delay reporting");
|
||||
ESP_LOGI(BT_AV_TAG, "Peer device unsupported delay reporting");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -415,15 +439,24 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* when passthrough responsed, this event comes */
|
||||
/* when passthrough response, this event comes */
|
||||
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC passthrough rsp: key_code 0x%x, key_state %d, rsp_code %d", rc->psth_rsp.key_code,
|
||||
rc->psth_rsp.key_state, rc->psth_rsp.rsp_code);
|
||||
break;
|
||||
}
|
||||
/* when metadata responsed, this event comes */
|
||||
/* when metadata response, this event comes */
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC metadata rsp: attribute id 0x%x, %s", rc->meta_rsp.attr_id, rc->meta_rsp.attr_text);
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if(rc->meta_rsp.attr_id == 0x80 && cover_art_connected && cover_art_getting == false) {
|
||||
/* check image handle is valid and different with last one, wo dont want to get an image repeatedly */
|
||||
if(image_handle_check(rc->meta_rsp.attr_text, rc->meta_rsp.attr_length)) {
|
||||
esp_avrc_ct_cover_art_get_linked_thumbnail(rc->meta_rsp.attr_text);
|
||||
cover_art_getting = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free(rc->meta_rsp.attr_text);
|
||||
break;
|
||||
}
|
||||
@ -436,6 +469,13 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
||||
/* when feature of remote device indicated, this event comes */
|
||||
case ESP_AVRC_CT_REMOTE_FEATURES_EVT: {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "AVRC remote features %"PRIx32", TG features %x", rc->rmt_feats.feat_mask, rc->rmt_feats.tg_feat_flag);
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if ((rc->rmt_feats.tg_feat_flag & ESP_AVRC_FEAT_FLAG_TG_COVER_ART) && !cover_art_connected) {
|
||||
ESP_LOGW(BT_RC_CT_TAG, "Peer support Cover Art feature, start connection...");
|
||||
/* set mtu to zero to use a default value */
|
||||
esp_avrc_ct_cover_art_connect(0);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
/* when notification capability of peer device got, this event comes */
|
||||
@ -448,6 +488,36 @@ static void bt_av_hdl_avrc_ct_evt(uint16_t event, void *p_param)
|
||||
bt_av_play_pos_changed();
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_CT_COVER_ART_STATE_EVT: {
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
if (rc->cover_art_state.state == ESP_AVRC_COVER_ART_CONNECTED) {
|
||||
cover_art_connected = true;
|
||||
ESP_LOGW(BT_RC_CT_TAG, "Cover Art Client connected");
|
||||
}
|
||||
else {
|
||||
cover_art_connected = false;
|
||||
ESP_LOGW(BT_RC_CT_TAG, "Cover Art Client disconnected, reason:%d", rc->cover_art_state.reason);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case ESP_AVRC_CT_COVER_ART_DATA_EVT: {
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
/* when rc->cover_art_data.final is true, it means we have received the entire image or get operation failed */
|
||||
if (rc->cover_art_data.final) {
|
||||
if(rc->cover_art_data.status == ESP_BT_STATUS_SUCCESS) {
|
||||
ESP_LOGI(BT_RC_CT_TAG, "Cover Art Client final data event, image size: %lu bytes", cover_art_image_size);
|
||||
}
|
||||
else {
|
||||
ESP_LOGE(BT_RC_CT_TAG, "Cover Art Client get operation failed");
|
||||
}
|
||||
cover_art_image_size = 0;
|
||||
/* set the getting state to false, we can get next image now */
|
||||
cover_art_getting = false;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
/* others */
|
||||
default:
|
||||
ESP_LOGE(BT_RC_CT_TAG, "%s unhandled event: %d", __func__, event);
|
||||
@ -545,6 +615,14 @@ void bt_app_a2d_data_cb(const uint8_t *data, uint32_t len)
|
||||
|
||||
void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param)
|
||||
{
|
||||
#if CONFIG_EXAMPLE_AVRCP_CT_COVER_ART_ENABLE
|
||||
/* we must handle ESP_AVRC_CT_COVER_ART_DATA_EVT in this callback, copy image data to other buff before return if need */
|
||||
if (event == ESP_AVRC_CT_COVER_ART_DATA_EVT && param->cover_art_data.status == ESP_BT_STATUS_SUCCESS) {
|
||||
cover_art_image_size += param->cover_art_data.data_len;
|
||||
/* copy image data to other place */
|
||||
/* memcpy(p_buf, param->cover_art_data.p_data, param->cover_art_data.data_len); */
|
||||
}
|
||||
#endif
|
||||
switch (event) {
|
||||
case ESP_AVRC_CT_METADATA_RSP_EVT:
|
||||
bt_app_alloc_meta_buffer(param);
|
||||
@ -553,7 +631,9 @@ void bt_app_rc_ct_cb(esp_avrc_ct_cb_event_t event, esp_avrc_ct_cb_param_t *param
|
||||
case ESP_AVRC_CT_PASSTHROUGH_RSP_EVT:
|
||||
case ESP_AVRC_CT_CHANGE_NOTIFY_EVT:
|
||||
case ESP_AVRC_CT_REMOTE_FEATURES_EVT:
|
||||
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT: {
|
||||
case ESP_AVRC_CT_GET_RN_CAPABILITIES_RSP_EVT:
|
||||
case ESP_AVRC_CT_COVER_ART_STATE_EVT:
|
||||
case ESP_AVRC_CT_COVER_ART_DATA_EVT: {
|
||||
bt_app_work_dispatch(bt_av_hdl_avrc_ct_evt, event, param, sizeof(esp_avrc_ct_cb_param_t), NULL);
|
||||
break;
|
||||
}
|
||||
|
@ -7,4 +7,5 @@ CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_A2DP_ENABLE=y
|
||||
CONFIG_BT_AVRCP_CT_COVER_ART_ENABLED=y
|
||||
CONFIG_DAC_DMA_AUTO_16BIT_ALIGN=n
|
||||
|
Loading…
Reference in New Issue
Block a user