Merge branch 'feature/support_bt_pba_client' into 'master'

feature(bt/bluedroid): Support bt pba client

Closes BT-3326 and IDFGH-10260

See merge request espressif/esp-idf!34260
This commit is contained in:
Wang Meng Yang
2024-12-09 11:26:01 +08:00
39 changed files with 4235 additions and 39 deletions

View File

@@ -146,6 +146,7 @@ if(CONFIG_BT_ENABLED)
host/bluedroid/bta/hd/include
host/bluedroid/bta/hh/include
host/bluedroid/bta/jv/include
host/bluedroid/bta/pba/include
host/bluedroid/bta/sdp/include
host/bluedroid/bta/sys/include
host/bluedroid/device/include
@@ -194,6 +195,7 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/api/esp_spp_api.c"
"host/bluedroid/api/esp_sdp_api.c"
"host/bluedroid/api/esp_l2cap_bt_api.c"
"host/bluedroid/api/esp_pbac_api.c"
"host/bluedroid/bta/ar/bta_ar.c"
"host/bluedroid/bta/av/bta_av_aact.c"
"host/bluedroid/bta/av/bta_av_act.c"
@@ -257,6 +259,10 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/bta/hf_client/bta_hf_client_rfc.c"
"host/bluedroid/bta/hf_client/bta_hf_client_sco.c"
"host/bluedroid/bta/hf_client/bta_hf_client_sdp.c"
"host/bluedroid/bta/pba/bta_pba_client_act.c"
"host/bluedroid/bta/pba/bta_pba_client_api.c"
"host/bluedroid/bta/pba/bta_pba_client_main.c"
"host/bluedroid/bta/pba/bta_pba_client_sdp.c"
"host/bluedroid/bta/sdp/bta_sdp.c"
"host/bluedroid/bta/sdp/bta_sdp_act.c"
"host/bluedroid/bta/sdp/bta_sdp_api.c"
@@ -299,6 +305,7 @@ if(CONFIG_BT_ENABLED)
"host/bluedroid/btc/profile/std/spp/btc_spp.c"
"host/bluedroid/btc/profile/std/sdp/btc_sdp.c"
"host/bluedroid/btc/profile/std/l2cap/btc_l2cap.c"
"host/bluedroid/btc/profile/std/pba/btc_pba_client.c"
"host/bluedroid/device/bdaddr.c"
"host/bluedroid/device/controller.c"
"host/bluedroid/device/interop.c"
@@ -411,6 +418,7 @@ if(CONFIG_BT_ENABLED)
"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/obex/obex_tl_rfcomm.c"
"host/bluedroid/stack/rfcomm/port_api.c"
"host/bluedroid/stack/rfcomm/port_rfc.c"
"host/bluedroid/stack/rfcomm/port_utils.c"

View File

@@ -57,6 +57,9 @@
#if BTC_HH_INCLUDED == TRUE
#include "btc_hh.h"
#endif /* BTC_HH_INCLUDED */
#if BTC_PBA_CLIENT_INCLUDED
#include "btc_pba_client.h"
#endif
#endif /* #if CLASSIC_BT_INCLUDED */
#endif
@@ -155,6 +158,9 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = {
#if BTC_HH_INCLUDED
[BTC_PID_HH] = {btc_hh_call_handler, btc_hh_cb_handler },
#endif
#if BTC_PBA_CLIENT_INCLUDED
[BTC_PID_PBA_CLIENT] = {btc_pba_client_call_handler, btc_pba_client_cb_handler},
#endif
#endif /* #if CLASSIC_BT_INCLUDED */
#endif
#if CONFIG_BLE_MESH

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -67,6 +67,9 @@ typedef enum {
#if (BTC_HF_CLIENT_INCLUDED == TRUE)
BTC_PID_HF_CLIENT,
#endif /* BTC_HF_CLIENT_INCLUDED */
#if (BTC_PBA_CLIENT_INCLUDED == TRUE)
BTC_PID_PBA_CLIENT,
#endif /* BTC_PBA_CLIENT_INCLUDED */
#endif /* CLASSIC_BT_INCLUDED */
#if CONFIG_BLE_MESH
BTC_PID_PROV,
@@ -123,8 +126,8 @@ extern "C" {
/**
* transfer an message to another module in the different task.
* @param msg message
* @param arg paramter
* @param arg_len length of paramter
* @param arg parameter
* @param arg_len length of parameter
* @param copy_func deep copy function
* @param free_func deep free function
* @return BT_STATUS_SUCCESS: success
@@ -134,7 +137,7 @@ bt_status_t btc_transfer_context(btc_msg_t *msg, void *arg, int arg_len, btc_arg
btc_arg_deep_free_t free_func);
/**
* transfer an message to another module in tha same task.
* transfer an message to another module in the same task.
* @param msg message
* @return BT_STATUS_SUCCESS: success
* others: fail

View File

@@ -191,6 +191,29 @@ config BT_HID_DEVICE_ENABLED
help
This enables the BT HID Device
menuconfig BT_PBAC_ENABLED
bool "PBAP Client"
depends on BT_CLASSIC_ENABLED
default n
select BT_GOEPC_ENABLED
help
This enables the Phone Book Access Profile Client
config BT_PBAC_SUPPORTED_FEAT
hex "PBAP Client Supported Features"
depends on BT_PBAC_ENABLED
default 0x000003FF
help
Set the supported features of PBAP Client, the default value is supported all features
config BT_PBAC_PREFERRED_MTU
int "PBAP Client Preferred MTU"
depends on BT_PBAC_ENABLED
default 0
help
MTU is limited by the max MTU of transport layer, and should not be smaller than 255,
but can be set to zero to use a default MTU of transport layer
config BT_GOEPC_ENABLED
bool
depends on BT_CLASSIC_ENABLED

View File

@@ -0,0 +1,251 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_err.h"
#include "esp_bt_main.h"
#include "esp_pbac_api.h"
#include "btc/btc_manage.h"
#include "btc_pba_client.h"
#if BTC_PBA_CLIENT_INCLUDED
esp_err_t esp_pbac_init(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_INIT_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_pbac_deinit(void)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_DEINIT_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_pbac_register_callback(esp_pbac_callback_t callback)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (callback == NULL) {
return ESP_FAIL;
}
btc_profile_cb_set(BTC_PID_PBA_CLIENT, callback);
return ESP_OK;
}
esp_err_t esp_pbac_connect(esp_bd_addr_t bd_addr)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (bd_addr == NULL) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_CONNECT_EVT;
btc_pba_client_args_t args = {0};
memcpy(args.connect.bd_addr.address, bd_addr, sizeof(esp_bd_addr_t));
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_pba_client_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_pbac_disconnect(esp_pbac_conn_hdl_t handle)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (handle == ESP_PBAC_INVALID_HANDLE) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_DISCONNECT_EVT;
btc_pba_client_args_t args = {0};
args.disconnect.handle = handle;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_pba_client_args_t), NULL, NULL);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_pbac_pull_phone_book(esp_pbac_conn_hdl_t handle, const char *name, esp_pbac_pull_phone_book_app_param_t *app_param)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (handle == ESP_PBAC_INVALID_HANDLE || name == NULL) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_PULL_PHONE_BOOK_EVT;
btc_pba_client_args_t args = {0};
args.pull_phone_book.handle = handle;
args.pull_phone_book.name = (char *)name;
if (app_param != NULL) {
args.pull_phone_book.include_app_param = true;
memcpy(&args.pull_phone_book.app_param, app_param, sizeof(esp_pbac_pull_phone_book_app_param_t));
}
else {
args.pull_phone_book.include_app_param = false;
}
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_pba_client_args_t), btc_pba_client_args_deep_copy, btc_pba_client_args_deep_free);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_pbac_set_phone_book(esp_pbac_conn_hdl_t handle, esp_pbac_set_phone_book_flags_t flags, const char *name)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
/* since ESP_PBAC_SET_PHONE_BOOK_FLAGS_ROOT is equal to ESP_PBAC_SET_PHONE_BOOK_FLAGS_DOWN, we dont check XXX_DOWN */
if (handle == ESP_PBAC_INVALID_HANDLE || (flags != ESP_PBAC_SET_PHONE_BOOK_FLAGS_ROOT && flags != ESP_PBAC_SET_PHONE_BOOK_FLAGS_UP)) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_SET_PHONE_BOOK_EVT;
btc_pba_client_args_t args = {0};
args.set_phone_book.handle = handle;
args.set_phone_book.flags = flags;
/* set phone book name is allowed to be NULL */
args.set_phone_book.name = (char *)name;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_pba_client_args_t), btc_pba_client_args_deep_copy, btc_pba_client_args_deep_free);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_pbac_set_phone_book2(esp_pbac_conn_hdl_t handle, const char *path)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_SET_PHONE_BOOK2_EVT;
btc_pba_client_args_t args = {0};
args.set_phone_book.handle = handle;
args.set_phone_book.name = (char *)path;
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_pba_client_args_t), btc_pba_client_args_deep_copy, btc_pba_client_args_deep_free);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_pbac_pull_vcard_listing(esp_pbac_conn_hdl_t handle, const char *name, esp_pbac_pull_vcard_listing_app_param_t *app_param)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (handle == ESP_PBAC_INVALID_HANDLE || name == NULL) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_PULL_VCARD_LISTING_EVT;
btc_pba_client_args_t args = {0};
args.pull_vcard_listing.handle = handle;
args.pull_vcard_listing.name = (char *)name;
if (app_param != NULL) {
args.pull_vcard_listing.include_app_param = true;
memcpy(&args.pull_vcard_listing.app_param, app_param, sizeof(esp_pbac_pull_vcard_listing_app_param_t));
}
else {
args.pull_vcard_listing.include_app_param = false;
}
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_pba_client_args_t), btc_pba_client_args_deep_copy, btc_pba_client_args_deep_free);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
esp_err_t esp_pbac_pull_vcard_entry(esp_pbac_conn_hdl_t handle, const char *name, esp_pbac_pull_vcard_entry_app_param_t *app_param)
{
if (esp_bluedroid_get_status() != ESP_BLUEDROID_STATUS_ENABLED) {
return ESP_ERR_INVALID_STATE;
}
if (handle == ESP_PBAC_INVALID_HANDLE || name == NULL) {
return ESP_ERR_INVALID_ARG;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CALL;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = BTC_PBA_CLIENT_PULL_VCARD_ENTRY_EVT;
btc_pba_client_args_t args = {0};
args.pull_vcard_entry.handle = handle;
args.pull_vcard_entry.name = (char *)name;
if (app_param != NULL) {
args.pull_vcard_entry.include_app_param = true;
memcpy(&args.pull_vcard_entry.app_param, app_param, sizeof(esp_pbac_pull_vcard_entry_app_param_t));
}
else {
args.pull_vcard_entry.include_app_param = false;
}
/* Switch to BTC context */
bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_pba_client_args_t), btc_pba_client_args_deep_copy, btc_pba_client_args_deep_free);
return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL;
}
#endif

View File

@@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_bt_defs.h"
/* Supported repositories bit mask */
#define ESP_PBA_SUPPORTED_REPO_LOCAL_PHONE_BOOK 0x01
#define ESP_PBA_SUPPORTED_REPO_SIM_CARD 0x02
#define ESP_PBA_SUPPORTED_REPO_SPEED_DIAL 0x04
#define ESP_PBA_SUPPORTED_REPO_FAVORITES 0x08
/* Supported features bit mask */
#define ESP_PBA_SUPPORTED_FEAT_DOWNLOAD 0x0001
#define ESP_PBA_SUPPORTED_FEAT_BROWSING 0x0002
#define ESP_PBA_SUPPORTED_FEAT_DATABASE_IDENTIFIER 0x0004
#define ESP_PBA_SUPPORTED_FEAT_FOLDER_VERSION_COUNTERS 0x0008
#define ESP_PBA_SUPPORTED_FEAT_VCARD_SELECTING 0x0010
#define ESP_PBA_SUPPORTED_FEAT_ENHANCED_MISSED_CALLS 0x0020
#define ESP_PBA_SUPPORTED_FEAT_X_BT_UCI_VCARD_PROPERTY 0x0040
#define ESP_PBA_SUPPORTED_FEAT_X_BT_UID_VCARD_PROPERTY 0x0080
#define ESP_PBA_SUPPORTED_FEAT_CONTACT_REFERENCING 0x0100
#define ESP_PBA_SUPPORTED_FEAT_DEFAULT_CONTACT_IMAGE_FORMAT 0x0200

View File

@@ -0,0 +1,340 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "esp_bt_defs.h"
#include "esp_pba_defs.h"
#define ESP_PBAC_INVALID_HANDLE 0 /*!< invalid handle value */
typedef uint16_t esp_pbac_conn_hdl_t;
/**
* @brief PBA client callback events
*/
typedef enum {
ESP_PBAC_INIT_EVT, /*!< PBA client initialized event */
ESP_PBAC_DEINIT_EVT, /*!< PBA client de-initialized event */
ESP_PBAC_CONNECTION_STATE_EVT, /*!< PBA client connection state changed event */
ESP_PBAC_PULL_PHONE_BOOK_RESPONSE_EVT, /*!< Response of pull phone book */
ESP_PBAC_SET_PHONE_BOOK_RESPONSE_EVT, /*!< Response of set phone book */
ESP_PBAC_PULL_VCARD_LISTING_RESPONSE_EVT, /*!< Response of pull vCard listing */
ESP_PBAC_PULL_VCARD_ENTRY_RESPONSE_EVT, /*!< Response of pull vCard entry */
} esp_pbac_event_t;
/**
* @brief PBA client status code
*/
typedef enum {
ESP_PBAC_SUCCESS = 0, /*!< Operation success */
ESP_PBAC_FAILURE, /*!< Generic failure */
ESP_PBAC_ALREADY_CONN, /*!< Connection to peer device already exist */
ESP_PBAC_NO_RESOURCE, /*!< No more resource */
ESP_PBAC_SDP_FAIL, /*!< Connection failed in SDP */
ESP_PBAC_GOEP_FAIL, /*!< Operation failed in GOEP */
ESP_PBAC_AUTH_FAIL, /*!< Connection failed in OBEX authentication */
ESP_PBAC_DEINIT, /*!< Connection closed due to pba client is deinit */
/* these error code is related to OBEX */
ESP_PBAC_BAD_REQUEST = 0xC0, /*!< Server couldn't understand request */
ESP_PBAC_UNAUTHORIZED = 0xC1, /*!< Unauthorized */
ESP_PBAC_FORBIDDEN = 0xC3, /*!< Operation is understood but refused */
ESP_PBAC_NOT_FOUND = 0xC4, /*!< Not found */
ESP_PBAC_NOT_ACCEPTABLE = 0xC6, /*!< Not Acceptable */
ESP_PBAC_PRECONDITION_FAILED = 0xCC, /*!< Precondition failed */
ESP_PBAC_NOT_IMPLEMENTED = 0xD1, /*!< Not implemented */
ESP_PBAC_SERVICE_UNAVAILABLE = 0xD3, /*!< Service unavailable */
} esp_pbac_status_t;
/**
* @brief PBA client set phone book flags
*/
typedef enum {
ESP_PBAC_SET_PHONE_BOOK_FLAGS_ROOT = 0x02, /*!< Go back to root, name should set to empty string, not NULL */
ESP_PBAC_SET_PHONE_BOOK_FLAGS_DOWN = 0x02, /*!< Go down 1 level, name should set to child folder */
ESP_PBAC_SET_PHONE_BOOK_FLAGS_UP = 0x03, /*!< Go up 1 level, name is optional */
} esp_pbac_set_phone_book_flags_t;
/**
* @brief PBA client pull phone book optional application parameter
*/
typedef struct {
uint8_t include_property_selector : 1; /*!< 1 if app param include property_selector */
uint8_t include_format : 1; /*!< 1 if app param include format */
uint8_t include_max_list_count : 1; /*!< 1 if app param include max_list_count */
uint8_t include_list_start_offset : 1; /*!< 1 if app param include list_start_offset */
uint8_t include_reset_new_missed_calls : 1; /*!< 1 if app param include reset_new_missed_calls */
uint8_t include_vcard_selector : 1; /*!< 1 if app param include vcard_selector */
uint8_t include_vcard_selector_operator : 1; /*!< 1 if app param include vcard_selector_operator */
uint8_t format; /*!< 0x00 = 2.1, 0x01 = 3.0 */
uint8_t reset_new_missed_calls; /*!< 0x01 = Reset */
uint8_t vcard_selector_operator; /*!< 0x00 = OR, 0x01 = AND */
uint16_t max_list_count; /*!< 0x0000 to 0xFFFF */
uint16_t list_start_offset; /*!< 0x0000 to 0xFFFF */
uint64_t property_selector; /*!< 64 bits mask */
uint64_t vcard_selector; /*!< 64 bits mask */
} esp_pbac_pull_phone_book_app_param_t;
/**
* @brief PBA client pull vCard listing optional application parameter
*/
typedef struct {
uint8_t include_order : 1; /*!< 1 if app param include order */
uint8_t include_search_value : 1; /*!< 1 if app param include search_value */
uint8_t include_search_property : 1; /*!< 1 if app param include search_property */
uint8_t include_max_list_count : 1; /*!< 1 if app param include max_list_count */
uint8_t include_list_start_offset : 1; /*!< 1 if app param include list_start_offset */
uint8_t include_reset_new_missed_calls : 1; /*!< 1 if app param include reset_new_missed_calls */
uint8_t include_vcard_selector : 1; /*!< 1 if app param include vcard_selector */
uint8_t include_vcard_selector_operator : 1; /*!< 1 if app param include vcard_selector_operator */
uint8_t order; /*!< 0x00 = indexed, 0x01 = alphanumeric */
uint8_t search_property; /*!< 0x00 = Name, 0x01 = Number, 0x02 = Sound */
uint8_t reset_new_missed_calls; /*!< 0x01 = Reset */
uint8_t vcard_selector_operator; /*!< 0x00 = OR, 0x01 = AND */
uint16_t max_list_count; /*!< 0x0000 to 0xFFFF */
uint16_t list_start_offset; /*!< 0x0000 to 0xFFFF */
char *search_value; /*!< Text */
uint64_t vcard_selector; /*!< 64 bits mask */
} esp_pbac_pull_vcard_listing_app_param_t;
/**
* @brief PBA client pull vCard entry optional application parameter
*/
typedef struct {
uint8_t include_property_selector : 1; /*!< 1 if app param include property_selector */
uint8_t include_format : 1; /*!< 1 if app param include format */
uint8_t format; /*!< 0x00 = 2.1, 0x01 = 3.0 */
uint64_t property_selector; /*!< 64 bits mask */
} esp_pbac_pull_vcard_entry_app_param_t;
/**
* @brief PBA client callback parameters
*/
typedef union {
/**
* @brief ESP_PBAC_CONNECTION_STATE_EVT
*/
struct pbac_conn_stat_param {
bool connected; /*!< whether pba client is connected to server */
esp_pbac_conn_hdl_t handle; /*!< connection handle, non zeros if exist */
uint8_t peer_supported_repo; /*!< peer supported repositories */
uint32_t peer_supported_feat; /*!< peer supported features */
esp_bd_addr_t remote_bda; /*!< remote bluetooth device address */
esp_pbac_status_t reason; /*!< reason if disconnect */
} conn_stat; /*!< PBA client connection status */
/**
* @brief ESP_PBAC_PULL_PHONE_BOOK_RESPONSE_EVT
*/
struct pbac_pull_phone_book_rsp_param {
esp_pbac_conn_hdl_t handle; /*!< PBA client connection handle */
esp_pbac_status_t result; /*!< operation result, ESP_PBAC_SUCCESS if success */
bool final; /*!< whether this is the final response packet */
uint8_t *data; /*!< response data */
uint16_t data_len; /*!< response data len */
/* The following are the application parameters */
uint8_t include_phone_book_size : 1; /*!< 1 if app param include phone_book_size */
uint8_t include_new_missed_calls : 1; /*!< 1 if app param include new_missed_calls */
uint8_t include_primary_folder_version : 1; /*!< 1 if app param include primary_folder_version */
uint8_t include_secondary_folder_version : 1; /*!< 1 if app param include secondary_folder_version */
uint8_t include_database_identifier : 1; /*!< 1 if app param include database_identifier */
uint8_t new_missed_calls; /*!< 0x00 to 0xFF */
uint16_t phone_book_size; /*!< 0x0000 to 0xFFFF */
uint8_t *primary_folder_version; /*!< 0 to (2^128 -1) */
uint8_t *secondary_folder_version; /*!< 0 to (2^128 -1) */
uint8_t *database_identifier; /*!< 0 to (2^128 -1) */
} pull_phone_book_rsp; /*!< pull phone book response */
/**
* @brief ESP_PBAC_SET_PHONE_BOOK_RESPONSE_EVT
*/
struct pbac_set_phone_book_rsp_param {
esp_pbac_conn_hdl_t handle; /*!< PBA client connection handle */
esp_pbac_status_t result; /*!< operation result, ESP_PBAC_SUCCESS if success */
} set_phone_book_rsp; /*!< set phone book response, always the final response */
/**
* @brief ESP_PBAC_PULL_VCARD_LISTING_RESPONSE_EVT
*/
struct pbac_pull_vcard_listing_rsp_param {
esp_pbac_conn_hdl_t handle; /*!< PBA client connection handle */
esp_pbac_status_t result; /*!< operation result, ESP_PBAC_SUCCESS if success */
bool final; /*!< whether this is the final response packet */
uint8_t *data; /*!< response data */
uint16_t data_len; /*!< response data len */
/* The following are the application parameters */
uint8_t include_phone_book_size : 1; /*!< 1 if app param include phone_book_size */
uint8_t include_new_missed_calls : 1; /*!< 1 if app param include new_missed_calls */
uint8_t include_primary_folder_version : 1; /*!< 1 if app param include primary_folder_version */
uint8_t include_secondary_folder_version : 1; /*!< 1 if app param include secondary_folder_version */
uint8_t include_database_identifier : 1; /*!< 1 if app param include database_identifier */
uint8_t new_missed_calls; /*!< 0x00 to 0xFF */
uint16_t phone_book_size; /*!< 0x0000 to 0xFFFF */
uint8_t *primary_folder_version; /*!< 0 to (2^128 -1) */
uint8_t *secondary_folder_version; /*!< 0 to (2^128 -1) */
uint8_t *database_identifier; /*!< 0 to (2^128 -1) */
} pull_vcard_listing_rsp; /*!< pull vcard listing response */
/**
* @brief ESP_PBAC_PULL_VCARD_ENTRY_RESPONSE_EVT
*/
struct pbac_pull_vcard_entry_rsp_param {
esp_pbac_conn_hdl_t handle; /*!< PBA client connection handle */
esp_pbac_status_t result; /*!< operation result, ESP_PBAC_SUCCESS if success */
bool final; /*!< whether this is the final response packet */
uint8_t *data; /*!< response data */
uint16_t data_len; /*!< response data len */
/* The following are the application parameters */
uint8_t include_database_identifier : 1; /*!< 1 if app param include database_identifier */
uint8_t *database_identifier; /*!< 0 to (2^128 -1) */
} pull_vcard_entry_rsp; /*!< pull vcard listing response */
} esp_pbac_param_t;
/**
* @brief PBA client callback function type
*
* @param event : Event type
*
* @param param : Pointer to callback parameter
*/
typedef void (*esp_pbac_callback_t)(esp_pbac_event_t event, esp_pbac_param_t *param);
/**
* @brief This function is called to register a user callbacks in PBA client.
*
* @param[in] callback: pointer to the user callback function.
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_register_callback(esp_pbac_callback_t callback);
/**
* @brief Initializes PBA client interface. This function should be called after bluedroid
* enable successfully, and should be called after esp_pbac_register_callback.
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_init(void);
/**
* @brief De-initializes PBA client interface. This will close all PBA client connection.
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_deinit(void);
/**
* @brief Start the process to establish a connection to PBA server.
*
* @param[in] bd_addr: peer bluetooth device address
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_connect(esp_bd_addr_t bd_addr);
/**
* @brief Disconnects from the current connected PBA server.
*
* @param[in] handle: connection handle retrieved from ESP_PBAC_CONNECTION_STATE_EVT
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_disconnect(esp_pbac_conn_hdl_t handle);
/**
* @brief Send a request to pull phone book.
*
* @param[in] handle: connection handle retrieved from ESP_PBAC_CONNECTION_STATE_EVT
*
* @param[in] name: phone book object path and name, shall contain the absolute path
* in the virtual folder architecture
*
* @param[in] app_param: optional application parameter
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_pull_phone_book(esp_pbac_conn_hdl_t handle, const char *name, esp_pbac_pull_phone_book_app_param_t *app_param);
/**
* @brief Send a request to set the current folder in the virtual folder architecture.
*
* @param[in] handle: connection handle retrieved from ESP_PBAC_CONNECTION_STATE_EVT
*
* @param[in] flags: operation flags, one of ESP_PBAC_SET_PHONE_BOOK_FLAGS_XXX
*
* @param[in] name: folder name, if flags is set to ROOT, name should be empty string (""),
* if flags is set to UP, name is optional, if flags is set to DOWN, name
* is mandatory
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_set_phone_book(esp_pbac_conn_hdl_t handle, esp_pbac_set_phone_book_flags_t flags, const char *name);
/**
* @brief Set the current folder in the virtual folder architecture, use absolute path.
*
* @param[in] handle: connection handle retrieved from ESP_PBAC_CONNECTION_STATE_EVT
*
* @param[in] path: absolute path of the folder intend to set. NULL or empty string will
* set to ROOT
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_set_phone_book2(esp_pbac_conn_hdl_t handle, const char *path);
/**
* @brief Send a request to pull vCard listing.
*
* @param[in] handle: connection handle retrieved from ESP_PBAC_CONNECTION_STATE_EVT
*
* @param[in] name: specifies the name of the folder to be retrieved, uses relative paths,
* shall not include any path information. An empty name (empty string "")
* may be sent to retrieve the vCard Listing object of the current folder.
* However, it is illegal to issue a pull vCard listing request with an
* empty name header from the telecom/ folder
*
* @param[in] app_param: optional application parameter
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_pull_vcard_listing(esp_pbac_conn_hdl_t handle, const char *name, esp_pbac_pull_vcard_listing_app_param_t *app_param);
/**
* @brief Send a request to pull vCard entry.
*
* @param[in] handle: connection handle retrieved from ESP_PBAC_CONNECTION_STATE_EVT
*
* @param[in] name: vCard name or, if supported, the X-BT-UID of the object to be retrieved.
* uses relative paths,shall not include any path information
*
* @param[in] app_param: optional application parameter
*
* @return
* - ESP_OK: success
* - other: failed
*/
esp_err_t esp_pbac_pull_vcard_entry(esp_pbac_conn_hdl_t handle, const char *name, esp_pbac_pull_vcard_entry_app_param_t *app_param);

