feat(bt/bluedroid): Support PBAP client role

Closes https://github.com/espressif/esp-idf/issues/11522
This commit is contained in:
linruihao
2024-10-17 19:45:42 +08:00
parent 23ed275eff
commit 42c0f17b7d
19 changed files with 3562 additions and 4 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"

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

@ -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

@ -186,6 +186,13 @@
#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

@ -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;