View File

@@ -278,6 +278,7 @@ void bta_av_ca_api_get(tBTA_AV_RCB *p_rcb, tBTA_AV_DATA *p_data)
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);
osi_free(p_data->api_ca_get.image_descriptor);
}
/* always request to enable srm */
GOEPC_RequestSetSRM(p_rcb->cover_art_goep_hdl, TRUE, FALSE);

View File

@@ -0,0 +1,92 @@
/*
* 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 "bta/bta_pba_defs.h"
#if BTA_PBA_CLIENT_INCLUDED
/* Phone Book Access Profile (client) version */
#define PBAP_PCE_VERSION 0x102 /* v1.2 */
#define PBAP_PCE_SUPPORTED_FEATURES 0x0000003F /* support all features */
/* PBA Client callback events */
#define BTA_PBA_CLIENT_ENABLE_EVT 0 /* PBA Client enabled */
#define BTA_PBA_CLIENT_DISABLE_EVT 1 /* PBA Client disabled */
#define BTA_PBA_CLIENT_REGISTER_EVT 2 /* PBA Client registered */
#define BTA_PBA_CLIENT_DEREGISTER_EVT 3 /* PBA Client deregistered */
#define BTA_PBA_CLIENT_CONN_OPEN_EVT 4 /* PBA Client connection opened */
#define BTA_PBA_CLIENT_CONN_CLOSE_EVT 5 /* PBA Client connection closed */
#define BTA_PBA_CLIENT_PULL_PHONE_BOOK_RSP_EVT 6 /* PBA Client pull phone book response */
#define BTA_PBA_CLIENT_SET_PHONE_BOOK_RSP_EVT 7 /* PBA Client set phone book response */
#define BTA_PBA_CLIENT_PULL_VCARD_LISTING_RSP_EVT 8 /* PBA Client pull vCard listing response */
#define BTA_PBA_CLIENT_PULL_VCARD_ENTRY_RSP_EVT 9 /* PBA Client pull vCard entry response */
typedef enum {
BTA_PBA_CLIENT_NO_ERROR, /* operation success */
BTA_PBA_CLIENT_FAIL, /* general fail */
BTA_PBA_CLIENT_ALREADY_CONN, /* connection to peer device already exist */
BTA_PBA_CLIENT_NO_RESOURCE, /* no resource */
BTA_PBA_CLIENT_SDP_ERROR, /* error in sdp */
BTA_PBA_CLIENT_GOEP_ERROR, /* error in goep */
BTA_PBA_CLIENT_AUTH_FAIL, /* authenticate failed */
BTA_PBA_CLIENT_DISABLE, /* operation failed due to pba client is disable */
/* these error code is related to OBEX */
BTA_PBA_CLIENT_BAD_REQUEST = 0xC0, /* Server couldn't understand request */
BTA_PBA_CLIENT_UNAUTHORIZED = 0xC1, /* Unauthorized */
BTA_PBA_CLIENT_FORBIDDEN = 0xC3, /* Operation is understood but refused > */
BTA_PBA_CLIENT_NOT_FOUND = 0xC4, /* Not found */
BTA_PBA_CLIENT_NOT_ACCEPTABLE = 0xC6, /* Not Acceptable */
BTA_PBA_CLIENT_PRECONDITION_FAILED = 0xCC, /* Precondition failed */
BTA_PBA_CLIENT_NOT_IMPLEMENTED = 0xD1, /* Not implemented */
BTA_PBA_CLIENT_SERVICE_UNAVAILABLE = 0xD3, /* Service unavailable */
} tBTA_PBA_CLIENT_ERR;
typedef struct {
UINT16 handle; /* connection handle */
tBTA_PBA_CLIENT_ERR error; /* error code */
BD_ADDR bd_addr; /* peer BD addr */
UINT8 peer_supported_repo; /* peer supported repositories */
UINT32 peer_supported_feat; /* peer supported feature */
} tBTA_PBA_CLIENT_CONN;
typedef struct {
tBTA_PBA_CLIENT_ERR status;
UINT16 handle; /* connection handle */
BOOLEAN final; /* final data event */
UINT8 *data; /* body data in response packet */
UINT16 data_len; /* body data length */
UINT8 *app_param; /* application parameters */
UINT16 app_param_len; /* application parameters length */
BT_HDR *pkt; /* raw buff that store all data, should be freed before return */
} tBTA_PBA_CLIENT_RESPONSE;
/* union of data associated with PBA Client callback */
typedef union {
tBTA_PBA_CLIENT_CONN conn; /* CONN_OPEN_EVT, CONN_CLOSE_EVT */
tBTA_PBA_CLIENT_RESPONSE response; /* XXX_RSP_EVT */
} tBTA_PBA_CLIENT;
typedef UINT8 tBTA_PBA_CLIENT_EVT;
typedef void (tBTA_PBA_CLIENT_CBACK)(tBTA_PBA_CLIENT_EVT event, tBTA_PBA_CLIENT *p_data);
void BTA_PbaClientEnable(tBTA_PBA_CLIENT_CBACK *p_cback);
void BTA_PbaClientDisable(void);
void BTA_PbaClientRegister(const char *server_name);
void BTA_PbaClientDeregister(void);
void BTA_PbaClientOpen(BD_ADDR bd_addr, tBTA_SEC sec_mask, UINT32 supported_feat, UINT16 mtu);
void BTA_PbaClientClose(UINT16 handle);
void BTA_PbaClientPullPhoneBook(UINT16 handle, char *name, UINT8 *app_param, UINT16 app_param_len);
void BTA_PbaClientSetPhoneBook(UINT16 handle, UINT8 flags, char *name);
void BTA_PbaClientPullvCardListing(UINT16 handle, char *name, UINT8 *app_param, UINT16 app_param_len);
void BTA_PbaClientPullvCardEntry(UINT16 handle, char *name, UINT8 *app_param, UINT16 app_param_len);
#endif

View File

@@ -0,0 +1,93 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
/* PBAP supported repositories */
#define BTA_PBAP_REPO_LOCAL_PHONEBOOK 0x01
#define BTA_PBAP_REPO_SIM_CARD 0x02
#define BTA_PBAP_REPO_SPEED_DIAL 0x04
#define BTA_PBAP_REPO_FAVORITES 0x08
/* PBAP supported features */
#define BTA_PBAP_FEAT_DOWNLOAD 0x0001
#define BTA_PBAP_FEAT_BROWSING 0x0002
#define BTA_PBAP_FEAT_DATABASE_IDENTIFIER 0x0004
#define BTA_PBAP_FEAT_FLODER_VERSION_COUNTER 0x0008
#define BTA_PBAP_FEAT_VCARD_SELECTING 0x0010
#define BTA_PBAP_FEAT_ENHANCED_MISSED_CALLS 0x0020
#define BTA_PBAP_FEAT_X_BT_UCI_VCARD_PROPERTY 0x0040
#define BTA_PBAP_FEAT_X_BT_UID_VCARD_PROPERTY 0x0080
#define BTA_PBAP_FEAT_CONTACT_REFERENCING 0x0100
#define BTA_PBAP_FEAT_DEFAULT_CONTACT_IMAGE_FORMAT 0x0200
/* PBAP default supported features */
#define BTA_PBAP_DEFAULT_SUPPORTED_FEATURES 0x0003
/* Application parameters tag id */
#define BTA_PBAP_APP_PARAM_ORDER 0x01
#define BTA_PBAP_APP_PARAM_SEARCH_VALUE 0x02
#define BTA_PBAP_APP_PARAM_SEARCH_PROPERTY 0x03
#define BTA_PBAP_APP_PARAM_MAX_LIST_COUNT 0x04
#define BTA_PBAP_APP_PARAM_LIST_START_OFFSET 0x05
#define BTA_PBAP_APP_PARAM_PROPERTY_SELECTOR 0x06
#define BTA_PBAP_APP_PARAM_FORMAT 0x07
#define BTA_PBAP_APP_PARAM_PHONE_BOOK_SIZE 0x08
#define BTA_PBAP_APP_PARAM_NEW_MISSED_CALLS 0x09
#define BTA_PBAP_APP_PARAM_PRIMARY_FOLDER_VERSION 0x0A
#define BTA_PBAP_APP_PARAM_SECONDARY_FOLDER_VERSION 0x0B
#define BTA_PBAP_APP_PARAM_VCARD_SELECTOR 0x0C
#define BTA_PBAP_APP_PARAM_DATABASE_IDENTIFIER 0x0D
#define BTA_PBAP_APP_PARAM_VCARD_SELECTOR_OPERATOR 0x0E
#define BTA_PBAP_APP_PARAM_RESET_NEW_MISSED_CALLS 0x0F
#define BTA_PBAP_APP_PARAM_PBAP_SUPPORTED_FEATURES 0x10
/* Application parameters length (except SearchValue) */
#define BTA_PBAP_APP_PARAM_LENGTH_ORDER 1
#define BTA_PBAP_APP_PARAM_LENGTH_SEARCH_PROPERTY 1
#define BTA_PBAP_APP_PARAM_LENGTH_MAX_LIST_COUNT 2
#define BTA_PBAP_APP_PARAM_LENGTH_LIST_START_OFFSET 2
#define BTA_PBAP_APP_PARAM_LENGTH_PROPERTY_SELECTOR 8
#define BTA_PBAP_APP_PARAM_LENGTH_FORMAT 1
#define BTA_PBAP_APP_PARAM_LENGTH_PHONE_BOOK_SIZE 2
#define BTA_PBAP_APP_PARAM_LENGTH_NEW_MISSED_CALLS 1
#define BTA_PBAP_APP_PARAM_LENGTH_PRIMARY_FOLDER_VERSION 16
#define BTA_PBAP_APP_PARAM_LENGTH_SECONDARY_FOLDER_VERSION 16
#define BTA_PBAP_APP_PARAM_LENGTH_VCARD_SELECTOR 8
#define BTA_PBAP_APP_PARAM_LENGTH_DATABASE_IDENTIFIER 16
#define BTA_PBAP_APP_PARAM_LENGTH_VCARD_SELECTOR_OPERATOR 1
#define BTA_PBAP_APP_PARAM_LENGTH_RESET_NEW_MISSED_CALLS 1
#define BTA_PBAP_APP_PARAM_LENGTH_PBAP_SUPPORTED_FEATURES 4
/* Application parameter tag (1 byte) + Application parameter length (1 byte) */
#define BTA_PBAP_APP_PARAM_HEADER_LENGTH 2
/* The minimal buff size that can hold all pull phone book application parameters */
#define BTA_PBAP_PULL_PHONE_BOOK_APP_PARAM_BUFF_SIZE_MIN 37
/* The minimal buff size that can hold pull vCard listing application parameters, except the value of SearchValue */
#define BTA_PBAP_PULL_VCARD_LISTING_APP_PARAM_BUFF_SIZE_MIN 34
/* The minimal buff size that can hold all pull vCard entry application parameters */
#define BTA_PBAP_PULL_VCARD_ENTRY_APP_PARAM_BUFF_SIZE_MIN 13
/* Application parameters bit mask */
#define PBAP_APP_PARAM_BIT_MASK_ORDER 0x0001
#define PBAP_APP_PARAM_BIT_MASK_SEARCH_VALUE 0x0002
#define PBAP_APP_PARAM_BIT_MASK_SEARCH_PROPERTY 0x0004
#define PBAP_APP_PARAM_BIT_MASK_MAX_LIST_COUNT 0x0008
#define PBAP_APP_PARAM_BIT_MASK_LIST_START_OFFSET 0x0010
#define PBAP_APP_PARAM_BIT_MASK_PROPERTY_SELECTOR 0x0020
#define PBAP_APP_PARAM_BIT_MASK_FORMAT 0x0040
#define PBAP_APP_PARAM_BIT_MASK_PHONE_BOOK_SIZE 0x0080
#define PBAP_APP_PARAM_BIT_MASK_NEW_MISSED_CALLS 0x0100
#define PBAP_APP_PARAM_BIT_MASK_PRIMARY_FOLDER_VERSION 0x0200
#define PBAP_APP_PARAM_BIT_MASK_SECONDARY_FOLDER_VERSION 0x0400
#define PBAP_APP_PARAM_BIT_MASK_VCARD_SELECTOR 0x0800
#define PBAP_APP_PARAM_BIT_MASK_DATABASE_IDENTIFIER 0x1000
#define PBAP_APP_PARAM_BIT_MASK_VCARD_SELECTOR_OPERATOR 0x2000
#define PBAP_APP_PARAM_BIT_MASK_RESET_NEW_MISSED_CALLS 0x4000
#define PBAP_APP_PARAM_BIT_MASK_PBAP_SUPPORTED_FEATURES 0x8000

View File

@@ -0,0 +1,702 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "osi/allocator.h"
#include "common/bt_target.h"
#include "stack/bt_types.h"
#include "stack/obex_api.h"
#include "stack/goep_common.h"
#include "stack/goepc_api.h"
#include "bta_pba_client_int.h"
#if BTA_PBA_CLIENT_INCLUDED
static const UINT8 pbap_target_uuid[16] = {0x79, 0x61, 0x35, 0xf0, 0xf0, 0xc5, 0x11, 0xd8, 0x09, 0x66, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66};
static const char *type_pull_phone_book = "x-bt/phonebook";
static const char *type_pull_vcard_listing = "x-bt/vcard-listing";
static const char *type_pull_vcard_entry = "x-bt/vcard";
#define TYPE_LEN_PULL_PHONE_BOOK 15
#define TYPE_LEN_PULL_VCARD_LISTING 19
#define TYPE_LEN_PULL_VCARD_ENTRY 11
static void free_ccb(tBTA_PBA_CLIENT_CCB *p_ccb)
{
/* free sdp db */
bta_pba_client_free_db(p_ccb);
/* clear all field, set allocated to 0 */
memset(p_ccb, 0, sizeof(tBTA_PBA_CLIENT_CCB));
}
static void close_goepc_and_report(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_ERR reason)
{
/* remove goep connection */
if (p_ccb->goep_handle != 0) {
GOEPC_Close(p_ccb->goep_handle);
p_ccb->goep_handle = 0;
}
/* report connection closed event */
tBTA_PBA_CLIENT_CONN conn;
conn.handle = p_ccb->allocated;
conn.error = reason;
bdcpy(conn.bd_addr, p_ccb->bd_addr);
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_CONN_CLOSE_EVT, (tBTA_PBA_CLIENT *)&conn);
/* free ccb */
free_ccb(p_ccb);
}
static void build_and_send_empty_get_req(tBTA_PBA_CLIENT_CCB *p_ccb)
{
tOBEX_PARSE_INFO info = {0};
info.opcode = OBEX_OPCODE_GET_FINAL;
/* empty get request, try to use a smaller buff size */
UINT16 tx_buff_size = BT_SMALL_BUFFER_SIZE < p_ccb->max_tx ? BT_SMALL_BUFFER_SIZE : p_ccb->max_tx;
UINT16 ret = GOEPC_PrepareRequest(p_ccb->goep_handle, &info, tx_buff_size);
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_ccb->goep_cid), 4);
ret |= GOEPC_SendRequest(p_ccb->goep_handle);
if (ret != GOEP_SUCCESS) {
close_goepc_and_report(p_ccb, BTA_PBA_CLIENT_GOEP_ERROR);
}
}
static uint8_t get_operation_response_event(tBTA_PBA_CLIENT_OP operation)
{
uint8_t event = 0;
switch (operation)
{
case BTA_PBA_CLIENT_OP_PULL_PHONE_BOOK:
event = BTA_PBA_CLIENT_PULL_PHONE_BOOK_RSP_EVT;
break;
case BTA_PBA_CLIENT_OP_SET_PHONE_BOOK:
event = BTA_PBA_CLIENT_SET_PHONE_BOOK_RSP_EVT;
break;
case BTA_PBA_CLIENT_OP_PULL_VCARD_LISTING:
event = BTA_PBA_CLIENT_PULL_VCARD_LISTING_RSP_EVT;
break;
case BTA_PBA_CLIENT_OP_PULL_VCARD_ENTRY:
event = BTA_PBA_CLIENT_PULL_VCARD_ENTRY_RSP_EVT;
break;
default:
assert(0);
break;
}
return event;
}
static tBTA_PBA_CLIENT_ERR calculate_response_error(uint8_t response_code)
{
tBTA_PBA_CLIENT_ERR error = BTA_PBA_CLIENT_FAIL;
/* always treat error response code as final response */
response_code |= OBEX_FINAL_BIT_MASK;
switch (response_code)
{
case (OBEX_RESPONSE_CODE_BAD_REQUEST | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_BAD_REQUEST;
break;
case (OBEX_RESPONSE_CODE_UNAUTHORIZED | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_UNAUTHORIZED;
break;
case (OBEX_RESPONSE_CODE_FORBIDDEN | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_FORBIDDEN;
break;
case (OBEX_RESPONSE_CODE_NOT_FOUND | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_NOT_FOUND;
break;
case (OBEX_RESPONSE_CODE_NOT_ACCEPTABLE | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_NOT_ACCEPTABLE;
break;
case (OBEX_RESPONSE_CODE_PRECONDITION_FAILED | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_PRECONDITION_FAILED;
break;
case (OBEX_RESPONSE_CODE_NOT_IMPLEMENTED | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_NOT_IMPLEMENTED;
break;
case (OBEX_RESPONSE_CODE_SERVICE_UNAVAILABLE | OBEX_FINAL_BIT_MASK):
error = BTA_PBA_CLIENT_SERVICE_UNAVAILABLE;
break;
default:
break;
}
return error;
}
static void report_data_event(tBTA_PBA_CLIENT_CCB *p_ccb, UINT8 *data, UINT16 data_len, UINT8 *app_param,
UINT16 app_param_len, BOOLEAN final, BT_HDR *pkt)
{
uint8_t event = get_operation_response_event(p_ccb->operation);
tBTA_PBA_CLIENT_RESPONSE response;
response.status = BTA_PBA_CLIENT_NO_ERROR;
response.handle = p_ccb->allocated;
response.final = final;
response.data_len = data_len;
response.data = data;
response.app_param_len = app_param_len;
response.app_param = app_param;
response.pkt = pkt;
bta_pba_client_cb.p_cback(event, (tBTA_PBA_CLIENT *)&response);
}
static void report_error_data_event(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_ERR status)
{
uint8_t event = get_operation_response_event(p_ccb->operation);
tBTA_PBA_CLIENT_RESPONSE response;
memset(&response, 0, sizeof(tBTA_PBA_CLIENT_RESPONSE));
response.status = status;
response.handle = p_ccb->allocated;
response.final = TRUE;
bta_pba_client_cb.p_cback(event, (tBTA_PBA_CLIENT *)&response);
}
static void ascii_to_utf16(const UINT8 *src, UINT16 len, UINT8 *dst)
{
UINT16 pos = 0;
for (int i = 0 ; i < len ; i++){
dst[pos++] = 0;
dst[pos++] = src[i];
}
}
void bta_pba_client_api_enable(tBTA_PBA_CLIENT_DATA *p_data)
{
/* initialize control block */
memset(&bta_pba_client_cb, 0, sizeof(tBTA_PBA_CLIENT_CB));
/* store callback function */
bta_pba_client_cb.p_cback = p_data->api_enable.p_cback;
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_ENABLE_EVT, NULL);
}
void bta_pba_client_api_disable(tBTA_PBA_CLIENT_DATA *p_data)
{
if (!bta_sys_is_register(BTA_ID_PBC)) {
return;
}
/* deregister with BTA system manager */
bta_sys_deregister(BTA_ID_PBC);
/* close all connections */
for (int i = 0; i < PBA_CLIENT_MAX_CONNECTION; ++i) {
if (bta_pba_client_cb.ccb[i].allocated) {
close_goepc_and_report(&bta_pba_client_cb.ccb[i], BTA_PBA_CLIENT_DISABLE);
}
}
/* store and clear callback function */
tBTA_PBA_CLIENT_CBACK *p_cback = bta_pba_client_cb.p_cback;
bta_pba_client_cb.p_cback = NULL;
if(p_cback) {
p_cback(BTA_PBA_CLIENT_DISABLE_EVT, NULL);
}
}
void bta_pba_client_api_register(tBTA_PBA_CLIENT_DATA *p_data)
{
/* create SDP records */
bta_pba_client_create_record(p_data->api_register.name);
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_REGISTER_EVT, NULL);
}
void bta_pba_client_api_deregister(tBTA_PBA_CLIENT_DATA *p_data)
{
/* delete SDP records */
bta_pba_client_del_record();
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_DEREGISTER_EVT, NULL);
}
void bta_pba_client_api_open(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
p_ccb->sec_mask = p_data->api_open.sec_mask;
if (p_data->api_open.mtu < PBA_CLIENT_MIN_MTU || p_data->api_open.mtu > PBA_CLIENT_MAX_MTU) {
p_ccb->max_rx = PBA_CLIENT_MAX_MTU;
p_ccb->max_tx = PBA_CLIENT_MAX_MTU;
}
else {
p_ccb->max_rx = p_data->api_open.mtu;
p_ccb->max_tx = p_data->api_open.mtu;
}
bdcpy(p_ccb->bd_addr, p_data->api_open.bd_addr);
p_ccb->our_supported_feat = p_data->api_open.supported_feat;
if (!bta_pba_client_do_disc(p_ccb)) {
close_goepc_and_report(p_ccb, BTA_PBA_CLIENT_SDP_ERROR);
}
}
void bta_pba_client_api_close(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
UNUSED(p_data);
tOBEX_PARSE_INFO info = {0};
info.opcode = OBEX_OPCODE_DISCONNECT;
UINT16 tx_buff_size = BT_SMALL_BUFFER_SIZE < p_ccb->max_tx ? BT_SMALL_BUFFER_SIZE : p_ccb->max_tx;
UINT16 ret = GOEPC_PrepareRequest(p_ccb->goep_handle, &info, tx_buff_size);
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_ccb->goep_cid), 4);
ret |= GOEPC_SendRequest(p_ccb->goep_handle);
if (ret != GOEP_SUCCESS) {
/* anyway, this close operation is requested by upper, set reason to success */
close_goepc_and_report(p_ccb, BTA_PBA_CLIENT_NO_ERROR);
}
}
void bta_pba_client_api_req(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
tOBEX_PARSE_INFO info = {0};
UINT16 ret;
if (p_data->api_req.operation == BTA_PBA_CLIENT_OP_SET_PHONE_BOOK) {
info.opcode = OBEX_OPCODE_SETPATH;
info.flags = p_data->api_req.flags;
}
else {
info.opcode = OBEX_OPCODE_GET_FINAL;
}
ret = GOEPC_PrepareRequest(p_ccb->goep_handle, &info, p_ccb->max_tx);
if (ret != GOEP_SUCCESS) {
goto error;
}
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_CONNECTION_ID, (UINT8 *)(&p_ccb->goep_cid), 4);
if (p_data->api_req.operation != BTA_PBA_CLIENT_OP_SET_PHONE_BOOK) {
ret |= GOEPC_RequestSetSRM(p_ccb->goep_handle, TRUE, FALSE);
}
if (p_data->api_req.name) {
UINT16 name_len = strlen(p_data->api_req.name) + 1;
if (name_len == 1) {
/* empty string, add empty name header */
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_NAME, NULL, 0);
}
else {
UINT8 *utf16_name = osi_malloc(2 * name_len);
assert(utf16_name != NULL);
ascii_to_utf16((UINT8 *)p_data->api_req.name, name_len, utf16_name);
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_NAME, utf16_name, 2 * name_len);
osi_free(utf16_name);
}
osi_free(p_data->api_req.name);
p_data->api_req.name = NULL;
}
switch (p_data->api_req.operation)
{
case BTA_PBA_CLIENT_OP_PULL_PHONE_BOOK:
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_TYPE, (const UINT8 *)type_pull_phone_book, TYPE_LEN_PULL_PHONE_BOOK);
break;
case BTA_PBA_CLIENT_OP_PULL_VCARD_LISTING:
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_TYPE, (const UINT8 *)type_pull_vcard_listing, TYPE_LEN_PULL_VCARD_LISTING);
break;
case BTA_PBA_CLIENT_OP_PULL_VCARD_ENTRY:
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_TYPE, (const UINT8 *)type_pull_vcard_entry, TYPE_LEN_PULL_VCARD_ENTRY);
break;
default:
break;
}
if (p_data->api_req.app_param) {
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_APP_PARAM, p_data->api_req.app_param, p_data->api_req.app_param_len);
osi_free(p_data->api_req.app_param);
}
ret |= GOEPC_SendRequest(p_ccb->goep_handle);
p_ccb->operation = p_data->api_req.operation;
if (ret != GOEP_SUCCESS) {
goto error;
}
return;
error:
if (p_data->api_req.name) {
osi_free(p_data->api_req.name);
}
if (p_data->api_req.app_param) {
osi_free(p_data->api_req.app_param);
}
close_goepc_and_report(p_ccb, BTA_PBA_CLIENT_GOEP_ERROR);
}
static void goep_event_callback(UINT16 handle, UINT8 event, tGOEPC_MSG *p_msg)
{
tBTA_PBA_CLIENT_DATA *p_data = NULL;
switch (event)
{
case GOEPC_OPENED_EVT:
p_data = (tBTA_PBA_CLIENT_DATA *)osi_malloc(sizeof(tBTA_PBA_CLIENT_GOEP_CONNECT));
assert(p_data != NULL);
p_data->goep_connect.hdr.event = BTA_PBA_CLIENT_GOEP_CONNECT_EVT;
p_data->goep_connect.hdr.layer_specific = handle;
p_data->goep_connect.our_mtu = p_msg->opened.our_mtu;
p_data->goep_connect.peer_mtu = p_msg->opened.peer_mtu;
break;
case GOEPC_CLOSED_EVT:
p_data = (tBTA_PBA_CLIENT_DATA *)osi_malloc(sizeof(tBTA_PBA_CLIENT_GOEP_DISCONNECT));
assert(p_data != NULL);
p_data->goep_disconnect.hdr.event = BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT;
p_data->goep_disconnect.hdr.layer_specific = handle;
p_data->goep_disconnect.reason = p_msg->closed.reason;
break;
case GOEPC_RESPONSE_EVT:
p_data = (tBTA_PBA_CLIENT_DATA *)osi_malloc(sizeof(tBTA_PBA_CLIENT_GOEP_RESPONSE));
assert(p_data != NULL);
p_data->goep_response.hdr.layer_specific = handle;
p_data->goep_response.pkt = p_msg->response.pkt;
p_data->goep_response.opcode = p_msg->response.opcode;
p_data->goep_response.srm_en = p_msg->response.srm_en;
p_data->goep_response.srm_wait = p_msg->response.srm_wait;
if (p_msg->response.final) {
p_data->goep_response.hdr.event = BTA_PBA_CLIENT_RESPONSE_FINAL_EVT;
}
else {
p_data->hdr.event = BTA_PBA_CLIENT_RESPONSE_EVT;
}
break;
case GOEPC_MTU_CHANGED_EVT:
case GOEPC_CONGEST_EVT:
case GOEPC_UNCONGEST_EVT:
/* ignore these event */
break;
default:
APPL_TRACE_WARNING("%s, unknown goep event: %d", __FUNCTION__, event);
break;
}
if (p_data != NULL) {
bta_sys_sendmsg(p_data);
}
}
void bta_pba_client_do_connect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
tBTA_PBA_CLIENT_ERR reason = BTA_PBA_CLIENT_FAIL;
/* find attr in sdp discovery result */
BOOLEAN found = bta_pba_client_sdp_find_attr(p_ccb);
if (found) {
tOBEX_SVR_INFO svr = {0};
if (p_ccb->peer_l2cap_psm != 0) {
/* peer support obex over l2cap, use it */
svr.tl = OBEX_OVER_L2CAP;
svr.l2cap.psm = p_ccb->peer_l2cap_psm;
svr.l2cap.sec_mask = p_ccb->sec_mask;
svr.l2cap.pref_mtu = 0;
bdcpy(svr.l2cap.addr, p_ccb->bd_addr);
}
else {
/* otherwise, use obex over rfcomm */
svr.tl = OBEX_OVER_RFCOMM;
svr.rfcomm.scn = p_ccb->peer_rfcomm_scn;
svr.rfcomm.sec_mask = p_ccb->sec_mask;
svr.rfcomm.pref_mtu = 0;
bdcpy(svr.rfcomm.addr, p_ccb->bd_addr);
}
if (GOEPC_Open(&svr, goep_event_callback, &p_ccb->goep_handle) == GOEP_SUCCESS) {
/* start connection success */
return;
}
else {
reason = BTA_PBA_CLIENT_GOEP_ERROR;
}
}
else {
reason = BTA_PBA_CLIENT_SDP_ERROR;
}
/* critical sdp attribute not found or start goep connection failed */
tBTA_PBA_CLIENT_CONN conn;
conn.handle = 0;
conn.error = reason;
bdcpy(conn.bd_addr, p_ccb->bd_addr);
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_CONN_CLOSE_EVT, (tBTA_PBA_CLIENT *)&conn);
/* free ccb */
free_ccb(p_ccb);
}
void bta_pba_client_authenticate(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
/* [todo]: support authenticate */
}
void bta_pba_client_connect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
tBTA_PBA_CLIENT_CONN conn;
conn.handle = p_ccb->allocated;
conn.peer_supported_repo = p_ccb->peer_supported_repo;
conn.peer_supported_feat = p_ccb->peer_supported_feat;
conn.error = BTA_PBA_CLIENT_NO_ERROR;
bdcpy(conn.bd_addr, p_ccb->bd_addr);
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_CONN_OPEN_EVT, (tBTA_PBA_CLIENT *)&conn);
}
void bta_pba_client_force_disconnect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
UNUSED(p_data);
/* force disconnect is requested by upper, set reason to success */
close_goepc_and_report(p_ccb, BTA_PBA_CLIENT_NO_ERROR);
}
void bta_pba_client_response(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
tOBEX_PARSE_INFO info;
tBTA_PBA_CLIENT_ERR reason = BTA_PBA_CLIENT_GOEP_ERROR;
OBEX_ParseResponse(p_data->goep_response.pkt, p_data->goep_response.opcode, &info);
if (p_data->goep_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;
UINT8 *app_param = NULL;
UINT16 app_param_len = 0;
while((header = OBEX_GetNextHeader(p_data->goep_response.pkt, &info)) != NULL) {
switch (*header)
{
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(p_ccb, body_data, body_data_len, NULL, 0, FALSE, NULL);
body_data = header + 3; /* skip opcode, length */
body_data_len = OBEX_GetHeaderLength(header) - 3;
}
break;
case OBEX_HEADER_ID_APP_PARAM:
app_param = header + 3;
app_param_len = OBEX_GetHeaderLength(header) - 3;
break;
default:
break;
}
}
if (body_data != NULL || app_param != NULL) {
/* report body data and app param, dont free packet here */
report_data_event(p_ccb, body_data, body_data_len, app_param, app_param_len, FALSE, p_data->goep_response.pkt);
}
else {
/* not any body data or app param */
osi_free(p_data->goep_response.pkt);
}
/* if SRM not enable, we need to send a empty get request */
if (!p_data->goep_response.srm_en || p_data->goep_response.srm_wait) {
build_and_send_empty_get_req(p_ccb);
}
}
else {
/* unexpected opcode or response code */
reason = calculate_response_error(info.response_code);
goto error;
}
return;
error:
if (p_data->goep_response.pkt != NULL) {
osi_free(p_data->goep_response.pkt);
}
close_goepc_and_report(p_ccb, reason);
}
void bta_pba_client_response_final(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
tOBEX_PARSE_INFO info;
UINT8 *header = NULL;
tBTA_PBA_CLIENT_ERR reason = BTA_PBA_CLIENT_FAIL;
OBEX_ParseResponse(p_data->goep_response.pkt, p_data->goep_response.opcode, &info);
if (p_data->goep_response.opcode == OBEX_OPCODE_CONNECT) {
if (info.response_code == (OBEX_RESPONSE_CODE_OK | OBEX_FINAL_BIT_MASK)) {
/* obex connect success */
if (info.max_packet_length < 255) {
p_ccb->max_tx = 255;
}
else if (p_ccb->max_tx > info.max_packet_length) {
p_ccb->max_tx = info.max_packet_length;
}
BOOLEAN cid_found = false;
while((header = OBEX_GetNextHeader(p_data->goep_response.pkt, &info)) != NULL) {
if (*header == OBEX_HEADER_ID_CONNECTION_ID) {
cid_found = true;
memcpy((UINT8 *)(&p_ccb->goep_cid), header + 1, 4);
break;
}
}
if (!cid_found) {
goto error;
}
BT_HDR *p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR));
assert(p_buf != NULL);
p_buf->event = BTA_PBA_CLIENT_CONNECT_EVT;
p_buf->layer_specific = p_ccb->allocated;
bta_sys_sendmsg(p_buf);
}
else if (info.response_code == (OBEX_RESPONSE_CODE_UNAUTHORIZED | OBEX_FINAL_BIT_MASK)){
/* need to authenticate */
if (p_ccb->authenticate) {
/* already try to authenticate, but failed */
reason = BTA_PBA_CLIENT_AUTH_FAIL;
goto error;
}
p_ccb->authenticate = TRUE;
/* [todo]: we don't support authenticate currently */
goto error;
}
else {
/* other unexpected response code */
goto error;
}
osi_free(p_data->goep_response.pkt);
}
else if (p_data->goep_response.opcode == OBEX_OPCODE_GET_FINAL) {
/* check response code is success */
if (info.response_code == (OBEX_RESPONSE_CODE_OK | OBEX_FINAL_BIT_MASK)) {
UINT8 *body_data = NULL;
UINT16 body_data_len = 0;
UINT8 *app_param = NULL;
UINT16 app_param_len = 0;
while((header = OBEX_GetNextHeader(p_data->goep_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(p_ccb, body_data, body_data_len, NULL, 0, FALSE, NULL);
body_data = header + 3; /* skip opcode, length */
body_data_len = OBEX_GetHeaderLength(header) - 3;
}
break;
case OBEX_HEADER_ID_APP_PARAM:
app_param = header + 3;
app_param_len = OBEX_GetHeaderLength(header) - 3;
break;
default:
break;
}
}
if (body_data != NULL || app_param != NULL) {
/* report body data and app param, dont free packet here */
report_data_event(p_ccb, body_data, body_data_len, app_param, app_param_len, TRUE, p_data->goep_response.pkt);
/* done, return */
return;
}
}
/* unexpected response code or body data not found */
reason = calculate_response_error(info.response_code);
report_error_data_event(p_ccb, reason);
osi_free(p_data->goep_response.pkt);
/* state machine is good, don't goto error */
}
else if (p_data->goep_response.opcode == OBEX_OPCODE_SETPATH) {
if (info.response_code == (OBEX_RESPONSE_CODE_OK | OBEX_FINAL_BIT_MASK)) {
report_data_event(p_ccb, NULL, 0, NULL, 0, TRUE, NULL);
}
else {
reason = calculate_response_error(info.response_code);
report_error_data_event(p_ccb, reason);
}
osi_free(p_data->goep_response.pkt);
}
else if (p_data->goep_response.opcode == OBEX_OPCODE_DISCONNECT) {
/* received disconnect response, close goep connection now */
reason = BTA_PBA_CLIENT_NO_ERROR;
close_goepc_and_report(p_ccb, reason);
osi_free(p_data->goep_response.pkt);
}
else {
/* unexpected opcode or response code */
reason = calculate_response_error(info.response_code);
goto error;
}
return;
error:
if (p_data->goep_response.pkt != NULL) {
osi_free(p_data->goep_response.pkt);
}
close_goepc_and_report(p_ccb, reason);
}
void bta_pba_client_goep_connect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
/* limit max rx to our goep mtu */
if (p_ccb->max_rx > p_data->goep_connect.our_mtu) {
p_ccb->max_rx = p_data->goep_connect.our_mtu;
}
/* limit max tx to peer goep mtu */
if (p_ccb->max_tx > p_data->goep_connect.peer_mtu) {
p_ccb->max_tx = p_data->goep_connect.peer_mtu;
}
/* build and send obex connect request */
tOBEX_PARSE_INFO info = {0};
info.opcode = OBEX_OPCODE_CONNECT;
info.obex_version_number = OBEX_VERSION_NUMBER;
info.max_packet_length = p_ccb->max_rx;
/* before obex connect response, we dont know the real max_tx, use BT_SMALL_BUFFER_SIZE as tx buff size */
UINT16 ret = GOEPC_PrepareRequest(p_ccb->goep_handle, &info, BT_SMALL_BUFFER_SIZE);
/* add target header */
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_TARGET, pbap_target_uuid, 16);
if (p_ccb->send_supported_feat) {
/* add application parameters with supported features */
UINT8 app_param[6];
app_param[0] = BTA_PBAP_APP_PARAM_PBAP_SUPPORTED_FEATURES;
app_param[1] = BTA_PBAP_APP_PARAM_LENGTH_PBAP_SUPPORTED_FEATURES;
UINT32_TO_FIELD(&app_param[2], p_ccb->our_supported_feat);
ret |= GOEPC_RequestAddHeader(p_ccb->goep_handle, OBEX_HEADER_ID_APP_PARAM, app_param, 6);
}
ret |= GOEPC_SendRequest(p_ccb->goep_handle);
if (ret != GOEP_SUCCESS) {
close_goepc_and_report(p_ccb, BTA_PBA_CLIENT_GOEP_ERROR);
}
}
void bta_pba_client_goep_disconnect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
p_ccb->goep_handle = 0;
/* report connection closed event */
tBTA_PBA_CLIENT_CONN conn;
conn.handle = p_ccb->allocated;
bdcpy(conn.bd_addr, p_ccb->bd_addr);
conn.error = BTA_PBA_CLIENT_GOEP_ERROR;
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_CONN_CLOSE_EVT, (tBTA_PBA_CLIENT *)&conn);
free_ccb(p_ccb);
}
void bta_pba_client_free_response(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data)
{
if (p_data->goep_response.pkt != NULL) {
osi_free(p_data->goep_response.pkt);
}
close_goepc_and_report(p_ccb, BTA_PBA_CLIENT_GOEP_ERROR);
}
#endif

View File

@@ -0,0 +1,161 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdio.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 "bta/bta_sys.h"
#include "bta/bta_api.h"
#include "bta_pba_client_int.h"
#if BTA_PBA_CLIENT_INCLUDED
static const tBTA_SYS_REG bta_pba_client_reg = {
bta_pba_client_hdl_event,
BTA_PbaClientDisable
};
void BTA_PbaClientEnable(tBTA_PBA_CLIENT_CBACK *p_cback)
{
tBTA_PBA_CLIENT_API_ENABLE *p_buf;
if (bta_sys_is_register(BTA_ID_PBC)) {
APPL_TRACE_ERROR("BTA PBA Client already enabled");
return;
}
/* register with BTA system manager */
bta_sys_register(BTA_ID_PBC, &bta_pba_client_reg);
if ((p_buf = (tBTA_PBA_CLIENT_API_ENABLE *)osi_malloc(sizeof(tBTA_PBA_CLIENT_API_ENABLE))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_API_ENABLE_EVT;
p_buf->p_cback = p_cback;
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientDisable(void)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_PBA_CLIENT_API_DISABLE_EVT;
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientRegister(const char *server_name)
{
tBTA_PBA_CLIENT_API_REGISTER *p_buf;
if ((p_buf = (tBTA_PBA_CLIENT_API_REGISTER *) osi_malloc(sizeof(tBTA_PBA_CLIENT_API_REGISTER))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_API_REGISTER_EVT;
memcpy(p_buf->name, server_name, strlen(server_name) + 1);
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientDeregister(void)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_PBA_CLIENT_API_DEREGISTER_EVT;
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientOpen(BD_ADDR bd_addr, tBTA_SEC sec_mask, UINT32 supported_feat, UINT16 mtu)
{
tBTA_PBA_CLIENT_API_OPEN *p_buf;
if ((p_buf = (tBTA_PBA_CLIENT_API_OPEN *)osi_malloc(sizeof(tBTA_PBA_CLIENT_API_OPEN))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_API_OPEN_EVT;
p_buf->sec_mask = sec_mask;
p_buf->mtu = mtu;
bdcpy(p_buf->bd_addr, bd_addr);
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientClose(UINT16 handle)
{
BT_HDR *p_buf;
if ((p_buf = (BT_HDR *) osi_malloc(sizeof(BT_HDR))) != NULL) {
p_buf->event = BTA_PBA_CLIENT_API_CLOSE_EVT;
p_buf->layer_specific = handle;
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientPullPhoneBook(UINT16 handle, char *name, UINT8 *app_param, UINT16 app_param_len)
{
tBTA_PBA_CLIENT_API_REQ *p_buf;
if ((p_buf = (tBTA_PBA_CLIENT_API_REQ *) osi_malloc(sizeof(tBTA_PBA_CLIENT_API_REQ))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_API_REQ_EVT;
p_buf->hdr.layer_specific = handle;
p_buf->operation = BTA_PBA_CLIENT_OP_PULL_PHONE_BOOK;
p_buf->name = name;
p_buf->app_param = app_param;
p_buf->app_param_len = app_param_len;
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientSetPhoneBook(UINT16 handle, UINT8 flags, char *name)
{
tBTA_PBA_CLIENT_API_REQ *p_buf;
if ((p_buf = (tBTA_PBA_CLIENT_API_REQ *) osi_malloc(sizeof(tBTA_PBA_CLIENT_API_REQ))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_API_REQ_EVT;
p_buf->hdr.layer_specific = handle;
p_buf->operation = BTA_PBA_CLIENT_OP_SET_PHONE_BOOK;
p_buf->flags = flags;
p_buf->name = name;
p_buf->app_param = NULL;
p_buf->app_param_len = 0;
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientPullvCardListing(UINT16 handle, char *name, UINT8 *app_param, UINT16 app_param_len)
{
tBTA_PBA_CLIENT_API_REQ *p_buf;
if ((p_buf = (tBTA_PBA_CLIENT_API_REQ *) osi_malloc(sizeof(tBTA_PBA_CLIENT_API_REQ))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_API_REQ_EVT;
p_buf->hdr.layer_specific = handle;
p_buf->operation = BTA_PBA_CLIENT_OP_PULL_VCARD_LISTING;
p_buf->name = name;
p_buf->app_param = app_param;
p_buf->app_param_len = app_param_len;
bta_sys_sendmsg(p_buf);
}
}
void BTA_PbaClientPullvCardEntry(UINT16 handle, char *name, UINT8 *app_param, UINT16 app_param_len)
{
tBTA_PBA_CLIENT_API_REQ *p_buf;
if ((p_buf = (tBTA_PBA_CLIENT_API_REQ *) osi_malloc(sizeof(tBTA_PBA_CLIENT_API_REQ))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_API_REQ_EVT;
p_buf->hdr.layer_specific = handle;
p_buf->operation = BTA_PBA_CLIENT_OP_PULL_VCARD_ENTRY;
p_buf->name = name;
p_buf->app_param = app_param;
p_buf->app_param_len = app_param_len;
bta_sys_sendmsg(p_buf);
}
}
#endif

View File

@@ -0,0 +1,293 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.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 "bta_pba_client_int.h"
#if BTA_PBA_CLIENT_INCLUDED
/* state machine states */
enum {
BTA_PBA_CLIENT_INIT_ST,
BTA_PBA_CLIENT_OPENING_ST,
BTA_PBA_CLIENT_OPENED_ST,
BTA_PBA_CLIENT_REQUESTING_ST,
BTA_PBA_CLIENT_CLOSING_ST
};
/* state machine action enumeration list */
enum {
BTA_PBA_CLIENT_API_OPEN,
BTA_PBA_CLIENT_API_CLOSE,
BTA_PBA_CLIENT_API_REQ,
BTA_PBA_CLIENT_DO_CONNECT,
BTA_PBA_CLIENT_AUTHENTICATE,
BTA_PBA_CLIENT_CONNECT,
BTA_PBA_CLIENT_RESPONSE,
BTA_PBA_CLIENT_RESPONSE_FINAL,
BTA_PBA_CLIENT_GOEP_CONNECT,
BTA_PBA_CLIENT_GOEP_DISCONNECT,
BTA_PBA_CLIENT_FORCE_DISCONNECT,
BTA_PBA_CLIENT_FREE_RESPONSE,
BTA_PBA_CLIENT_NUM_ACTIONS
};
#define BTA_PBA_CLIENT_IGNORE BTA_PBA_CLIENT_NUM_ACTIONS
/* type for action functions */
typedef void (*tBTA_PBA_CLIENT_ACTION)(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
/* action functions table, indexed with action enum */
const tBTA_PBA_CLIENT_ACTION bta_pba_client_action[] = {
/* BTA_PBA_CLIENT_API_OPEN */ bta_pba_client_api_open,
/* BTA_PBA_CLIENT_API_CLOSE */ bta_pba_client_api_close,
/* BTA_PBA_CLIENT_API_REQ */ bta_pba_client_api_req,
/* BTA_PBA_CLIENT_DO_CONNECT */ bta_pba_client_do_connect,
/* BTA_PBA_CLIENT_AUTHENTICATE */ bta_pba_client_authenticate,
/* BTA_PBA_CLIENT_CONNECT */ bta_pba_client_connect,
/* BTA_PBA_CLIENT_RESPONSE */ bta_pba_client_response,
/* BTA_PBA_CLIENT_RESPONSE_FINAL */ bta_pba_client_response_final,
/* BTA_PBA_CLIENT_GOEP_CONNECT */ bta_pba_client_goep_connect,
/* BTA_PBA_CLIENT_GOEP_DISCONNECT*/ bta_pba_client_goep_disconnect,
/* BTA_PBA_CLIENT_FORCE_DISCONNECT */ bta_pba_client_force_disconnect,
/* BTA_PBA_CLIENT_FREE_RESPONSE */ bta_pba_client_free_response,
};
/* state table information */
#define BTA_PBA_CLIENT_ACTION 0 /* position of action */
#define BTA_PBA_CLIENT_NEXT_STATE 1 /* position of next state */
#define BTA_PBA_CLIENT_NUM_COLS 2 /* number of columns */
const uint8_t bta_pba_client_st_init[][BTA_PBA_CLIENT_NUM_COLS] = {
/* Event Action Next state */
/* BTA_PBA_CLIENT_API_OPEN_EVT */ {BTA_PBA_CLIENT_API_OPEN, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_API_CLOSE_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_API_REQ_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_DISC_RES_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_AUTHENTICATE_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_RESPONSE_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_RESPONSE_FINAL_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_GOEP_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_INIT_ST},
};
const uint8_t bta_pba_client_st_opening[][BTA_PBA_CLIENT_NUM_COLS] = {
/* Event Action Next state */
/* BTA_PBA_CLIENT_API_OPEN_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_API_CLOSE_EVT */ {BTA_PBA_CLIENT_FORCE_DISCONNECT, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_API_REQ_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_DISC_RES_EVT */ {BTA_PBA_CLIENT_DO_CONNECT, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_AUTHENTICATE_EVT */ {BTA_PBA_CLIENT_AUTHENTICATE, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_CONNECT_EVT */ {BTA_PBA_CLIENT_CONNECT, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_RESPONSE_EVT */ {BTA_PBA_CLIENT_FREE_RESPONSE, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_RESPONSE_FINAL_EVT */ {BTA_PBA_CLIENT_RESPONSE_FINAL, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_GOEP_CONNECT_EVT */ {BTA_PBA_CLIENT_GOEP_CONNECT, BTA_PBA_CLIENT_OPENING_ST},
/* BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT */ {BTA_PBA_CLIENT_GOEP_DISCONNECT, BTA_PBA_CLIENT_INIT_ST},
};
const uint8_t bta_pba_client_st_opened[][BTA_PBA_CLIENT_NUM_COLS] = {
/* Event Action Next state */
/* BTA_PBA_CLIENT_API_OPEN_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_API_CLOSE_EVT */ {BTA_PBA_CLIENT_API_CLOSE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_API_REQ_EVT */ {BTA_PBA_CLIENT_API_REQ, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_DISC_RES_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_AUTHENTICATE_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_RESPONSE_EVT */ {BTA_PBA_CLIENT_FREE_RESPONSE, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_RESPONSE_FINAL_EVT */ {BTA_PBA_CLIENT_FREE_RESPONSE, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_GOEP_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT */ {BTA_PBA_CLIENT_GOEP_DISCONNECT, BTA_PBA_CLIENT_INIT_ST},
};
const uint8_t bta_pba_client_st_getting[][BTA_PBA_CLIENT_NUM_COLS] = {
/* Event Action Next state */
/* BTA_PBA_CLIENT_API_OPEN_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_API_CLOSE_EVT */ {BTA_PBA_CLIENT_FORCE_DISCONNECT, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_API_REQ_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_DISC_RES_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_AUTHENTICATE_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_RESPONSE_EVT */ {BTA_PBA_CLIENT_RESPONSE, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_RESPONSE_FINAL_EVT */ {BTA_PBA_CLIENT_RESPONSE_FINAL, BTA_PBA_CLIENT_OPENED_ST},
/* BTA_PBA_CLIENT_GOEP_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_REQUESTING_ST},
/* BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT */ {BTA_PBA_CLIENT_GOEP_DISCONNECT, BTA_PBA_CLIENT_INIT_ST},
};
const uint8_t bta_pba_client_st_closing[][BTA_PBA_CLIENT_NUM_COLS] = {
/* Event Action Next state */
/* BTA_PBA_CLIENT_API_OPEN_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_API_CLOSE_EVT */ {BTA_PBA_CLIENT_FORCE_DISCONNECT, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_API_REQ_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_DISC_RES_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_AUTHENTICATE_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_RESPONSE_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_RESPONSE_FINAL_EVT */ {BTA_PBA_CLIENT_RESPONSE_FINAL, BTA_PBA_CLIENT_INIT_ST},
/* BTA_PBA_CLIENT_GOEP_CONNECT_EVT */ {BTA_PBA_CLIENT_IGNORE, BTA_PBA_CLIENT_CLOSING_ST},
/* BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT */ {BTA_PBA_CLIENT_GOEP_DISCONNECT, BTA_PBA_CLIENT_INIT_ST},
};
/* type for state table */
typedef const UINT8 (*tBTA_PBA_CLIENT_ST_TBL)[BTA_PBA_CLIENT_NUM_COLS];
/* state table */
const tBTA_PBA_CLIENT_ST_TBL bta_pba_client_st_tbl[] = {
bta_pba_client_st_init,
bta_pba_client_st_opening,
bta_pba_client_st_opened,
bta_pba_client_st_getting,
bta_pba_client_st_closing
};
/* PBA Client control block */
#if BTA_DYNAMIC_MEMORY == FALSE
tBTA_PBA_CLIENT_CB bta_pba_client_cb;
#else
tBTA_PBA_CLIENT_CB *bta_pba_client_cb_ptr;
#endif
static tBTA_PBA_CLIENT_CCB *allocate_ccb(void)
{
tBTA_PBA_CLIENT_CCB *p_ccb = NULL;
for (int i = 0; i < PBA_CLIENT_MAX_CONNECTION; ++i) {
if (bta_pba_client_cb.ccb[i].allocated == 0) {
bta_pba_client_cb.ccb[i].allocated = i + 1;
p_ccb = &bta_pba_client_cb.ccb[i];
break;
}
}
return p_ccb;
}
static tBTA_PBA_CLIENT_CCB *find_ccb_by_handle(UINT16 handle)
{
tBTA_PBA_CLIENT_CCB *p_ccb = NULL;
for (int i = 0; i < PBA_CLIENT_MAX_CONNECTION; ++i) {
if (bta_pba_client_cb.ccb[i].allocated != 0 && bta_pba_client_cb.ccb[i].allocated == handle) {
p_ccb = &bta_pba_client_cb.ccb[i];
}
}
return p_ccb;
}
static tBTA_PBA_CLIENT_CCB *find_ccb_by_goep_handle(UINT16 goep_handle)
{
tBTA_PBA_CLIENT_CCB *p_ccb = NULL;
for (int i = 0; i < PBA_CLIENT_MAX_CONNECTION; ++i) {
if (bta_pba_client_cb.ccb[i].allocated != 0 && bta_pba_client_cb.ccb[i].goep_handle == goep_handle) {
p_ccb = &bta_pba_client_cb.ccb[i];
}
}
return p_ccb;
}
static tBTA_PBA_CLIENT_CCB *find_ccb_by_bd_addr(BD_ADDR bd_addr)
{
tBTA_PBA_CLIENT_CCB *p_ccb = NULL;
for (int i = 0; i < PBA_CLIENT_MAX_CONNECTION; ++i) {
if (bta_pba_client_cb.ccb[i].allocated != 0 && bdcmp(bta_pba_client_cb.ccb[i].bd_addr, bd_addr) == 0) {
p_ccb = &bta_pba_client_cb.ccb[i];
}
}
return p_ccb;
}
void bta_pba_client_sm_execute(tBTA_PBA_CLIENT_CCB *p_ccb, UINT16 event, tBTA_PBA_CLIENT_DATA *p_data)
{
tBTA_PBA_CLIENT_ST_TBL state_table;
UINT8 action;
state_table = bta_pba_client_st_tbl[p_ccb->state];
event &= 0xff;
p_ccb->state = state_table[event][BTA_PBA_CLIENT_NEXT_STATE];
if ((action = state_table[event][BTA_PBA_CLIENT_ACTION]) != BTA_PBA_CLIENT_IGNORE) {
(*bta_pba_client_action[action])(p_ccb, p_data);
}
return;
}
BOOLEAN bta_pba_client_hdl_event(BT_HDR *p_msg)
{
tBTA_PBA_CLIENT_CCB *p_ccb = NULL;
BOOLEAN execute_sm = FALSE;
tBTA_PBA_CLIENT_CONN conn = {0};
tBTA_PBA_CLIENT_DATA *p_data = (tBTA_PBA_CLIENT_DATA *)p_msg;
switch (p_msg->event) {
case BTA_PBA_CLIENT_API_ENABLE_EVT:
bta_pba_client_api_enable(p_data);
break;
case BTA_PBA_CLIENT_API_DISABLE_EVT:
bta_pba_client_api_disable(p_data);
break;
case BTA_PBA_CLIENT_API_REGISTER_EVT:
bta_pba_client_api_register(p_data);
break;
case BTA_PBA_CLIENT_API_DEREGISTER_EVT:
bta_pba_client_api_deregister(p_data);
break;
case BTA_PBA_CLIENT_API_OPEN_EVT:
if (find_ccb_by_bd_addr(p_data->api_open.bd_addr) != NULL) {
/* already connected */
conn.handle = 0;
conn.error = BTA_PBA_CLIENT_ALREADY_CONN;
bdcpy(conn.bd_addr, p_data->api_open.bd_addr);
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_CONN_OPEN_EVT, (tBTA_PBA_CLIENT *)&conn);
/* break, don't execute sm */
break;
}
p_ccb = allocate_ccb();
if (p_ccb == NULL) {
/* no resource to allocate ccb */
conn.handle = 0;
conn.error = BTA_PBA_CLIENT_NO_RESOURCE;
bdcpy(conn.bd_addr, p_data->api_open.bd_addr);
bta_pba_client_cb.p_cback(BTA_PBA_CLIENT_CONN_OPEN_EVT, (tBTA_PBA_CLIENT *)&conn);
/* break, don't execute sm */
break;
}
execute_sm = TRUE;
break;
case BTA_PBA_CLIENT_GOEP_CONNECT_EVT:
case BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT:
case BTA_PBA_CLIENT_RESPONSE_EVT:
case BTA_PBA_CLIENT_RESPONSE_FINAL_EVT:
p_ccb = find_ccb_by_goep_handle(p_msg->layer_specific);
if (p_ccb == NULL) {
/* ignore event with invalid goep handle */
break;
}
execute_sm = TRUE;
break;
default:
p_ccb = find_ccb_by_handle(p_msg->layer_specific);
if (p_ccb == NULL) {
/* ignore event with invalid handle */
break;
}
execute_sm = TRUE;
}
if (execute_sm) {
bta_pba_client_sm_execute(p_ccb, p_msg->event, (tBTA_PBA_CLIENT_DATA *) p_msg);
}
return TRUE;
}
#endif

View File

@@ -0,0 +1,280 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "osi/allocator.h"
#include "common/bt_defs.h"
#include "stack/sdp_api.h"
#include "bta/bta_api.h"
#include "bta/bta_pba_defs.h"
#include "bta/bta_pba_client_api.h"
#include "bta_pba_client_int.h"
#if BTA_PBA_CLIENT_INCLUDED
/* Number of elements in service class id list. */
#define BTA_PBA_CLIENT_NUM_SVC_ELEMS 1
/*******************************************************************************
**
** Function bta_pba_client_sdp_cback
**
** Description SDP callback function.
**
**
** Returns void
**
*******************************************************************************/
static void bta_pba_client_sdp_cback(UINT16 status, void *user_data)
{
tBTA_PBA_CLIENT_DISC_RESULT *p_buf;
tBTA_PBA_CLIENT_CCB *p_ccb = (tBTA_PBA_CLIENT_CCB *)user_data;
APPL_TRACE_DEBUG("bta_pba_client_sdp_cback status:0x%x", status);
if ((p_buf = (tBTA_PBA_CLIENT_DISC_RESULT *) osi_malloc(sizeof(tBTA_PBA_CLIENT_DISC_RESULT))) != NULL) {
p_buf->hdr.event = BTA_PBA_CLIENT_DISC_RES_EVT;
p_buf->hdr.layer_specific = p_ccb->allocated;
p_buf->status = status;
bta_sys_sendmsg(p_buf);
}
}
/******************************************************************************
**
** Function bta_pba_client_add_record
**
** Description Add PBA Client information to an SDP record. Prior to
** calling this function the application must call
** SDP_CreateRecord() to create an SDP record.
**
** Returns TRUE if function execution succeeded,
** FALSE if function execution failed.
**
******************************************************************************/
static BOOLEAN bta_pba_client_add_record(const char *p_service_name, UINT32 sdp_handle)
{
UINT16 svc_class_id_list[BTA_PBA_CLIENT_NUM_SVC_ELEMS];
UINT16 version;
UINT16 profile_uuid;
BOOLEAN result = TRUE;
APPL_TRACE_DEBUG("bta_pba_client_add_record");
/* add service class id list */
svc_class_id_list[0] = UUID_SERVCLASS_PBAP_PCE;
result &= SDP_AddServiceClassIdList(sdp_handle, BTA_PBA_CLIENT_NUM_SVC_ELEMS, svc_class_id_list);
/* add service name */
if (p_service_name != NULL && p_service_name[0] != 0) {
result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE,
(UINT32)(strlen(p_service_name) + 1), (UINT8 *) p_service_name);
}
/* add profile descriptor list */
profile_uuid = UUID_SERVCLASS_PHONE_ACCESS;
version = PBAP_PCE_VERSION;
result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version);
return result;
}
/*******************************************************************************
**
** Function bta_pba_client_create_record
**
** Description Create SDP record for registered service.
**
**
** Returns void
**
*******************************************************************************/
void bta_pba_client_create_record(const char *p_service_name)
{
/* add sdp record if not already registered */
if (bta_pba_client_cb.sdp_handle == 0) {
bta_pba_client_cb.sdp_handle = SDP_CreateRecord();
bta_pba_client_add_record(p_service_name, bta_pba_client_cb.sdp_handle);
bta_sys_add_uuid(UUID_SERVCLASS_PBAP_PCE);
}
}
/*******************************************************************************
**
** Function bta_pba_client_del_record
**
** Description Delete SDP record for registered service.
**
**
** Returns void
**
*******************************************************************************/
void bta_pba_client_del_record(void)
{
APPL_TRACE_DEBUG("bta_pba_client_del_record");
if (bta_pba_client_cb.sdp_handle != 0) {
SDP_DeleteRecord(bta_pba_client_cb.sdp_handle);
bta_pba_client_cb.sdp_handle = 0;
bta_sys_remove_uuid(UUID_SERVCLASS_PBAP_PCE);
}
}
/*******************************************************************************
**
** Function bta_pba_client_sdp_find_attr
**
** Description Process SDP discovery results to find requested attribute
**
**
** Returns TRUE if results found, FALSE otherwise.
**
*******************************************************************************/
BOOLEAN bta_pba_client_sdp_find_attr(tBTA_PBA_CLIENT_CCB *p_ccb)
{
tSDP_DISC_REC *p_rec = NULL;
tSDP_DISC_ATTR *p_attr;
tSDP_PROTOCOL_ELEM pe;
BOOLEAN result = FALSE;
/* loop through all records we found */
while (TRUE) {
/* get next record; if none found, we're done */
if ((p_rec = SDP_FindServiceInDb(p_ccb->p_disc_db, UUID_SERVCLASS_PBAP_PSE, p_rec)) == NULL) {
break;
}
/* get rfcomm scn from proto desc list */
if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
p_ccb->peer_rfcomm_scn = (UINT8) pe.params[0];
}
else {
/* not found, go to next record */
continue;
}
/* get supported repositories */
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_REPOSITORIES)) != NULL) {
/* Found attribute, get value */
p_ccb->peer_supported_repo = p_attr->attr_value.v.u8;
}
else {
/* not found, clear rfcomm scn and go to next record */
p_ccb->peer_rfcomm_scn = 0;
continue;
}
/* get profile version */
SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_PHONE_ACCESS, &p_ccb->peer_version);
/* get GOEP L2CAP PSM */
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_GOEP_L2CAP_PSM)) != NULL) {
/* Found attribute, get value */
p_ccb->peer_l2cap_psm = p_attr->attr_value.v.u16;
}
/* try to get supported features */
if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_PBAP_SUPPORTED_FEATURES)) != NULL) {
/* found attribute, get value */
p_ccb->peer_supported_feat = p_attr->attr_value.v.u32;
p_ccb->send_supported_feat = TRUE;
}
else {
/* assume as default value if not found in sdp record */
p_ccb->peer_supported_feat = BTA_PBAP_DEFAULT_SUPPORTED_FEATURES;
p_ccb->send_supported_feat = FALSE;
}
/* found what we needed */
result = TRUE;
break;
}
APPL_TRACE_DEBUG("%s peer_version:0x%x, supported repositories:0x%x, supported features:0x%x",
__FUNCTION__, p_ccb->peer_version, p_ccb->peer_supported_repo, p_ccb->peer_supported_feat);
return result;
}
/*******************************************************************************
**
** Function bta_pba_client_do_disc
**
** Description Do service discovery.
**
**
** Returns TRUE if start service discovery successfully
**
*******************************************************************************/
BOOLEAN bta_pba_client_do_disc(tBTA_PBA_CLIENT_CCB *p_ccb)
{
tSDP_UUID uuid_list[1];
UINT16 num_uuid = 1;
UINT16 attr_list[4];
UINT8 num_attr;
BOOLEAN db_inited = FALSE;
/* get proto list and features */
attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST;
attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST;
attr_list[3] = ATTR_ID_GOEP_L2CAP_PSM;
attr_list[4] = ATTR_ID_SUPPORTED_REPOSITORIES;
attr_list[5] = ATTR_ID_PBAP_SUPPORTED_FEATURES;
num_attr = 6;
uuid_list[0].uu.uuid16 = UUID_SERVCLASS_PBAP_PSE;
uuid_list[0].len = LEN_UUID_16;
if (p_ccb->p_disc_db != NULL) {
APPL_TRACE_WARNING("%s service discovery already in progress", __FUNCTION__);
return FALSE;
}
/* allocate buffer for sdp database */
p_ccb->p_disc_db = (tSDP_DISCOVERY_DB *) osi_malloc(BT_DEFAULT_BUFFER_SIZE);
if (p_ccb->p_disc_db) {
/* set up service discovery database; attr happens to be attr_list len */
db_inited = SDP_InitDiscoveryDb(p_ccb->p_disc_db, BT_DEFAULT_BUFFER_SIZE, num_uuid,
uuid_list, num_attr, attr_list);
}
if (db_inited) {
/*start service discovery */
/* todo: avoid p_ccb being free during sdp */
db_inited = SDP_ServiceSearchAttributeRequest2(p_ccb->bd_addr, p_ccb->p_disc_db,
bta_pba_client_sdp_cback, p_ccb);
}
if (!db_inited) {
/*free discover db */
bta_pba_client_free_db(p_ccb);
APPL_TRACE_ERROR("%s start service discovery failed", __FUNCTION__);
return FALSE;
}
return TRUE;
}
/*******************************************************************************
**
** Function bta_hf_client_free_db
**
** Description Free discovery database.
**
**
** Returns void
**
*******************************************************************************/
void bta_pba_client_free_db(tBTA_PBA_CLIENT_CCB *p_ccb)
{
if (p_ccb->p_disc_db != NULL) {
osi_free(p_ccb->p_disc_db);
p_ccb->p_disc_db = NULL;
}
}
#endif

View File

@@ -0,0 +1,177 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "osi/list.h"
#include "common/bt_target.h"
#include "stack/sdp_api.h"
#include "stack/obex_api.h"
#include "bta/bta_sys.h"
#include "bta/bta_api.h"
#include "bta/bta_pba_client_api.h"
#if BTA_PBA_CLIENT_INCLUDED
#define PBA_CLIENT_MAX_CONNECTION 2
#define PBA_CLIENT_MAX_MTU L2CAP_MTU_SIZE /* RFCOMM is base on L2CAP, its MTU will smaller than this */
#define PBA_CLIENT_MIN_MTU 255
enum {
/* these events are handled by the state machine */
BTA_PBA_CLIENT_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_PBC),
BTA_PBA_CLIENT_API_CLOSE_EVT,
BTA_PBA_CLIENT_API_REQ_EVT,
BTA_PBA_CLIENT_DISC_RES_EVT,
BTA_PBA_CLIENT_AUTHENTICATE_EVT,
BTA_PBA_CLIENT_CONNECT_EVT,
BTA_PBA_CLIENT_RESPONSE_EVT,
BTA_PBA_CLIENT_RESPONSE_FINAL_EVT,
BTA_PBA_CLIENT_GOEP_CONNECT_EVT,
BTA_PBA_CLIENT_GOEP_DISCONNECT_EVT,
/* these events are handled outside of the state machine */
BTA_PBA_CLIENT_API_ENABLE_EVT,
BTA_PBA_CLIENT_API_DISABLE_EVT,
BTA_PBA_CLIENT_API_REGISTER_EVT,
BTA_PBA_CLIENT_API_DEREGISTER_EVT,
};
typedef enum {
BTA_PBA_CLIENT_OP_PULL_PHONE_BOOK,
BTA_PBA_CLIENT_OP_SET_PHONE_BOOK,
BTA_PBA_CLIENT_OP_PULL_VCARD_LISTING,
BTA_PBA_CLIENT_OP_PULL_VCARD_ENTRY,
} tBTA_PBA_CLIENT_OP;
typedef struct {
BT_HDR hdr;
tBTA_PBA_CLIENT_CBACK *p_cback;
} tBTA_PBA_CLIENT_API_ENABLE;
typedef struct {
BT_HDR hdr;
char name[BTA_SERVICE_NAME_LEN + 1];
} tBTA_PBA_CLIENT_API_REGISTER;
typedef struct {
BT_HDR hdr;
BD_ADDR bd_addr;
tBTA_SEC sec_mask;
UINT16 mtu;
UINT32 supported_feat;
} tBTA_PBA_CLIENT_API_OPEN;
typedef struct {
BT_HDR hdr;
UINT8 operation;
UINT8 flags;
char *name;
UINT16 app_param_len;
UINT8 *app_param;
} tBTA_PBA_CLIENT_API_REQ;
typedef struct {
BT_HDR hdr;
UINT16 status;
} tBTA_PBA_CLIENT_DISC_RESULT;
typedef struct {
BT_HDR hdr;
UINT16 our_mtu;
UINT16 peer_mtu;
} tBTA_PBA_CLIENT_GOEP_CONNECT;
typedef struct {
BT_HDR hdr;
UINT16 reason;
} tBTA_PBA_CLIENT_GOEP_DISCONNECT;
typedef struct {
BT_HDR hdr;
BT_HDR *pkt;
UINT8 opcode;
BOOLEAN srm_en;
BOOLEAN srm_wait;
} tBTA_PBA_CLIENT_GOEP_RESPONSE;
typedef union {
BT_HDR hdr;
tBTA_PBA_CLIENT_API_ENABLE api_enable;
tBTA_PBA_CLIENT_API_REGISTER api_register;
tBTA_PBA_CLIENT_API_OPEN api_open;
tBTA_PBA_CLIENT_API_REQ api_req;
tBTA_PBA_CLIENT_DISC_RESULT disc_result;
tBTA_PBA_CLIENT_GOEP_CONNECT goep_connect;
tBTA_PBA_CLIENT_GOEP_DISCONNECT goep_disconnect;
tBTA_PBA_CLIENT_GOEP_RESPONSE goep_response;
} tBTA_PBA_CLIENT_DATA;
typedef struct {
BD_ADDR bd_addr; /* peer BD address */
tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */
tBTA_SEC sec_mask; /* security mask */
UINT16 peer_version; /* peer profile version */
UINT16 peer_l2cap_psm; /* peer l2cap psm */
UINT8 peer_rfcomm_scn; /* peer rfcomm scn */
UINT8 peer_supported_repo; /* peer supported repositories */
UINT32 peer_supported_feat; /* peer supported features */
UINT32 our_supported_feat; /* we supported features */
BOOLEAN send_supported_feat; /* whether we should send supported features in connect request */
UINT16 goep_handle; /* goep connection handle */
UINT32 goep_cid; /* goep connection id */
UINT16 max_rx; /* max rx bytes */
UINT16 max_tx; /* max tx bytes */
BOOLEAN authenticate; /* whether we are authenticated */
tBTA_PBA_CLIENT_OP operation; /* ongoing or last operations */
UINT8 state; /* main state machine */
UINT8 allocated; /* index + 1 if allocated, otherwise 0 */
} tBTA_PBA_CLIENT_CCB;
typedef struct {
tBTA_PBA_CLIENT_CCB ccb[PBA_CLIENT_MAX_CONNECTION]; /* connection control block */
tBTA_PBA_CLIENT_CBACK *p_cback; /* message callback to upper */
UINT32 sdp_handle; /* sdp record handle */
UINT8 trace_level; /* debug trace level */
} tBTA_PBA_CLIENT_CB;
#if BTA_DYNAMIC_MEMORY == FALSE
extern tBTA_PBA_CLIENT_CB bta_pba_client_cb;
#else
extern tBTA_PBA_CLIENT_CB *bta_pba_client_cb_ptr;
#define bta_pba_client_cb (*bta_pba_client_cb_ptr)
#endif
extern void bta_pba_client_api_enable(tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_api_disable(tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_api_register(tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_api_deregister(tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_api_open(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_api_close(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_api_req(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_do_connect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_authenticate(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_force_disconnect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_connect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_response(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_response_final(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
void bta_pba_client_goep_connect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
void bta_pba_client_goep_disconnect(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
void bta_pba_client_free_response(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
void bta_pba_client_free_sdp_db(tBTA_PBA_CLIENT_CCB *p_ccb, tBTA_PBA_CLIENT_DATA *p_data);
extern void bta_pba_client_create_record(const char *p_service_name);
extern void bta_pba_client_del_record(void);
extern BOOLEAN bta_pba_client_sdp_find_attr(tBTA_PBA_CLIENT_CCB *p_ccb);
extern BOOLEAN bta_pba_client_do_disc(tBTA_PBA_CLIENT_CCB *p_ccb);
extern void bta_pba_client_free_db(tBTA_PBA_CLIENT_CCB *p_ccb);
extern void bta_pba_client_sm_execute(tBTA_PBA_CLIENT_CCB *p_ccb, UINT16 event, tBTA_PBA_CLIENT_DATA *p_data);
extern BOOLEAN bta_pba_client_hdl_event(BT_HDR *p_msg);
#endif

View File

@@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "common/bt_defs.h"
#include "esp_pbac_api.h"
#if BTC_PBA_CLIENT_INCLUDED
typedef enum {
BTC_PBA_CLIENT_INIT_EVT = 0,
BTC_PBA_CLIENT_DEINIT_EVT,
BTC_PBA_CLIENT_CONNECT_EVT,
BTC_PBA_CLIENT_DISCONNECT_EVT,
BTC_PBA_CLIENT_PULL_PHONE_BOOK_EVT,
BTC_PBA_CLIENT_SET_PHONE_BOOK_EVT,
BTC_PBA_CLIENT_SET_PHONE_BOOK2_EVT,
BTC_PBA_CLIENT_PULL_VCARD_LISTING_EVT,
BTC_PBA_CLIENT_PULL_VCARD_ENTRY_EVT
} BTC_PBA_CLIENT_EVT;
/* equal to BTA max conn */
#define BTC_PBA_CLIENT_MAX_CONN_NUM 2
typedef struct {
uint16_t handle;
bt_bdaddr_t bd_addr;
char *path;
uint16_t path_len;
uint16_t path_pos;
bool busy;
} btc_pba_client_ccb_t;
typedef struct {
btc_pba_client_ccb_t ccb[BTC_PBA_CLIENT_MAX_CONN_NUM];
} btc_pba_client_cb_t;
typedef union {
// BTC_PBA_CLIENT_CONNECT_EVT
struct pba_client_connect_arg {
bt_bdaddr_t bd_addr;
} connect;
// BTC_PBA_CLIENT_DISCONNECT_EVT
struct pba_client_disconnect_arg {
uint16_t handle;
} disconnect;
// BTC_PBA_CLIENT_PULL_PHONE_BOOK_EVT
struct pba_client_pull_phone_book_arg {
uint16_t handle;
char *name;
bool include_app_param;
esp_pbac_pull_phone_book_app_param_t app_param;
} pull_phone_book;
// BTC_PBA_CLIENT_SET_PHONE_BOOK_EVT
struct pba_client_set_phone_book_arg {
uint16_t handle;
uint8_t flags;
char *name;
} set_phone_book;
// BTC_PBA_CLIENT_PULL_VCARD_LISTING_EVT
struct pba_client_pull_vcard_listing_arg {
uint16_t handle;
char *name;
bool include_app_param;
esp_pbac_pull_vcard_listing_app_param_t app_param;
} pull_vcard_listing;
// BTC_PBA_CLIENT_PULL_VCARD_ENTRY_EVT
struct pba_client_pull_vcard_entry_arg {
uint16_t handle;
char *name;
bool include_app_param;
esp_pbac_pull_vcard_entry_app_param_t app_param;
} pull_vcard_entry;
} btc_pba_client_args_t;
void btc_pba_client_call_handler(btc_msg_t *msg);
void btc_pba_client_cb_handler(btc_msg_t *msg);
void btc_pba_client_args_deep_free(btc_msg_t *msg);
void btc_pba_client_args_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
#endif

View File

@@ -0,0 +1,971 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "osi/allocator.h"
#include "bta/bta_api.h"
#include "bta/bta_pba_defs.h"
#include "bta/bta_pba_client_api.h"
#include "btc/btc_profile_queue.h"
#include "btc/btc_manage.h"
#include "btc/btc_task.h"
#include "btc_pba_client.h"
#include "esp_pbac_api.h"
#if BTC_PBA_CLIENT_INCLUDED
#define BTC_PBA_CLIENT_SECURITY (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)
static bool s_btc_pba_client_init = 0;
btc_pba_client_cb_t btc_pba_client_cb;
static void bte_pba_client_evt(tBTA_PBA_CLIENT_EVT event, tBTA_PBA_CLIENT *p_data)
{
bt_status_t status;
int param_len = 0;
bool ignore = false;
switch (event) {
case BTA_PBA_CLIENT_CONN_OPEN_EVT:
case BTA_PBA_CLIENT_CONN_CLOSE_EVT:
param_len = sizeof(tBTA_PBA_CLIENT_CONN);
break;
case BTA_PBA_CLIENT_PULL_PHONE_BOOK_RSP_EVT:
case BTA_PBA_CLIENT_SET_PHONE_BOOK_RSP_EVT:
case BTA_PBA_CLIENT_PULL_VCARD_LISTING_RSP_EVT:
case BTA_PBA_CLIENT_PULL_VCARD_ENTRY_RSP_EVT:
param_len = sizeof(tBTA_PBA_CLIENT_RESPONSE);
break;
case BTA_PBA_CLIENT_REGISTER_EVT:
param_len = 0;
break;
case BTA_PBA_CLIENT_DISABLE_EVT:
param_len = 0;
break;
case BTA_PBA_CLIENT_ENABLE_EVT:
case BTA_PBA_CLIENT_DEREGISTER_EVT:
default:
ignore = true;
break;
}
if (ignore) {
return;
}
btc_msg_t msg;
msg.sig = BTC_SIG_API_CB;
msg.pid = BTC_PID_PBA_CLIENT;
msg.act = event;
status = btc_transfer_context(&msg, p_data, param_len, NULL, NULL);
if (status != BT_STATUS_SUCCESS) {
BTC_TRACE_ERROR("context transfer failed");
}
}
static void btc_pba_client_callback_to_app(esp_pbac_event_t event, esp_pbac_param_t *param)
{
esp_pbac_callback_t callback = (esp_pbac_callback_t)btc_profile_cb_get(BTC_PID_PBA_CLIENT);
if (callback) {
callback(event, param);
}
}
static void btc_pba_client_init(void)
{
if (!s_btc_pba_client_init) {
s_btc_pba_client_init = true;
memset(&btc_pba_client_cb, 0, sizeof(btc_pba_client_cb_t));
/* enable pba client */
BTA_PbaClientEnable(bte_pba_client_evt);
/* register sdp record */
BTA_PbaClientRegister("Phonebook Access PCE");
}
}
static void btc_pba_client_deinit(void)
{
if (s_btc_pba_client_init) {
s_btc_pba_client_init = false;
/* deregister sdp record */
BTA_PbaClientDeregister();
/* disable pba client */
BTA_PbaClientDisable();
}
}
static BOOLEAN is_connected(bt_bdaddr_t *bd_addr)
{
for (int i = 0; i < BTC_PBA_CLIENT_MAX_CONN_NUM; ++i) {
if (btc_pba_client_cb.ccb[i].handle != 0 && bdcmp(bd_addr->address, btc_pba_client_cb.ccb[i].bd_addr.address) == 0) {
return TRUE;
}
}
return FALSE;
}
static bt_status_t connect_int(bt_bdaddr_t *bd_addr, uint16_t uuid)
{
if (is_connected(bd_addr)) {
return BT_STATUS_BUSY;
}
BTA_PbaClientOpen(bd_addr->address, BTC_PBA_CLIENT_SECURITY, (uint32_t)BTC_PBA_SUPPORTED_FEAT, (uint16_t)BTC_PBA_PREFERRED_MTU);
return BT_STATUS_SUCCESS;
}
static void btc_pba_client_connect(bt_bdaddr_t *bd_addr)
{
if (!s_btc_pba_client_init) {
return;
}
btc_queue_connect(UUID_SERVCLASS_PBAP_PCE, bd_addr, connect_int);
}
static void btc_pba_client_disconnect(uint16_t handle)
{
do {
if (!s_btc_pba_client_init) {
break;
}
if (handle == 0 || handle > BTC_PBA_CLIENT_MAX_CONN_NUM) {
/* invalid handle value */
break;
}
btc_pba_client_ccb_t *p_ccb = &btc_pba_client_cb.ccb[handle - 1];
if (p_ccb->handle != handle) {
/* not connect */
break;
}
BTA_PbaClientClose(handle);
} while (0);
}
static bool btc_pba_client_pull_phone_book(uint16_t handle, char *name, bool include_app_param, esp_pbac_pull_phone_book_app_param_t *app_param)
{
bt_status_t err = BT_STATUS_FAIL;
uint8_t *app_param_buff = NULL;
uint16_t app_param_len = 0;
do {
if (!s_btc_pba_client_init) {
/* pba client not init */
err = BT_STATUS_NOT_READY;
break;
}
if (handle == 0 || handle > BTC_PBA_CLIENT_MAX_CONN_NUM) {
/* invalid handle value */
err = BT_STATUS_PARM_INVALID;
break;
}
btc_pba_client_ccb_t *p_ccb = &btc_pba_client_cb.ccb[handle - 1];
if (p_ccb->handle != handle) {
/* not connect */
err = BT_STATUS_PARM_INVALID;
break;
}
if (p_ccb->busy) {
/* busy */
err = BT_STATUS_BUSY;
break;
}
if (include_app_param) {
app_param_buff = osi_malloc(BTA_PBAP_PULL_PHONE_BOOK_APP_PARAM_BUFF_SIZE_MIN);
if (app_param_buff == NULL) {
err = BT_STATUS_NOMEM;
break;
}
uint8_t *p = app_param_buff;
if (app_param->include_property_selector) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_PROPERTY_SELECTOR);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_PROPERTY_SELECTOR);
UINT64_TO_BE_STREAM(p, app_param->property_selector);
}
if (app_param->include_format) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_FORMAT);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_FORMAT);
UINT8_TO_BE_STREAM(p, app_param->format);
}
if (app_param->include_max_list_count) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_MAX_LIST_COUNT);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_MAX_LIST_COUNT);
UINT16_TO_BE_STREAM(p, app_param->max_list_count);
}
if (app_param->include_list_start_offset) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LIST_START_OFFSET);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_LIST_START_OFFSET);
UINT16_TO_BE_STREAM(p, app_param->list_start_offset);
}
if (app_param->include_reset_new_missed_calls) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_RESET_NEW_MISSED_CALLS);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_RESET_NEW_MISSED_CALLS);
UINT8_TO_BE_STREAM(p, app_param->reset_new_missed_calls);
}
if (app_param->include_vcard_selector) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_VCARD_SELECTOR);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_VCARD_SELECTOR);
UINT64_TO_BE_STREAM(p, app_param->vcard_selector);
}
if (app_param->include_vcard_selector_operator) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_VCARD_SELECTOR_OPERATOR);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_VCARD_SELECTOR_OPERATOR);
UINT8_TO_BE_STREAM(p, app_param->vcard_selector_operator);
}
app_param_len = p - app_param_buff;
assert(app_param_len <= BTA_PBAP_PULL_PHONE_BOOK_APP_PARAM_BUFF_SIZE_MIN);
if (app_param_len == 0) {
/* user give us an empty app param, allow but not recommend */
osi_free(app_param_buff);
app_param_buff = NULL;
}
}
p_ccb->busy = true;
BTA_PbaClientPullPhoneBook(handle, name, app_param_buff, app_param_len);
err = BT_STATUS_SUCCESS;
} while (0);
if (err != BT_STATUS_SUCCESS) {
BTC_TRACE_WARNING("%s failed, handle: %d, reason: %d", __FUNCTION__, handle, err);
return false;
}
return true;
}
static bool btc_pba_client_set_phone_book(uint16_t handle, uint8_t flags, char *name)
{
bt_status_t err = BT_STATUS_FAIL;
do {
if (!s_btc_pba_client_init) {
/* pba client not init */
err = BT_STATUS_NOT_READY;
break;
}
if (handle == 0 || handle > BTC_PBA_CLIENT_MAX_CONN_NUM) {
/* invalid handle value */
err = BT_STATUS_PARM_INVALID;
break;
}
btc_pba_client_ccb_t *p_ccb = &btc_pba_client_cb.ccb[handle - 1];
if (p_ccb->handle != handle) {
/* not connect */
err = BT_STATUS_PARM_INVALID;
break;
}
if (p_ccb->busy) {
/* busy */
err = BT_STATUS_BUSY;
break;
}
p_ccb->busy = true;
BTA_PbaClientSetPhoneBook(handle, flags, (char *)name);
err = BT_STATUS_SUCCESS;
} while (0);
if (err != BT_STATUS_SUCCESS) {
BTC_TRACE_WARNING("%s failed, handle: %d, reason: %d", __FUNCTION__, handle, err);
return false;
}
return true;
}
static bool btc_pba_client_set_phone_book2(uint16_t handle, char *path)
{
bt_status_t err = BT_STATUS_FAIL;
do {
if (!s_btc_pba_client_init) {
/* pba client not init */
err = BT_STATUS_NOT_READY;
break;
}
if (handle == 0 || handle > BTC_PBA_CLIENT_MAX_CONN_NUM) {
/* invalid handle value */
err = BT_STATUS_PARM_INVALID;
break;
}
btc_pba_client_ccb_t *p_ccb = &btc_pba_client_cb.ccb[handle - 1];
if (p_ccb->handle != handle) {
/* not connect */
err = BT_STATUS_PARM_INVALID;
break;
}
if (p_ccb->busy) {
/* busy */
err = BT_STATUS_BUSY;
break;
}
p_ccb->busy = true;
if (path != NULL) {
p_ccb->path_len = strlen(path) + 1;
/* ignore the first slash */
if (path[0] == '/') {
p_ccb->path_pos = 1;
}
else {
p_ccb->path_pos = 0;
}
/* since we use absolute path, treat empty path as go to ROOT */
if (p_ccb->path_len == p_ccb->path_pos + 1) {
p_ccb->path_len = 0;
p_ccb->path_pos = 0;
osi_free(path);
path = NULL;
}
else {
p_ccb->path = path;
}
}
/* anyway, go to ROOT first */
char *empty_name = osi_malloc(1);
assert(empty_name != NULL);
*empty_name = '\0';
BTA_PbaClientSetPhoneBook(handle, ESP_PBAC_SET_PHONE_BOOK_FLAGS_ROOT, empty_name);
err = BT_STATUS_SUCCESS;
} while (0);
if (err != BT_STATUS_SUCCESS) {
BTC_TRACE_WARNING("%s failed, handle: %d, reason: %d", __FUNCTION__, handle, err);
return false;
}
return true;
}
static bool btc_pba_client_pull_vcard_listing(uint16_t handle, char *name, bool include_app_param, esp_pbac_pull_vcard_listing_app_param_t *app_param)
{
bt_status_t err = BT_STATUS_FAIL;
uint8_t *app_param_buff = NULL;
uint16_t app_param_len = 0;
do {
if (!s_btc_pba_client_init) {
/* pba client not init */
err = BT_STATUS_NOT_READY;
break;
}
if (handle == 0 || handle > BTC_PBA_CLIENT_MAX_CONN_NUM) {
/* invalid handle value */
err = BT_STATUS_PARM_INVALID;
break;
}
btc_pba_client_ccb_t *p_ccb = &btc_pba_client_cb.ccb[handle - 1];
if (p_ccb->handle != handle) {
/* not connect */
err = BT_STATUS_PARM_INVALID;
break;
}
if (p_ccb->busy) {
/* busy */
err = BT_STATUS_BUSY;
break;
}
if (include_app_param) {
uint8_t search_value_len = 0;
if (app_param->include_search_value) {
search_value_len = strlen(app_param->search_value) + 1;
}
app_param_buff = osi_malloc(BTA_PBAP_PULL_VCARD_LISTING_APP_PARAM_BUFF_SIZE_MIN + search_value_len);
if (app_param_buff == NULL) {
err = BT_STATUS_NOMEM;
break;
}
uint8_t *p = app_param_buff;
if (app_param->include_order) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_ORDER);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_ORDER);
UINT8_TO_BE_STREAM(p, app_param->order);
}
if (app_param->include_search_value) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_SEARCH_VALUE);
UINT8_TO_BE_STREAM(p, search_value_len);
memcpy(p, app_param->search_value, search_value_len);
p += search_value_len;
}
if (app_param->include_search_property) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_SEARCH_PROPERTY);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_SEARCH_PROPERTY);
UINT8_TO_BE_STREAM(p, app_param->search_property);
}
if (app_param->include_max_list_count) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_MAX_LIST_COUNT);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_MAX_LIST_COUNT);
UINT16_TO_BE_STREAM(p, app_param->max_list_count);
}
if (app_param->include_list_start_offset) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LIST_START_OFFSET);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_LIST_START_OFFSET);
UINT16_TO_BE_STREAM(p, app_param->list_start_offset);
}
if (app_param->include_reset_new_missed_calls) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_RESET_NEW_MISSED_CALLS);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_RESET_NEW_MISSED_CALLS);
UINT8_TO_BE_STREAM(p, app_param->reset_new_missed_calls);
}
if (app_param->include_vcard_selector) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_VCARD_SELECTOR);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_VCARD_SELECTOR);
UINT64_TO_BE_STREAM(p, app_param->vcard_selector);
}
if (app_param->include_vcard_selector_operator) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_VCARD_SELECTOR_OPERATOR);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_VCARD_SELECTOR_OPERATOR);
UINT8_TO_BE_STREAM(p, app_param->vcard_selector_operator);
}
app_param_len = p - app_param_buff;
assert(app_param_len <= BTA_PBAP_PULL_VCARD_LISTING_APP_PARAM_BUFF_SIZE_MIN + search_value_len);
if (app_param_len == 0) {
/* user give us an empty app param, allow but not recommend */
osi_free(app_param_buff);
app_param_buff = NULL;
}
/* free search_value (allocated by deep_copy) */
if (app_param->include_search_value && app_param->search_value) {
osi_free(app_param->search_value);
app_param->search_value = NULL;
}
}
p_ccb->busy = true;
BTA_PbaClientPullvCardListing(handle, (char *)name, app_param_buff, app_param_len);
err = BT_STATUS_SUCCESS;
} while (0);
if (err != BT_STATUS_SUCCESS) {
BTC_TRACE_WARNING("%s failed, handle: %d, reason: %d", __FUNCTION__, handle, err);
return false;
}
return true;
}
static bool btc_pba_client_pull_vcard_entry(uint16_t handle, char *name, bool include_app_param, esp_pbac_pull_vcard_entry_app_param_t *app_param)
{
bt_status_t err = BT_STATUS_FAIL;
uint8_t *app_param_buff = NULL;
uint16_t app_param_len = 0;
do {
if (!s_btc_pba_client_init) {
/* pba client not init */
err = BT_STATUS_NOT_READY;
break;
}
if (handle == 0 || handle > BTC_PBA_CLIENT_MAX_CONN_NUM) {
/* invalid handle value */
err = BT_STATUS_PARM_INVALID;
break;
}
btc_pba_client_ccb_t *p_ccb = &btc_pba_client_cb.ccb[handle - 1];
if (p_ccb->handle != handle) {
/* not connect */
err = BT_STATUS_PARM_INVALID;
break;
}
if (p_ccb->busy) {
/* busy */
err = BT_STATUS_BUSY;
break;
}
if (include_app_param) {
app_param_buff = osi_malloc(BTA_PBAP_PULL_VCARD_ENTRY_APP_PARAM_BUFF_SIZE_MIN);
if (app_param_buff == NULL) {
err = BT_STATUS_NOMEM;
break;
}
uint8_t *p = app_param_buff;
if (app_param->include_property_selector) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_PROPERTY_SELECTOR);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_PROPERTY_SELECTOR);
UINT64_TO_BE_STREAM(p, app_param->property_selector);
}
if (app_param->include_format) {
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_FORMAT);
UINT8_TO_BE_STREAM(p, BTA_PBAP_APP_PARAM_LENGTH_FORMAT);
UINT8_TO_BE_STREAM(p, app_param->format);
}
app_param_len = p - app_param_buff;
assert(app_param_len <= BTA_PBAP_PULL_VCARD_ENTRY_APP_PARAM_BUFF_SIZE_MIN);
if (app_param_len == 0) {
/* user give us an empty app param, allow but not recommend */
osi_free(app_param_buff);
app_param_buff = NULL;
}
}
p_ccb->busy = true;
BTA_PbaClientPullvCardEntry(handle, (char *)name, app_param_buff, app_param_len);
err = BT_STATUS_SUCCESS;
} while (0);
if (err != BT_STATUS_SUCCESS) {
BTC_TRACE_WARNING("%s failed, handle: %d, reason: %d", __FUNCTION__, handle, err);
return false;
}
return true;
}
void btc_pba_client_args_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src)
{
btc_pba_client_args_t *dst = (btc_pba_client_args_t *)p_dest;
btc_pba_client_args_t *src = (btc_pba_client_args_t *)p_src;
size_t len;
switch (msg->act) {
case BTC_PBA_CLIENT_PULL_PHONE_BOOK_EVT:
len = strlen(src->pull_phone_book.name) + 1;
dst->pull_phone_book.name = (char *)osi_malloc(len);
if (dst->pull_phone_book.name) {
memcpy(dst->pull_phone_book.name, src->pull_phone_book.name, len);
} else {
BTC_TRACE_ERROR("%s %d no mem\n", __FUNCTION__, msg->act);
}
break;
case BTC_PBA_CLIENT_PULL_VCARD_LISTING_EVT:
len = strlen(src->pull_vcard_listing.name) + 1;
dst->pull_vcard_listing.name = (char *)osi_malloc(len);
if (dst->pull_vcard_listing.name) {
memcpy(dst->pull_vcard_listing.name, src->pull_vcard_listing.name, len);
} else {
BTC_TRACE_ERROR("%s %d no mem\n", __FUNCTION__, msg->act);
}
if (src->pull_vcard_listing.include_app_param && src->pull_vcard_listing.app_param.include_search_value) {
len = strlen(src->pull_vcard_listing.app_param.search_value) + 1;
dst->pull_vcard_listing.app_param.search_value = (char *)osi_malloc(len);
if (dst->pull_vcard_listing.app_param.search_value) {
memcpy(dst->pull_vcard_listing.app_param.search_value, src->pull_vcard_listing.app_param.search_value, len);
} else {
BTC_TRACE_ERROR("%s %d no mem\n", __FUNCTION__, msg->act);
}
}
break;
case BTC_PBA_CLIENT_PULL_VCARD_ENTRY_EVT:
len = strlen(src->pull_vcard_entry.name) + 1;
dst->pull_vcard_entry.name = (char *)osi_malloc(len);
if (dst->pull_vcard_entry.name) {
memcpy(dst->pull_vcard_entry.name, src->pull_vcard_entry.name, len);
} else {
BTC_TRACE_ERROR("%s %d no mem\n", __FUNCTION__, msg->act);
}
break;
case BTC_PBA_CLIENT_SET_PHONE_BOOK_EVT:
case BTC_PBA_CLIENT_SET_PHONE_BOOK2_EVT:
/* set phone book name may be NULL */
if (src->set_phone_book.name) {
len = strlen(src->set_phone_book.name) + 1;
dst->set_phone_book.name = (char *)osi_malloc(len);
if (dst->set_phone_book.name) {
memcpy(dst->set_phone_book.name, src->set_phone_book.name, len);
} else {
BTC_TRACE_ERROR("%s %d no mem\n", __FUNCTION__, msg->act);
}
}
break;
default:
BTC_TRACE_DEBUG("%s Unhandled deep copy %d\n", __FUNCTION__, msg->act);
UNUSED(dst);
UNUSED(src);
UNUSED(len);
break;
}
}
void btc_pba_client_args_deep_free(btc_msg_t *msg)
{
btc_pba_client_args_t *arg = (btc_pba_client_args_t *)msg->arg;
switch (msg->act) {
case BTC_PBA_CLIENT_PULL_PHONE_BOOK_EVT:
if (arg->pull_phone_book.name) {
osi_free(arg->pull_phone_book.name);
}
break;
case BTC_PBA_CLIENT_PULL_VCARD_LISTING_EVT:
if (arg->pull_vcard_listing.name) {
osi_free(arg->pull_vcard_listing.name);
}
if (arg->pull_vcard_listing.include_app_param
&& arg->pull_vcard_listing.app_param.include_search_value
&& arg->pull_vcard_listing.app_param.search_value) {
osi_free(arg->pull_vcard_listing.app_param.search_value);
}
break;
case BTC_PBA_CLIENT_PULL_VCARD_ENTRY_EVT:
if (arg->pull_vcard_entry.name) {
osi_free(arg->pull_vcard_entry.name);
}
break;
case BTC_PBA_CLIENT_SET_PHONE_BOOK_EVT:
case BTC_PBA_CLIENT_SET_PHONE_BOOK2_EVT:
if (arg->set_phone_book.name) {
osi_free(arg->set_phone_book.name);
}
break;
default:
BTC_TRACE_DEBUG("%s Unhandled deep free %d\n", __FUNCTION__, msg->act);
UNUSED(arg);
break;
}
}
void btc_pba_client_call_handler(btc_msg_t *msg)
{
bool ret = true;
btc_pba_client_args_t *arg = (btc_pba_client_args_t *)(msg->arg);
switch (msg->act) {
case BTC_PBA_CLIENT_INIT_EVT:
btc_pba_client_init();
break;
case BTC_PBA_CLIENT_DEINIT_EVT:
btc_pba_client_deinit();
break;
case BTC_PBA_CLIENT_CONNECT_EVT:
btc_pba_client_connect(&arg->connect.bd_addr);
break;
case BTC_PBA_CLIENT_DISCONNECT_EVT:
btc_pba_client_disconnect(arg->disconnect.handle);
break;
case BTC_PBA_CLIENT_PULL_PHONE_BOOK_EVT:
ret = btc_pba_client_pull_phone_book(arg->pull_phone_book.handle, arg->pull_phone_book.name, arg->pull_phone_book.include_app_param, &arg->pull_phone_book.app_param);
break;
case BTC_PBA_CLIENT_SET_PHONE_BOOK_EVT:
ret = btc_pba_client_set_phone_book(arg->set_phone_book.handle, arg->set_phone_book.flags, arg->set_phone_book.name);
break;
case BTC_PBA_CLIENT_SET_PHONE_BOOK2_EVT:
ret = btc_pba_client_set_phone_book2(arg->set_phone_book.handle, arg->set_phone_book.name);
break;
case BTC_PBA_CLIENT_PULL_VCARD_LISTING_EVT:
ret = btc_pba_client_pull_vcard_listing(arg->pull_vcard_listing.handle, arg->pull_vcard_listing.name, arg->pull_vcard_listing.include_app_param, &arg->pull_vcard_listing.app_param);
break;
case BTC_PBA_CLIENT_PULL_VCARD_ENTRY_EVT:
ret = btc_pba_client_pull_vcard_entry(arg->pull_vcard_entry.handle, arg->pull_vcard_entry.name, arg->pull_vcard_entry.include_app_param, &arg->pull_vcard_entry.app_param);
break;
default:
BTC_TRACE_WARNING("unknown pba client action %i", msg->act);
break;
}
if (!ret) {
/* operation failed, do deep free */
btc_pba_client_args_deep_free(msg);
}
}
static void parse_pull_phone_book_app_param(esp_pbac_param_t *param, uint8_t *app_param, uint16_t app_param_len)
{
if (app_param == NULL || app_param_len == 0) {
return;
}
uint8_t *ptr = app_param;
while(ptr < app_param + app_param_len) {
switch (*ptr)
{
case BTA_PBAP_APP_PARAM_PHONE_BOOK_SIZE:
param->pull_phone_book_rsp.include_phone_book_size = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
BE_STREAM_TO_UINT16(param->pull_phone_book_rsp.phone_book_size, ptr);
break;
case BTA_PBAP_APP_PARAM_NEW_MISSED_CALLS:
param->pull_phone_book_rsp.include_new_missed_calls = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
BE_STREAM_TO_UINT8(param->pull_phone_book_rsp.new_missed_calls, ptr);
break;
case BTA_PBAP_APP_PARAM_PRIMARY_FOLDER_VERSION:
param->pull_phone_book_rsp.include_primary_folder_version = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
/* don't copy */
param->pull_phone_book_rsp.primary_folder_version = ptr;
ptr += BTA_PBAP_APP_PARAM_LENGTH_PRIMARY_FOLDER_VERSION;
break;
case BTA_PBAP_APP_PARAM_SECONDARY_FOLDER_VERSION:
param->pull_phone_book_rsp.include_secondary_folder_version = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
/* don't copy */
param->pull_phone_book_rsp.secondary_folder_version = ptr;
ptr += BTA_PBAP_APP_PARAM_LENGTH_SECONDARY_FOLDER_VERSION;
break;
case BTA_PBAP_APP_PARAM_DATABASE_IDENTIFIER:
param->pull_phone_book_rsp.include_database_identifier = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
/* don't copy */
param->pull_phone_book_rsp.database_identifier = ptr;
ptr += BTA_PBAP_APP_PARAM_LENGTH_DATABASE_IDENTIFIER;
break;
default:
goto error;
break;
}
}
error:
return;
}
static void parse_pull_vcard_listing_app_param(esp_pbac_param_t *param, uint8_t *app_param, uint16_t app_param_len)
{
if (app_param == NULL || app_param_len == 0) {
return;
}
uint8_t *ptr = app_param;
while(ptr < app_param + app_param_len) {
switch (*ptr)
{
case BTA_PBAP_APP_PARAM_PHONE_BOOK_SIZE:
param->pull_vcard_listing_rsp.include_phone_book_size = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
BE_STREAM_TO_UINT16(param->pull_vcard_listing_rsp.phone_book_size, ptr);
break;
case BTA_PBAP_APP_PARAM_NEW_MISSED_CALLS:
param->pull_vcard_listing_rsp.include_new_missed_calls = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
BE_STREAM_TO_UINT8(param->pull_vcard_listing_rsp.new_missed_calls, ptr);
break;
case BTA_PBAP_APP_PARAM_PRIMARY_FOLDER_VERSION:
param->pull_vcard_listing_rsp.include_primary_folder_version = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
/* don't copy */
param->pull_vcard_listing_rsp.primary_folder_version = ptr;
ptr += BTA_PBAP_APP_PARAM_LENGTH_PRIMARY_FOLDER_VERSION;
break;
case BTA_PBAP_APP_PARAM_SECONDARY_FOLDER_VERSION:
param->pull_vcard_listing_rsp.include_secondary_folder_version = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
/* don't copy */
param->pull_vcard_listing_rsp.secondary_folder_version = ptr;
ptr += BTA_PBAP_APP_PARAM_LENGTH_SECONDARY_FOLDER_VERSION;
break;
case BTA_PBAP_APP_PARAM_DATABASE_IDENTIFIER:
param->pull_vcard_listing_rsp.include_database_identifier = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
/* don't copy */
param->pull_vcard_listing_rsp.database_identifier = ptr;
ptr += BTA_PBAP_APP_PARAM_LENGTH_DATABASE_IDENTIFIER;
break;
default:
goto error;
break;
}
}
error:
return;
}
static void parse_pull_vcard_entry_app_param(esp_pbac_param_t *param, uint8_t *app_param, uint16_t app_param_len)
{
if (app_param == NULL || app_param_len == 0) {
return;
}
uint8_t *ptr = app_param;
while(ptr < app_param + app_param_len) {
switch (*ptr)
{
case BTA_PBAP_APP_PARAM_DATABASE_IDENTIFIER:
param->pull_vcard_entry_rsp.include_database_identifier = 1;
ptr += BTA_PBAP_APP_PARAM_HEADER_LENGTH;
/* don't copy */
param->pull_vcard_entry_rsp.database_identifier = ptr;
ptr += BTA_PBAP_APP_PARAM_LENGTH_DATABASE_IDENTIFIER;
break;
default:
goto error;
break;
}
}
error:
return;
}
static uint16_t get_next_dir_len_from_path(char *path, uint16_t path_len, uint16_t path_pos)
{
uint16_t ret = 0;
for (int i = path_pos; i < path_len; ++i) {
if (path[i] == '/' || path[i] == '\0') {
break;
}
else {
++ret;
}
}
return ret;
}
void btc_pba_client_cb_handler(btc_msg_t *msg)
{
uint16_t event = msg->act;
tBTA_PBA_CLIENT *p_data = (tBTA_PBA_CLIENT *)msg->arg;
btc_pba_client_ccb_t *p_ccb = NULL;
esp_pbac_param_t param = {0};
switch (event) {
case BTA_PBA_CLIENT_CONN_OPEN_EVT:
if (p_data->conn.error == BTA_PBA_CLIENT_NO_ERROR) {
/* allocate ccb */
p_ccb = &btc_pba_client_cb.ccb[p_data->conn.handle - 1];
p_ccb->handle = p_data->conn.handle;
bdcpy(p_ccb->bd_addr.address, p_data->conn.bd_addr);
p_ccb->busy = false;
param.conn_stat.connected = true;
param.conn_stat.peer_supported_repo = p_data->conn.peer_supported_repo;
param.conn_stat.peer_supported_feat = p_data->conn.peer_supported_feat;
param.conn_stat.reason = BTA_PBA_CLIENT_NO_ERROR;
}
else {
param.conn_stat.connected = false;
/* error codes are compatible */
param.conn_stat.reason = p_data->conn.error;
}
bdcpy(param.conn_stat.remote_bda, p_data->conn.bd_addr);
param.conn_stat.handle = p_data->conn.handle;
btc_pba_client_callback_to_app(ESP_PBAC_CONNECTION_STATE_EVT, &param);
btc_queue_advance();
break;
case BTA_PBA_CLIENT_CONN_CLOSE_EVT:
/* clear ccb */
p_ccb = &btc_pba_client_cb.ccb[p_data->conn.handle - 1];
p_ccb->handle = 0;
p_ccb->busy = false;
p_ccb->path_len = 0;
p_ccb->path_pos = 0;
if (p_ccb->path) {
osi_free(p_ccb->path);
p_ccb->path = NULL;
}
memset(p_ccb->bd_addr.address, 0, BD_ADDR_LEN);
param.conn_stat.connected = false;
bdcpy(param.conn_stat.remote_bda, p_data->conn.bd_addr);
param.conn_stat.handle = p_data->conn.handle;
/* error codes are compatible */
param.conn_stat.reason = p_data->conn.error;
btc_pba_client_callback_to_app(ESP_PBAC_CONNECTION_STATE_EVT, &param);
btc_queue_advance();
break;
case BTA_PBA_CLIENT_PULL_PHONE_BOOK_RSP_EVT:
if (p_data->response.final) {
p_ccb = &btc_pba_client_cb.ccb[p_data->response.handle - 1];
p_ccb->busy = false;
}
param.pull_phone_book_rsp.handle = p_data->response.handle;
param.pull_phone_book_rsp.final = p_data->response.final;
param.pull_phone_book_rsp.result = p_data->response.status;
param.pull_phone_book_rsp.data = p_data->response.data;
param.pull_phone_book_rsp.data_len = p_data->response.data_len;
parse_pull_phone_book_app_param(&param, p_data->response.app_param, p_data->response.app_param_len);
btc_pba_client_callback_to_app(ESP_PBAC_PULL_PHONE_BOOK_RESPONSE_EVT, &param);
if (p_data->response.pkt != NULL) {
osi_free(p_data->response.pkt);
}
break;
case BTA_PBA_CLIENT_SET_PHONE_BOOK_RSP_EVT:
p_ccb = &btc_pba_client_cb.ccb[p_data->response.handle - 1];
if (p_data->response.status == BTA_PBA_CLIENT_NO_ERROR && p_ccb->path_pos < p_ccb->path_len) {
/* since path_len is not zero, path should not be NULL, use asset to check */
assert(p_ccb->path != NULL);
uint16_t dir_name_len = get_next_dir_len_from_path(p_ccb->path, p_ccb->path_len, p_ccb->path_pos);
if (dir_name_len > 0) {
char *dir_name = osi_malloc(dir_name_len + 1);
assert(dir_name != NULL);
memcpy(dir_name, p_ccb->path + p_ccb->path_pos, dir_name_len);
dir_name[dir_name_len] = '\0';
p_ccb->path_pos += dir_name_len + 1;
BTA_PbaClientSetPhoneBook(p_data->response.handle, ESP_PBAC_SET_PHONE_BOOK_FLAGS_DOWN, dir_name);
/* break here, don't report event to upper */
break;
}
}
/* set path done or failed, clear status */
p_ccb->path_len = 0;
p_ccb->path_pos = 0;
if (p_ccb->path) {
osi_free(p_ccb->path);
p_ccb->path = NULL;
}
p_ccb->busy = false;
param.set_phone_book_rsp.handle = p_data->response.handle;
param.set_phone_book_rsp.result = p_data->response.status;
btc_pba_client_callback_to_app(ESP_PBAC_SET_PHONE_BOOK_RESPONSE_EVT, &param);
if (p_data->response.pkt != NULL) {
osi_free(p_data->response.pkt);
}
break;
case BTA_PBA_CLIENT_PULL_VCARD_LISTING_RSP_EVT:
if (p_data->response.final) {
p_ccb = &btc_pba_client_cb.ccb[p_data->response.handle - 1];
p_ccb->busy = false;
}
param.pull_vcard_listing_rsp.handle = p_data->response.handle;
param.pull_vcard_listing_rsp.final = p_data->response.final;
param.pull_vcard_listing_rsp.result = p_data->response.status;
param.pull_vcard_listing_rsp.data = p_data->response.data;
param.pull_vcard_listing_rsp.data_len = p_data->response.data_len;
parse_pull_vcard_listing_app_param(&param, p_data->response.app_param, p_data->response.app_param_len);
btc_pba_client_callback_to_app(ESP_PBAC_PULL_VCARD_LISTING_RESPONSE_EVT, &param);
if (p_data->response.pkt != NULL) {
osi_free(p_data->response.pkt);
}
break;
case BTA_PBA_CLIENT_PULL_VCARD_ENTRY_RSP_EVT:
if (p_data->response.final) {
p_ccb = &btc_pba_client_cb.ccb[p_data->response.handle - 1];
p_ccb->busy = false;
}
param.pull_vcard_entry_rsp.handle = p_data->response.handle;
param.pull_vcard_entry_rsp.final = p_data->response.final;
param.pull_vcard_entry_rsp.result = p_data->response.status;
param.pull_vcard_entry_rsp.data = p_data->response.data;
param.pull_vcard_entry_rsp.data_len = p_data->response.data_len;
parse_pull_vcard_entry_app_param(&param, p_data->response.app_param, p_data->response.app_param_len);
btc_pba_client_callback_to_app(ESP_PBAC_PULL_VCARD_ENTRY_RESPONSE_EVT, &param);
if (p_data->response.pkt != NULL) {
osi_free(p_data->response.pkt);
}
break;
case BTA_PBA_CLIENT_REGISTER_EVT:
/* init process: Enable -> Register */
btc_pba_client_callback_to_app(ESP_PBAC_INIT_EVT, NULL);
break;
case BTA_PBA_CLIENT_DISABLE_EVT:
/* deinit process: Deregister -> Disable */
btc_pba_client_callback_to_app(ESP_PBAC_DEINIT_EVT, NULL);
break;
default:
BTC_TRACE_WARNING("%s: unknown event (%d)", __func__, event);
break;
}
}
#endif

View File

@@ -124,6 +124,25 @@
#define UC_BT_ENC_KEY_SIZE_CTRL_MODE 0
#endif
//PBAP Client
#ifdef CONFIG_BT_PBAC_ENABLED
#define UC_BT_PBAC_ENABLED CONFIG_BT_PBAC_ENABLED
#else
#define UC_BT_PBAC_ENABLED FALSE
#endif
#ifdef CONFIG_BT_PBAC_SUPPORTED_FEAT
#define UC_BT_PBAC_SUPPORTED_FEAT CONFIG_BT_PBAC_SUPPORTED_FEAT
#else
#define UC_BT_PBAC_SUPPORTED_FEAT 0x00
#endif
#ifdef CONFIG_BT_PBAC_PREFERRED_MTU
#define UC_BT_PBAC_PREFERRED_MTU CONFIG_BT_PBAC_PREFERRED_MTU
#else
#define UC_BT_PBAC_PREFERRED_MTU 0
#endif
//GOEPC (BT)
#ifdef CONFIG_BT_GOEPC_ENABLED
#define UC_BT_GOEPC_ENABLED CONFIG_BT_GOEPC_ENABLED

View File

@@ -177,12 +177,22 @@
#endif /* UC_BT_HID_DEVICE_ENABLED */
#if UC_BT_GOEPC_ENABLED
#ifndef RFCOMM_INCLUDED
#define RFCOMM_INCLUDED TRUE
#endif
#ifndef OBEX_INCLUDED
#define OBEX_INCLUDED TRUE
#endif
#define GOEPC_INCLUDED TRUE
#endif /* UC_BT_GOEPC_ENABLED */
#if UC_BT_PBAC_ENABLED
#define BTC_PBA_CLIENT_INCLUDED TRUE
#define BTC_PBA_SUPPORTED_FEAT UC_BT_PBAC_SUPPORTED_FEAT
#define BTC_PBA_PREFERRED_MTU UC_BT_PBAC_PREFERRED_MTU
#define BTA_PBA_CLIENT_INCLUDED TRUE
#endif
#endif /* UC_BT_CLASSIC_ENABLED */
/* This is set to enable use of GAP L2CAP connections. */

View File

@@ -330,6 +330,13 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l
#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 OBEX_TL_RFCOMM */
#define OBEX_TL_RFCOMM_TRACE_ERROR(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVRC, ERROR)) BT_PRINT_E("OBEX_TL_RFCOMM", fmt, ## args);}
#define OBEX_TL_RFCOMM_TRACE_WARNING(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVRC, WARNING)) BT_PRINT_W("OBEX_TL_RFCOMM", fmt, ## args);}
#define OBEX_TL_RFCOMM_TRACE_API(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVRC,API)) BT_PRINT_I("OBEX_TL_RFCOMM", fmt, ## args);}
#define OBEX_TL_RFCOMM_TRACE_EVENT(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("OBEX_TL_RFCOMM", fmt, ## args);}
#define OBEX_TL_RFCOMM_TRACE_DEBUG(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("OBEX_TL_RFCOMM", 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);}
@@ -512,12 +519,20 @@ extern UINT8 btif_trace_level;
#define OBEX_TRACE_EVENT(fmt, args...)
#define OBEX_TRACE_DEBUG(fmt, args...)
/* Define tracing for OBEX L2CAP transport layer */
#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 OBEX RFCOMM transport layer */
#define OBEX_TL_RFCOMM_TRACE_ERROR(fmt, args...)
#define OBEX_TL_RFCOMM_TRACE_WARNING(fmt, args...)
#define OBEX_TL_RFCOMM_TRACE_API(fmt, args...)
#define OBEX_TL_RFCOMM_TRACE_EVENT(fmt, args...)
#define OBEX_TL_RFCOMM_TRACE_DEBUG(fmt, args...)
/* Define tracing for GOEPC */
#define GOEPC_TRACE_ERROR(fmt, args...)
#define GOEPC_TRACE_WARNING(fmt, args...)

View File

@@ -156,6 +156,10 @@
#include "bta_pan_int.h"
#endif
#if BTA_PBA_CLIENT_INCLUDED == TRUE
#include "bta_pba_client_int.h"
#endif
#include "bta_sys_int.h"
// control block for patch ram downloading
@@ -184,6 +188,12 @@ void BTE_DeinitStack(void)
{
//BTA Modules
#if (BTA_INCLUDED == TRUE && BTA_DYNAMIC_MEMORY == TRUE)
#if BTA_PBA_CLIENT_INCLUDED == TRUE
if (bta_pba_client_cb_ptr) {
osi_free(bta_pba_client_cb_ptr);
bta_pba_client_cb_ptr = NULL;
}
#endif
#if GATTS_INCLUDED == TRUE
if (bta_gatts_cb_ptr){
osi_free(bta_gatts_cb_ptr);
@@ -521,6 +531,12 @@ bt_status_t BTE_InitStack(void)
#if BTA_PAN_INCLUDED==TRUE
memset((void *)bta_pan_cb_ptr, 0, sizeof(tBTA_PAN_CB));
#endif
#if BTA_PBA_CLIENT_INCLUDED == TRUE
if ((bta_pba_client_cb_ptr = (tBTA_PBA_CLIENT_CB *)osi_malloc(sizeof(tBTA_PBA_CLIENT_CB))) == NULL) {
goto error_exit;
}
memset((void *)bta_pba_client_cb_ptr, 0, sizeof(tBTA_PBA_CLIENT_CB));
#endif
#endif // BTA_INCLUDED == TRUE
return BT_STATUS_SUCCESS;

View File

@@ -359,7 +359,7 @@ UINT16 GOEPC_RequestAddHeader(UINT16 handle, UINT8 header_id, const UINT8 *data,
break;
}
if (data == NULL || data_len == 0) {
if ((data == NULL) ^ (data_len == 0)) {
ret = GOEP_INVALID_PARAM;
break;
}

View File

@@ -8,11 +8,12 @@
#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 */
/* GOEP Client or Server(not supported yet) API return code */
#define GOEP_SUCCESS 0x00 /* Operation successful */
#define GOEP_FAILURE 0x01 /* Operation failed */
#define GOEP_NO_RESOURCES 0x02 /* Not enough resources */
#define GOEP_BAD_HANDLE 0x04 /* Bad handle */
#define GOEP_INVALID_PARAM 0x08 /* Invalid parameter */
#define GOEP_INVALID_STATE 0x10 /* Operation not allow in current state */
#define GOEP_CONGEST 0x20 /* Congest */
#define GOEP_TL_ERROR 0x40 /* Lower transport layer error */

View File

@@ -19,8 +19,6 @@
#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
@@ -162,12 +160,21 @@ typedef struct
BD_ADDR addr; /* peer bluetooth device address */
} tOBEX_OVER_L2CAP_SVR;
typedef struct
{
UINT8 scn; /* service channel number */
UINT16 sec_mask; /* security mask */
UINT16 pref_mtu; /* preferred mtu, limited by rfcomm mtu */
BD_ADDR addr; /* peer bluetooth device address */
} tOBEX_OVER_RFCOMM_SVR;
typedef struct
{
UINT8 tl; /* transport type, OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */
union
{
tOBEX_OVER_L2CAP_SVR l2cap;
tOBEX_OVER_RFCOMM_SVR rfcomm;
};
} tOBEX_SVR_INFO;

View File

@@ -14,7 +14,13 @@
#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 */
#if (RFCOMM_INCLUDED == TRUE)
#define OBEX_BT_HDR_MIN_OFFSET OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET /* should set to max value of all transport layer */
#define OBEX_BT_HDR_RESERVE_LEN OBEX_TL_RFCOMM_BT_HDR_RESERVE_LEN /* should set to max value of all transport layer */
#else
#define OBEX_BT_HDR_MIN_OFFSET OBEX_TL_L2CAP_BT_HDR_OFFSET_MIN
#define OBEX_BT_HDR_RESERVE_LEN OBEX_TL_L2CAP_BT_HDR_RESERVE_LEN
#endif
#define OBEX_ROLE_CLIENT 0x01
#define OBEX_ROLE_SERVER 0x02
@@ -24,10 +30,6 @@
#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 */
@@ -65,6 +67,7 @@ extern tOBEX_CB *obex_cb_ptr;
#endif
void obex_tl_l2cap_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg);
void obex_tl_rfcomm_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);

View File

@@ -68,9 +68,18 @@ typedef struct
BD_ADDR addr; /* peer bluetooth device address */
} tOBEX_TL_L2CAP_SVR;
typedef struct
{
UINT8 scn; /* service channel number */
UINT16 sec_mask; /* security mask */
UINT16 pref_mtu; /* preferred mtu, limited by rfcomm mtu */
BD_ADDR addr; /* peer bluetooth device address */
} tOBEX_TL_RFCOMM_SVR;
typedef union
{
tOBEX_TL_L2CAP_SVR l2cap;
tOBEX_TL_RFCOMM_SVR rfcomm;
} tOBEX_TL_SVR_INFO;
typedef void (tOBEX_TL_CBACK)(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg);

View File

@@ -10,7 +10,8 @@
#if (OBEX_INCLUDED == TRUE)
#define OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET 13 /* L2CAP_MIN_OFFSET */
#define OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET 13 /* equal to L2CAP_MIN_OFFSET */
#define OBEX_TL_L2CAP_BT_HDR_RESERVE_LEN 0 /* not require any additional byte */
tOBEX_TL_OPS *obex_tl_l2cap_ops_get(void);

View File

@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "obex_tl.h"
#if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE)
#define OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET 18 /* RFCOMM_MIN_OFFSET + L2CAP_MIN_OFFSET */
#define OBEX_TL_RFCOMM_BT_HDR_RESERVE_LEN 1 /* reserve 1 byte for rfcomm fcs */
tOBEX_TL_OPS *obex_tl_rfcomm_ops_get(void);
#endif /* #if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE) */

View File

@@ -14,6 +14,7 @@
#include "obex_int.h"
#include "obex_tl.h"
#include "obex_tl_l2cap.h"
#include "obex_tl_rfcomm.h"
#if (OBEX_INCLUDED == TRUE)
@@ -25,6 +26,12 @@ static inline void obex_server_to_tl_server(tOBEX_SVR_INFO *server, tOBEX_TL_SVR
tl_server->l2cap.pref_mtu = server->l2cap.pref_mtu;
bdcpy(tl_server->l2cap.addr, server->l2cap.addr);
}
else if (server->tl == OBEX_OVER_RFCOMM) {
tl_server->rfcomm.scn = server->rfcomm.scn;
tl_server->rfcomm.sec_mask = server->rfcomm.sec_mask;
tl_server->rfcomm.pref_mtu = server->rfcomm.pref_mtu;
bdcpy(tl_server->rfcomm.addr, server->rfcomm.addr);
}
else {
OBEX_TRACE_ERROR("Unsupported OBEX transport type\n");
assert(0);
@@ -34,7 +41,7 @@ static inline void obex_server_to_tl_server(tOBEX_SVR_INFO *server, tOBEX_TL_SVR
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);
UINT16_TO_BE_FIELD(p_pkt_len, len);
}
/*******************************************************************************
@@ -62,13 +69,12 @@ UINT16 OBEX_Init(void)
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 */
/*
#if (RFCOMM_INCLUDED == TRUE)
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);
}
*/
#endif
obex_cb.trace_level = BT_TRACE_LEVEL_ERROR;
return OBEX_SUCCESS;
}
@@ -317,7 +323,7 @@ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_
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;
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN;
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
if (p_buf == NULL) {
@@ -327,7 +333,7 @@ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_
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;
p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET - OBEX_BT_HDR_RESERVE_LEN;
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
/* byte 0: opcode */
*p_data++ = info->opcode;
@@ -343,7 +349,7 @@ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_
/* 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);
UINT16_TO_BE_FIELD(p_data, info->max_packet_length);
pkt_len += 4;
break;
case OBEX_OPCODE_SETPATH:
@@ -357,7 +363,7 @@ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_
break;
}
STORE16BE(p_pkt_len, pkt_len);
UINT16_TO_BE_FIELD(p_pkt_len, pkt_len);
p_buf->len = pkt_len;
*out_pkt = p_buf;
return OBEX_SUCCESS;
@@ -378,7 +384,7 @@ UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out
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;
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN;
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
if (p_buf == NULL) {
@@ -388,7 +394,7 @@ UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out
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;
p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET - OBEX_BT_HDR_RESERVE_LEN;
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
/* byte 0: response code */
*p_data++ = info->response_code;
@@ -405,14 +411,14 @@ UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out
/* 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);
UINT16_TO_BE_FIELD(p_data, info->max_packet_length);
pkt_len += 4;
break;
default:
break;
}
STORE16BE(p_pkt_len, pkt_len);
UINT16_TO_BE_FIELD(p_pkt_len, pkt_len);
p_buf->len = pkt_len;
*out_pkt = p_buf;
return OBEX_SUCCESS;
@@ -465,7 +471,7 @@ UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header)
pkt->len += header_len;
/* point to packet len */
p_data++;
STORE16BE(p_data, pkt->len);
UINT16_TO_BE_FIELD(p_data, pkt->len);
return OBEX_SUCCESS;
}
@@ -481,7 +487,11 @@ UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header)
*******************************************************************************/
UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UINT16 data_len)
{
if (pkt == NULL || data == NULL) {
if (pkt == NULL) {
return OBEX_INVALID_PARAM;
}
if ((data == NULL) ^ (data_len == 0)) {
return OBEX_INVALID_PARAM;
}
@@ -524,15 +534,17 @@ UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UIN
*p_start++ = header_id;
if (store_header_len) {
/* store header length */
STORE16BE(p_start, header_len);
UINT16_TO_BE_FIELD(p_start, header_len);
p_start+= 2;
}
/* store data */
memcpy(p_start, data, data_len);
if (data != NULL) {
/* store data */
memcpy(p_start, data, data_len);
}
pkt->len += header_len;
/* point to packet len */
p_data++;
STORE16BE(p_data, pkt->len);
UINT16_TO_BE_FIELD(p_data, pkt->len);
return OBEX_SUCCESS;
}

View File

@@ -208,4 +208,9 @@ void obex_tl_l2cap_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg)
obex_tl_evt_handler(OBEX_OVER_L2CAP, evt, msg);
}
void obex_tl_rfcomm_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg)
{
obex_tl_evt_handler(OBEX_OVER_RFCOMM, evt, msg);
}
#endif /* #if (OBEX_INCLUDED == TRUE) */

View File

@@ -484,6 +484,10 @@ void obex_tl_l2cap_disconnect_ind(UINT16 lcid, BOOLEAN is_conf_needed)
return;
}
if (p_ccb->initiator && find_scb_by_psm(p_ccb->vpsm) == NULL) {
L2CA_Deregister(p_ccb->vpsm);
}
tOBEX_TL_MSG msg = {0};
msg.any.hdl = p_ccb->allocated;
obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);

View File

@@ -0,0 +1,439 @@
/*
* 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/port_api.h"
#include "stack/btm_api.h"
#include "stack/sdpdefs.h"
#include "obex_tl.h"
#include "obex_tl_rfcomm.h"
#if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE)
#define OBEX_TL_RFCOMM_NUM_CONN 4
#define OBEX_TL_RFCOMM_NUM_SERVER 2
#define OBEX_TL_RFCOMM_EVENT_MARK (PORT_EV_FC | PORT_EV_FCS)
typedef struct {
UINT16 rfc_handle; /* rfcomm handle */
UINT16 mtu; /* rfcomm mtu */
BOOLEAN initiator; /* TRUE if is initiator, otherwise FALSE */
UINT8 scn; /* service channel number */
BD_ADDR addr; /* peer bluetooth device address */
UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, equal to handle */
} tOBEX_TL_RFCOMM_CCB;
typedef struct {
UINT16 rfc_handle; /* rfcomm handle */
UINT8 scn; /* service channel number */
UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, handle of server will left shift 8 bits */
} tOBEX_TL_RFCOMM_SCB;
typedef struct {
tOBEX_TL_CBACK *callback; /* Upper layer callback */
tOBEX_TL_RFCOMM_CCB ccb[OBEX_TL_RFCOMM_NUM_CONN];
tOBEX_TL_RFCOMM_SCB scb[OBEX_TL_RFCOMM_NUM_SERVER];
UINT8 trace_level; /* trace level */
} tOBEX_TL_RFCOMM_CB;
#if OBEX_DYNAMIC_MEMORY == FALSE
static tOBEX_TL_RFCOMM_CB obex_tl_rfcomm_cb;
#else
static tOBEX_TL_RFCOMM_CB *obex_tl_rfcomm_cb_ptr = NULL;
#define obex_tl_rfcomm_cb (*obex_tl_rfcomm_cb_ptr)
#endif
static tOBEX_TL_RFCOMM_CCB *allocate_ccb(void)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = NULL;
for(int i = 0; i < OBEX_TL_RFCOMM_NUM_CONN; ++i) {
if (obex_tl_rfcomm_cb.ccb[i].allocated == 0) {
obex_tl_rfcomm_cb.ccb[i].allocated = i + 1;
p_ccb = &obex_tl_rfcomm_cb.ccb[i];
break;
}
}
return p_ccb;
}
static tOBEX_TL_RFCOMM_SCB *allocate_scb(void)
{
tOBEX_TL_RFCOMM_SCB *p_scb = NULL;
for(int i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; ++i) {
if (obex_tl_rfcomm_cb.scb[i].allocated == 0) {
obex_tl_rfcomm_cb.scb[i].allocated = i + 1;
p_scb = &obex_tl_rfcomm_cb.scb[i];
break;
}
}
return p_scb;
}
static void free_ccb(tOBEX_TL_RFCOMM_CCB *p_ccb)
{
memset(p_ccb, 0, sizeof(tOBEX_TL_RFCOMM_CCB));
}
static void free_scb(tOBEX_TL_RFCOMM_SCB *p_scb)
{
memset(p_scb, 0, sizeof(tOBEX_TL_RFCOMM_SCB));
}
static tOBEX_TL_RFCOMM_CCB *find_ccb_by_handle(UINT16 handle)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = NULL;
if (handle > 0 && handle <= OBEX_TL_RFCOMM_NUM_CONN) {
if (obex_tl_rfcomm_cb.ccb[handle-1].allocated == handle) {
p_ccb = &obex_tl_rfcomm_cb.ccb[handle-1];
}
}
return p_ccb;
}
static tOBEX_TL_RFCOMM_CCB *find_ccb_by_rfc_handle(UINT16 rfc_handle)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = NULL;
for(int i = 0; i < OBEX_TL_RFCOMM_NUM_CONN; ++i) {
if (obex_tl_rfcomm_cb.ccb[i].allocated && obex_tl_rfcomm_cb.ccb[i].rfc_handle == rfc_handle) {
p_ccb = &obex_tl_rfcomm_cb.ccb[i];
break;
}
}
return p_ccb;
}
static tOBEX_TL_RFCOMM_SCB *find_scb_by_handle(UINT16 handle)
{
tOBEX_TL_RFCOMM_SCB *p_scb = NULL;
handle = handle >> 8;
if (handle > 0 && handle <= OBEX_TL_RFCOMM_NUM_SERVER) {
if (obex_tl_rfcomm_cb.scb[handle-1].allocated == handle) {
p_scb = &obex_tl_rfcomm_cb.scb[handle-1];
}
}
return p_scb;
}
static tOBEX_TL_RFCOMM_SCB *find_scb_by_rfc_handle(UINT16 rfc_handle)
{
tOBEX_TL_RFCOMM_SCB *p_scb = NULL;
for(int i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; ++i) {
if (obex_tl_rfcomm_cb.scb[i].allocated && obex_tl_rfcomm_cb.scb[i].rfc_handle == rfc_handle) {
p_scb = &obex_tl_rfcomm_cb.scb[i];
break;
}
}
return p_scb;
}
static tOBEX_TL_RFCOMM_SCB *find_scb_by_scn(UINT16 scn)
{
tOBEX_TL_RFCOMM_SCB *p_scb = NULL;
for(int i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; ++i) {
if (obex_tl_rfcomm_cb.scb[i].allocated && obex_tl_rfcomm_cb.scb[i].scn == scn) {
p_scb = &obex_tl_rfcomm_cb.scb[i];
break;
}
}
return p_scb;
}
static void rfcomm_mgmt_event_handler(tOBEX_TL_RFCOMM_CCB *p_ccb, UINT32 code)
{
tOBEX_TL_MSG msg = {0};
msg.any.hdl = p_ccb->allocated;
switch (code)
{
case PORT_SUCCESS:
/* event already handled, do nothing */
break;
default:
/* other event, disconnect */
obex_tl_rfcomm_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
free_ccb(p_ccb);
break;
}
}
static void rfcomm_client_mgmt_callback(UINT32 code, UINT16 rfc_handle, void* data)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_rfc_handle(rfc_handle);
if (p_ccb == NULL) {
OBEX_TL_RFCOMM_TRACE_DEBUG("No ccb to handle rfcomm event\n");
return;
}
/* connection opened, handle event here */
if (code == PORT_SUCCESS) {
assert(data != NULL);
tPORT_MGMT_CL_CALLBACK_ARG *cl_mgmt_cb_arg = (tPORT_MGMT_CL_CALLBACK_ARG *)data;
p_ccb->mtu = cl_mgmt_cb_arg->peer_mtu;
tOBEX_TL_MSG msg = {0};
msg.conn_open.hdl = p_ccb->allocated;
msg.conn_open.peer_mtu = p_ccb->mtu;
msg.conn_open.our_mtu = p_ccb->mtu;
obex_tl_rfcomm_cb.callback(OBEX_TL_CONN_OPEN_EVT, &msg);
}
rfcomm_mgmt_event_handler(p_ccb, code);
}
static void rfcomm_server_mgmt_callback(UINT32 code, UINT16 rfc_handle, void* data)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = NULL;
/* incoming connection, handle event here */
if (code == PORT_SUCCESS) {
assert(data != NULL);
tOBEX_TL_RFCOMM_SCB *p_scb = find_scb_by_rfc_handle(rfc_handle);
tPORT_MGMT_SR_CALLBACK_ARG *sr_mgmt_cb_arg = (tPORT_MGMT_SR_CALLBACK_ARG *)data;
if (p_scb == NULL) {
OBEX_TL_RFCOMM_TRACE_WARNING("No scb to this rfcomm connection\n");
/* tell rfcomm to reject this connection */
sr_mgmt_cb_arg->accept = FALSE;
return;
}
/* try to find p_ccb with this rfc_handle, we expect to get a NULL */
p_ccb = find_ccb_by_rfc_handle(rfc_handle);
if (p_ccb == NULL) {
p_ccb = allocate_ccb();
if (p_ccb == NULL) {
OBEX_TL_RFCOMM_TRACE_WARNING("can not allocate a ccb for new connection\n");
sr_mgmt_cb_arg->accept = FALSE;
return;
}
}
else {
OBEX_TL_RFCOMM_TRACE_WARNING("found duplicate rfcomm connection\n");
}
p_ccb->initiator = FALSE;
p_ccb->rfc_handle = rfc_handle;
p_ccb->scn = p_scb->scn;
p_ccb->mtu = sr_mgmt_cb_arg->peer_mtu;
/* get peer bd_addr */
PORT_CheckConnection(rfc_handle, FALSE, p_ccb->addr, NULL);
tOBEX_TL_MSG msg = {0};
msg.conn_income.hdl = p_ccb->allocated;
msg.conn_income.peer_mtu = p_ccb->mtu;
msg.conn_income.our_mtu = p_ccb->mtu;
msg.conn_income.svr_hdl = (p_scb->allocated << 8);
obex_tl_rfcomm_cb.callback(OBEX_TL_CONN_INCOME_EVT, &msg);
}
else {
/* other event, it means server is connected */
p_ccb = find_ccb_by_rfc_handle(rfc_handle);
if (p_ccb == NULL) {
OBEX_TL_RFCOMM_TRACE_DEBUG("No ccb to handle rfcomm event\n");
return;
}
}
rfcomm_mgmt_event_handler(p_ccb, code);
}
static int rfcomm_data_callback(UINT16 rfc_handle, UINT8 *p_buf, UINT16 len, int type)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_rfc_handle(rfc_handle);
if (p_ccb != NULL && type == DATA_CO_CALLBACK_TYPE_INCOMING) {
tOBEX_TL_MSG msg = {0};
msg.data.hdl = p_ccb->allocated;
msg.data.p_buf = (BT_HDR *)p_buf;
obex_tl_rfcomm_cb.callback(OBEX_TL_DATA_EVT, &msg);
PORT_FlowControl_GiveCredit(rfc_handle, TRUE, 1);
}
else if(p_buf != NULL) {
osi_free(p_buf);
}
return 1;
}
static void rfcomm_event_callback(UINT32 code, UINT16 rfc_handle)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_rfc_handle(rfc_handle);
if (p_ccb == NULL) {
OBEX_TL_RFCOMM_TRACE_WARNING("No ccb to handle rfcomm event\n");
return;
}
if (code & PORT_EV_FC) {
tOBEX_TL_MSG msg = {0};
msg.any.hdl = p_ccb->allocated;
if (code & PORT_EV_FCS) {
obex_tl_rfcomm_cb.callback(OBEX_TL_UNCONGEST_EVT, &msg);
}
else {
obex_tl_rfcomm_cb.callback(OBEX_TL_CONGEST_EVT, &msg);
}
}
}
void obex_tl_rfcomm_init(tOBEX_TL_CBACK *callback)
{
assert(callback != NULL);
#if (OBEX_DYNAMIC_MEMORY)
if (!obex_tl_rfcomm_cb_ptr) {
obex_tl_rfcomm_cb_ptr = (tOBEX_TL_RFCOMM_CB *)osi_malloc(sizeof(tOBEX_TL_RFCOMM_CB));
if (!obex_tl_rfcomm_cb_ptr) {
OBEX_TL_RFCOMM_TRACE_ERROR("OBEX over RFCOMM transport layer initialize failed, no memory\n");
assert(0);
}
}
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
memset(&obex_tl_rfcomm_cb, 0, sizeof(tOBEX_TL_RFCOMM_CB));
obex_tl_rfcomm_cb.callback = callback;
obex_tl_rfcomm_cb.trace_level = BT_TRACE_LEVEL_ERROR;
}
void obex_tl_rfcomm_deinit(void)
{
#if (OBEX_DYNAMIC_MEMORY)
if (obex_tl_rfcomm_cb_ptr) {
osi_free(obex_tl_rfcomm_cb_ptr);
obex_tl_rfcomm_cb_ptr = NULL;
}
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
}
UINT16 obex_tl_rfcomm_connect(tOBEX_TL_SVR_INFO *server)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = allocate_ccb();
if (p_ccb == NULL) {
return 0;
}
BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_OBEX, server->rfcomm.sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, server->rfcomm.scn);
if (RFCOMM_CreateConnection(UUID_PROTOCOL_OBEX, server->rfcomm.scn, FALSE, server->rfcomm.pref_mtu,
server->rfcomm.addr, &p_ccb->rfc_handle, rfcomm_client_mgmt_callback) != PORT_SUCCESS) {
free_ccb(p_ccb);
return 0;
}
/* set up data callback, event mask and event callback */
PORT_SetDataCOCallback(p_ccb->rfc_handle, rfcomm_data_callback);
PORT_SetEventMask(p_ccb->rfc_handle, OBEX_TL_RFCOMM_EVENT_MARK);
PORT_SetEventCallback(p_ccb->rfc_handle, rfcomm_event_callback);
bdcpy(p_ccb->addr, server->rfcomm.addr);
p_ccb->scn = server->rfcomm.scn;
p_ccb->initiator = TRUE;
return p_ccb->allocated;
}
void obex_tl_rfcomm_disconnect(UINT16 handle)
{
tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_handle(handle);
if (p_ccb != NULL) {
RFCOMM_RemoveConnection(p_ccb->rfc_handle);
free_ccb(p_ccb);
}
}
UINT16 obex_tl_rfcomm_send(UINT16 handle, BT_HDR *p_buf)
{
UINT16 ret = OBEX_TL_FAILED;
tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_handle(handle);
do {
if (p_ccb == NULL) {
osi_free(p_buf);
break;
}
/* Can not send data size larger than MTU */
/* Offset should not smaller than OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET */
if (p_buf->len > p_ccb->mtu || p_buf->offset < OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET) {
osi_free(p_buf);
break;
}
if (PORT_Write(p_ccb->rfc_handle, p_buf) == PORT_SUCCESS) {
ret = OBEX_TL_SUCCESS;
}
} while (0);
return ret;
}
UINT16 obex_tl_rfcomm_bind(tOBEX_TL_SVR_INFO *server)
{
tOBEX_TL_RFCOMM_SCB *p_scb = find_scb_by_scn(server->rfcomm.scn);
if (p_scb != NULL) {
/* scn already used */
return 0;
}
p_scb = allocate_scb();
if (p_scb == NULL) {
OBEX_TL_RFCOMM_TRACE_WARNING("Can not allocate scb, out of number\n");
return 0;
}
BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_OBEX, server->rfcomm.sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, server->rfcomm.scn);
if (RFCOMM_CreateConnection(UUID_PROTOCOL_OBEX, server->rfcomm.scn, TRUE, server->rfcomm.pref_mtu,
server->rfcomm.addr, &p_scb->rfc_handle, rfcomm_server_mgmt_callback) != PORT_SUCCESS) {
free_scb(p_scb);
return 0;
}
/* set up data callback, event mask and event callback */
PORT_SetDataCOCallback(p_scb->rfc_handle, rfcomm_data_callback);
PORT_SetEventMask(p_scb->rfc_handle, OBEX_TL_RFCOMM_EVENT_MARK);
PORT_SetEventCallback(p_scb->rfc_handle, rfcomm_event_callback);
p_scb->scn = server->rfcomm.scn;
/* left shift 8 bits as server handle, avoid confuse with connection handle */
return (p_scb->allocated << 8);
}
void obex_tl_rfcomm_unbind(UINT16 handle)
{
tOBEX_TL_RFCOMM_SCB *p_scb = find_scb_by_handle(handle);
if (p_scb) {
tOBEX_TL_RFCOMM_CCB *p_ccb = NULL;
while ((p_ccb = find_ccb_by_rfc_handle(p_scb->rfc_handle)) != NULL) {
RFCOMM_RemoveConnection(p_ccb->rfc_handle);
tOBEX_TL_MSG msg = {0};
msg.any.hdl = p_ccb->allocated;
obex_tl_rfcomm_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg);
free_ccb(p_ccb);
}
RFCOMM_RemoveServer(p_scb->rfc_handle);
free_scb(p_scb);
}
}
static tOBEX_TL_OPS obex_tl_rfcomm_ops = {
.init = obex_tl_rfcomm_init,
.deinit = obex_tl_rfcomm_deinit,
.connect = obex_tl_rfcomm_connect,
.disconnect = obex_tl_rfcomm_disconnect,
.bind = obex_tl_rfcomm_bind,
.unbind = obex_tl_rfcomm_unbind,
.send = obex_tl_rfcomm_send
};
/*******************************************************************************
**
** Function obex_tl_rfcomm_ops_get
**
** Description Get the operation function structure pointer of OBEX over
** RFCOMM transport layer
**
** Returns Pointer to operation function structure
**
*******************************************************************************/
tOBEX_TL_OPS *obex_tl_rfcomm_ops_get(void)
{
return &obex_tl_rfcomm_ops;
}
#endif /* #if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE) */

View File

@@ -64,6 +64,18 @@ PCM Signal supports three configurations in menuconfig: PCM Role, PCM Polar and
- The default configuration is `Stereo Mode`, you can change the PCM Channel mode in `menuconfig` path:
`Component config --> Bluetooth --> Controller Options --> PCM Signal Configurations --> PCM Signal Configurations: Role, Polar and Channel Mode(Stereo/Mono) --> Channel Mode(Stereo/Mono)`
### Special Configurations for PBA Client
To use PBA Client function, we need to enable PBA Client in `menuconfig` path: `Component config --> Bluetooth --> Bluedroid Options --> Classic Bluetooth --> Classic BT PBA Client`, this example already enable PBA Client by `sdkconfig.defaults`.
Step to initialize PBA Client connection:
- Register user callback: `esp_pbac_register_callback(bt_app_pbac_cb)`
- Initialize PBA Client API: `esp_pbac_init()`
- Connect to peer device ...
- Call `esp_pbac_connect(peer_addr, supported_features, 0)`, this will initiate service discover and try to connect to PBA Server.
- After the operation done, whether success or not, we will receive a `ESP_PBAC_CONNECTION_STATE_EVT` event in user callback.
### Codec Choice
ESP32 supports two types of codec for HFP audio data: `CVSD` and `mSBC`.

View File

@@ -2,6 +2,7 @@ idf_component_register(SRCS "app_hf_msg_set.c"
"bt_app_core.c"
"bt_app_hf.c"
"gpio_pcm_config.c"
"bt_app_pbac.c"
"main.c"
PRIV_REQUIRES bt nvs_flash esp_driver_gpio console esp_ringbuf
INCLUDE_DIRS ".")

View File

@@ -17,6 +17,7 @@
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_hf_client_api.h"
#include "esp_pbac_api.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
@@ -244,6 +245,9 @@ void bt_app_hf_client_cb(esp_hf_client_cb_event_t event, esp_hf_client_cb_param_
param->conn_stat.peer_feat,
param->conn_stat.chld_feat);
memcpy(peer_addr,param->conn_stat.remote_bda,ESP_BD_ADDR_LEN);
if (param->conn_stat.state == ESP_HF_CLIENT_CONNECTION_STATE_SLC_CONNECTED) {
esp_pbac_connect(peer_addr);
}
break;
}

View File

@@ -0,0 +1,86 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_pbac_api.h"
#include "bt_app_core.h"
#include "bt_app_pbac.h"
#define BT_PBAC_TAG "BT_PBAC"
esp_pbac_conn_hdl_t pba_conn_handle;
void bt_app_pbac_cb(esp_pbac_event_t event, esp_pbac_param_t *param)
{
switch (event)
{
case ESP_PBAC_CONNECTION_STATE_EVT:
ESP_LOGI(BT_PBAC_TAG, "PBA client connection event, state: %s, reason: 0x%x", (param->conn_stat.connected ? "Connected" : "Disconnected"), param->conn_stat.reason);
ESP_LOGI(BT_PBAC_TAG, "Peer supported repositories: 0x%x, supported features: 0x%lx", param->conn_stat.peer_supported_repo, param->conn_stat.peer_supported_feat);
if (param->conn_stat.connected) {
pba_conn_handle = param->conn_stat.handle;
/* set phone book to "telecom" folder, just to test set phone book function */
esp_pbac_set_phone_book(pba_conn_handle, ESP_PBAC_SET_PHONE_BOOK_FLAGS_DOWN, "telecom");
}
break;
case ESP_PBAC_PULL_PHONE_BOOK_RESPONSE_EVT:
/* if multiple PBA connection, we should check param->pull_phone_book_rsp.handle */
ESP_LOGI(BT_PBAC_TAG, "PBA client pull phone book response, handle:%d, result: 0x%x", param->pull_phone_book_rsp.handle, param->pull_phone_book_rsp.result);
if (param->pull_phone_book_rsp.result == ESP_PBAC_SUCCESS && param->pull_phone_book_rsp.data_len > 0) {
printf("%.*s\n", param->pull_phone_book_rsp.data_len, param->pull_phone_book_rsp.data);
/* copy data to other buff before return, if phone book size is too large, it will be sent in multiple response event */
}
if (param->pull_phone_book_rsp.final) {
ESP_LOGI(BT_PBAC_TAG, "PBA client pull phone book final response");
/* pull phone book done, now we can perform other operation */
if (param->pull_phone_book_rsp.result == ESP_PBAC_SUCCESS && param->pull_phone_book_rsp.include_phone_book_size) {
ESP_LOGI(BT_PBAC_TAG, "Phone Book Size:%d", param->pull_phone_book_rsp.phone_book_size);
esp_pbac_pull_phone_book_app_param_t app_param = {0};
app_param.include_property_selector = 1;
/* property bit mask, filter out photo, refer to Phone Book Access Profile */
app_param.property_selector = 0xFFFFFFF7;
/* pull again, without 'max_list_count = 0', then we can get the entire phone book */
esp_pbac_pull_phone_book(pba_conn_handle, "telecom/pb.vcf", &app_param);
}
}
break;
case ESP_PBAC_SET_PHONE_BOOK_RESPONSE_EVT:
ESP_LOGI(BT_PBAC_TAG, "PBA client set phone book response, handle:%d, result: 0x%x", param->set_phone_book_rsp.handle, param->set_phone_book_rsp.result);
/* done, set phone book response will always be a final response */
if (param->set_phone_book_rsp.result == ESP_PBAC_SUCCESS) {
esp_pbac_pull_phone_book_app_param_t app_param = {0};
app_param.include_max_list_count = 1;
/* set max_list_count to zero, then we can get phone book size in peer response */
app_param.max_list_count = 0;
/* pull phone book use a absolute path; if no app param, we can pass a NULL to API */
esp_pbac_pull_phone_book(pba_conn_handle, "telecom/pb.vcf", &app_param);
}
break;
case ESP_PBAC_PULL_VCARD_LISTING_RESPONSE_EVT:
ESP_LOGI(BT_PBAC_TAG, "PBA client pull vCard listing response, handle:%d, result: 0x%x", param->pull_vcard_listing_rsp.handle, param->pull_vcard_listing_rsp.result);
if (param->pull_vcard_listing_rsp.result == ESP_PBAC_SUCCESS) {
printf("%.*s\n", param->pull_vcard_listing_rsp.data_len, param->pull_vcard_listing_rsp.data);
}
if (param->pull_vcard_listing_rsp.final) {
ESP_LOGI(BT_PBAC_TAG, "PBA client pull vCard listing final response");
}
break;
case ESP_PBAC_PULL_VCARD_ENTRY_RESPONSE_EVT:
ESP_LOGI(BT_PBAC_TAG, "PBA client pull vCard entry response, handle:%d, result: 0x%x", param->pull_vcard_entry_rsp.handle, param->pull_vcard_entry_rsp.result);
if (param->pull_vcard_entry_rsp.result == ESP_PBAC_SUCCESS) {
printf("%.*s\n", param->pull_vcard_entry_rsp.data_len, param->pull_vcard_entry_rsp.data);
}
if (param->pull_vcard_entry_rsp.final) {
ESP_LOGI(BT_PBAC_TAG, "PBA client pull vCard entry final response");
}
break;
default:
break;
}
}

View File

@@ -0,0 +1,11 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_bt.h"
#include "esp_pbac_api.h"
void bt_app_pbac_cb(esp_pbac_event_t event, esp_pbac_param_t *param);

View File

@@ -21,10 +21,12 @@
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
#include "esp_hf_client_api.h"
#include "esp_pbac_api.h"
#include "bt_app_hf.h"
#include "gpio_pcm_config.h"
#include "esp_console.h"
#include "app_hf_msg_set.h"
#include "bt_app_pbac.h"
esp_bd_addr_t peer_addr = {0};
static char peer_bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1];
@@ -251,6 +253,8 @@ static void bt_hf_client_hdl_stack_evt(uint16_t event, void *p_param)
esp_bt_gap_register_callback(esp_bt_gap_cb);
esp_hf_client_register_callback(bt_app_hf_client_cb);
esp_hf_client_init();
esp_pbac_register_callback(bt_app_pbac_cb);
esp_pbac_init();
#if (CONFIG_EXAMPLE_SSP_ENABLED == true)
/* Set default parameters for Secure Simple Pairing */

View File

@@ -8,3 +8,4 @@ CONFIG_BT_BLUEDROID_ENABLED=y
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_HFP_ENABLE=y
CONFIG_BT_HFP_CLIENT_ENABLE=y
CONFIG_BT_PBAC_ENABLED=y